# Very Good Ventures — Full Content > Software consultancy specializing in Flutter, Dart, AI/ML, and cross-platform app development. This file inlines the full text of all published content. ## Blog Posts ### How do I know GenUI is worth the cost? Source: https://verygood.ventures/blog/is-genui-worth-the-cost/ There’s a question that stalls Generative UI (GenUI) projects before they start. [GenUI uses an LLM to build the user interface](https://docs.flutter.dev/ai/genui), which means every round trip with a user burns tokens, and sooner or later someone is going to look at that and ask: “We’ll burn a bunch of expensive tokens on this. How do we know it will be worth it?” If you own the planning and implementation of a GenUI experience you need to be able to answer that question. We've asked ourselves the same question while building [GenUI Kit](/resources/genui-kit/). At the end of the day, it’s a standard ROI question. In this post, we’ll talk about how you can address the question head on paired with the tools GenUI kit, our Flutter toolkit for building generative UI, provides to do so. ## Start with the goal in mind Calculating the cost is straightforward. You're going to get a bill from your cloud provider and they're going to tell you exactly how many tokens you spent, and you can attribute those costs to your generative experience. But that’s only one half of the equation. What about the revenue side? That's where you need to think carefully about the goals of your project, what success looks like, and how you would actually track and calculate it. This doesn’t have to be novel. These are traditional metrics businesses have always cared about. Let’s be concrete: Are you trying to drive more sales? Reduce bounce rate? Increase time on site? Save employee time? I've sat across from enterprise teams who have a mandate to put generative UI somewhere, anywhere, in their organization. While they’ve considered the cost, some haven’t clearly defined what success looks like. Those projects can’t prove they’re worth the investment, because nobody decided up front what the goal is and how to measure it. ## Measure it like any other experience There's a temptation to argue that generative UI is a new paradigm, we’re still figuring it out, and it shouldn't be held to the same standard of measurement as everything else. I don't buy it. I would measure it the exact same way I’d measure any other change: I would line up an A/B test. Run the static experience against the generative one, same goal, same instrumentation. For ecommerce experiences the target is blunt: what did people spend per user in each arm, and what did each arm cost per user? The goal doesn't change because the interface generates itself. If the point of the experience is to have people spend money, that is still literally the goal of the whole thing. The only question is how successful each experience actually is. There's [research showing personalized experiences lead to higher spending](https://www.mckinsey.com/capabilities/growth-marketing-and-sales/our-insights/the-value-of-getting-personalization-right-or-wrong-is-multiplying), between 10 and 15%. That is an assumption, though. There is no guarantee. The lift is the hypothesis your A/B test exists to check. ## Not every GenUI experience will be a winner [MIT's widely cited NANDA report](https://www.mindtheproduct.com/why-most-ai-products-fail-key-findings-from-mits-2025-ai-report/) found 95% of enterprise generative AI pilots never touch the P&L, and [Gartner expects over 40% of agentic AI projects to be canceled by 2027](https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027), with cost a leading killer. I build generative UI for a living, so this is the part where I'm supposed to tell you GenUI is the exception. I'm not going to, because I’m confident GenUI experiences will be amongst the corpses too. There are going to be a lot of successful deployments and a lot of unsuccessful ones. That's why the most important thing is to measure it. I am a software engineer at the end of the day, and I’m genuinely interested in helping teams discover what works and what doesn’t, backed up by real data. From my perspective, the most common failure mode is the unmeasured internal tool, where the success metric is something fuzzy like "people find it useful." Maybe folks use it all the time. It's awesome. However, if it's costing your team a lot of money to run each month and the actual value is undefined, how would you know it’s worth it? If you aren't measuring the uplift, in hours saved, in invoices sent, in products sold, in some number a finance team can actually check, it’s going to show up as pure cost. The second failure mode hides in your averages. For example, say you have power users of an AI product, and those users cost $30 to $35 a month against a $20 price per month subscription. That's fine as long as the average is significantly under $20. However, if your average usage is $35, you're simply setting money on fire. When you model this out you need a spending cap, a strategy to drive costs down, or a real story about changing the usage distribution. One more caveat: your first generative experience is going to get tested against a static experience you've spent years honing, so that's not a fair fight at first, and you'd want to give it a few rounds before you call it. But there does need to be a cut-off point at some point. ## You’re in control of the costs Now that you’ve thought about success, remember that session cost isn't just handed to you. There are a few different levers you can pull during development to drive costs down before you ship your first GenUI experience. On the input token side, the levers are the size of your GenUI catalog, the length of your system prompt, and [caching strategies](https://ai.google.dev/gemini-api/docs/caching). A governed catalog bounds what the model can generate, which keeps hallucination risk and token spend down at the same time. It's the approach we take in GenUI Kit. On the output side, you choose cheaper models, ask for simpler responses, or both. Then there's routing, because not every step of a journey needs the same model. The first few steps of a flow are often really simple questions, so you could route those through a cheaper, faster model and then route the final dashboard through a slower, more expensive one. Cheaper isn't automatically better, though. On one financial-planning experience we helped build, the finale was a rich generated dashboard. When I threw cheaper models at it, they produced simple, uninteresting dashboards, and the whole reveal factor wasn't that useful. The savings weren't worth what they cost the experience. It becomes an interesting sort of Goldilocks project: the call between quality, speed, and cost, triangulating between those three parameters. I've written before about how model choice changes what an experience can be, in our [model benchmarking guide for generative UI frameworks](/resources/genui-benchmarks/). The other half of this is that you don't have to guess. GenUI Kit's sandbox shows the input and output tokens for a complete session. Build the experience, run it end to end, read the meter, and you have a per-session cost you can plug into your cloud provider's own calculator and project across a year of real traffic. Then the conversation becomes a threshold question: does the lift you can reasonably expect cover the run cost you can already see, or not? I want to be careful with the language here because these are cost estimates or projections. That said, you can and should estimate the price to run your GenUI experience before you ship it to production. To give this a concrete example, let's go with round numbers. Put 1,000 users in the static arm and 1,000 in the generative arm. Say the sandbox tells you a complete generative session costs 40 cents, so that arm costs $400 per test cycle. If average spend per user is $200 and the generative arm converts even two points better, you added thousands in revenue against hundreds in cost, so it clears. Reverse those numbers on a low-margin journey and it doesn't. ## Ask a better question So the question I'd actually ask isn't "can we afford the tokens," it's "which of our journeys clear the threshold." Ship generative UI where you have a measurable goal, where attribution is clean, and where the lift would cover a run cost you've already projected. You need to have a goal in mind, whether that's time savings, add-to-cart, reduced bounce rate, or increased time on site. Decide what your success criteria are, decide how you attribute them, and then test that on a generative experience. E-commerce journeys tend to clear first, because e-commerce is the very best attribution there is. At the end of the day, the question is simply: what did people spend in that experience? Internal tools can clear too, but only if you do the harder work of turning saved hours into dollars before you build. This is also why I think generative UI has a more direct path to the P&L than most enterprise AI. It can be user-facing, and you can use it to generate user-facing returns, which means the test is real. Put your cards on the table, put it in front of customers against the control, and see whether it has an uplift. Getting there safely takes real production work: a governed catalog that bounds cost and brand risk at once, token metering from your first sandbox session, and a success metric with its attribution designed in before the first generation is served. We cover that in [the GenUI approval checklist for production experiences](/blog/genui-approval-checklist/). ## Closing thoughts When we first started working with GenUI, my colleague called generative UI a solution looking for a problem, and I agreed. Neither of us believe that anymore. The more we brainstormed, the more we realized there are a lot of good use cases for it, and the ones worth shipping are the ones you can afford to measure honestly. If my CFO asked me for the one-sentence version, here it is: we have a really interesting bet to uplift conversion by 10 to 15%, it's unknown whether that will be worth the cost in the end, so give me a fixed budget to take that bet and test whether it pays off. A fixed budget, a defined metric, an honest test, and a cut-off point is a business case, not a demo with a bill attached. If you want to figure out which of your journeys clear the threshold, that's a working session rather than a sales pitch: map your top three candidate journeys, model their session costs in the GenUI Kit sandbox, and walk out with the numbers your CFO is going to ask for. ### Feature-First Clean Architecture: The AI-Ready Monorepo Pattern for Flutter Source: https://verygood.ventures/blog/feature-first-clean-architecture/ I have been organizing Flutter code feature-first for years. I named the approach *Feature-First Clean Architecture*, or FFCA. The motivation was ownership. I wanted each feature team to own what they build end to end, without stepping on another team's code. Clean architecture did the rest, keeping a clear separation of concerns and responsibilities between the layers inside each feature. When I joined VGV, I saw a chance to take FFCA further. I proposed it based on that past experience, and the team's Flutter experts helped sharpen it. [Brian Egan](/blog/authors/brian-egan/) contributed several solid improvements. The proving ground was a large native-to-Flutter migration. Multiple apps, dozens of features, one monorepo, and a mandate to move fast without the code turning into a mud pit. Two things shaped the result. The scale of a shared monorepo, that's not so new, and the arrival of [coding agents](/blog/vgv-wingspan-agentic-engineering-workflow/) as real contributors, and that was very new! The way you organize the code is also the interface your tools read. A structure only humans can navigate leaves your agents guessing on every task. FFCA keeps what [clean architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) already does well and changes one thing. The feature, not the layer, becomes the top-level unit of organization. Feature organization and package structure in the monorepo, as well as clean architecture's dependency rule, are not only guidelines you enforce in review. They also become rules the compiler enforces for you. The complete standard, with the full package and naming conventions, is documented on [Very Good Engineering](https://engineering.verygood.ventures/architecture/ffca/overview/). ## Why layer-first organization stops scaling in a Flutter monorepo Most Flutter codebases organize by layer, including VGV's own [Very Good Layered Architecture](/blog/very-good-flutter-architecture/). For a single app with a handful of features, that is the right call, and I still reach for it. ![Very Good Layered Architecture with three layers: an application layer holding each feature's page and Bloc, a domain layer of repositories, and a data layer of data sources](/assets/images/blog/feature-first-clean-architecture/legacy-layered.png) In a layer-organized codebase, packages group by responsibility, not by feature. A feature's UI and logic sit in the application layer, its repository in the domain layer, and its data source in the data layer. That works well when there are three features. At ten or more, any single feature is spread across all three layers, and nothing in the structure ties its pieces together except a naming convention you hope everyone follows. ![The Cart feature highlighted across the application, domain, and data layers, showing that moving or reusing it means touching all three layers](/assets/images/blog/feature-first-clean-architecture/layered-scatter.png) Three costs compound from there. Feature boundaries stay soft. Nothing physically stops the cart's presentation code from reaching into product internals, so over time it does. The boundary lives in a reviewer's head, not in the build. Isolating or reusing a whole feature is harder than it should be. When you want to pull `Cart` into a second app, or remove it from one, there is no single thing to move. You go hunting through every layer package for the pieces that belong to it. And then there are the agents. A coding agent working in a layer-organized repo has no machine-legible map of where a feature begins and ends. Feature folders do not change that. To the tooling it is all one package, so any file can import any other, and there is no boundary for the agent to respect. So it does what it has to. It guesses where new code belongs. It reasons over the whole repo to find what is relevant, because relevance is not encoded anywhere it can read. And it crosses layer boundaries in ways that only surface in review, because nothing in the structure told it the boundary was there. ## The shift: feature-first packaging with enforced layers FFCA inverts the top level. The primary unit of organization is the feature, and each feature carries its own layers as separate packages. ``` features/ cart/ cart_domain/ # Dart: models, repository interfaces, use cases cart_data/ # Dart: data sources, DTOs, mappers, repo impls cart_presentation/ # Flutter: screens, blocs, the feature's Module product/ product_domain/ product_data/ product_presentation/ auth/ # Headless feature: no screens, so no presentation auth_domain/ auth_data/ apps/ mobile_app/ # Composes features + shared into a deployable app admin_app/ shared/ ui_kit/ # Brand-agnostic widgets api_client/ ``` ![Diagram of a feature-first Flutter monorepo: apps compose feature and shared packages, and each feature owns its domain, data, and presentation layer packages](/assets/images/blog/feature-first-clean-architecture/monorepo-structure.png) Clean architecture's rules still hold inside each feature. The domain layer is pure Dart and depends on nothing else in the feature. The data layer depends on the domain to implement its repository interfaces. The presentation layer depends on the domain to assemble screens, and never on the data layer. A feature with no screens, like `auth` or `analytics`, is a headless feature: domain plus data, and it can grow a presentation package later with no structural change. ![Diagram of a single feature's three layers as separate packages, with the presentation and data layers both depending on the domain layer](/assets/images/blog/feature-first-clean-architecture/feature-layers.png) What changed is not the layering. A feature is now a thing you can point at, move, reuse, and reason about as a unit. And because each layer is a real package with a declared dependency graph, the boundaries are no longer just conventions. They are artifacts the build can check. This is more than feature folders. A feature-based layout collocates a feature's files but keeps the layers as conventions inside a single package. FFCA makes each layer its own package, wired together through [Dart workspaces](https://dart.dev/tools/pub/workspaces), so the dependency graph enforces the boundary rather than a reviewer. Two things fall out of this structure. The first is that an app becomes little more than the list of features it composes. One app can ship a feature that another leaves out, and neither app touches the other's code to make that happen. Because each feature declares its own dependencies, an app pulls in only the code and SDKs the features it ships actually use, nothing more. The second is deferred loading. Each feature is its own package, and a feature's presentation layer gives each screen its own barrel file, so an app can defer-load a whole feature, or a single screen, on demand. On the web that splits one large bundle into smaller chunks loaded as they are needed, and on Android it maps onto dynamic feature modules. This is one of the improvements Brian brought to FFCA, and it stays a real win for large web apps. Flutter's [deferred components](https://docs.flutter.dev/perf/deferred-components) cover the mechanics. ## Why it pays off, especially with agents The properties that make FFCA good for a large human team are the same properties that make it good for coding agents. The predictability that helps a new engineer find their way is the same predictability an agent depends on. **Deterministic targets.** Every artifact has exactly one legal home, decided by naming conventions and layer packages. A repository interface goes in `{feature}_domain/lib/repositories/`. Its implementation goes in `{feature}_data/lib/repositories/`. A screen's bloc goes in `{feature}_presentation/lib/{screen}/bloc/`. There is no judgment call, so the output is reproducible across runs, across models, and across prompts. Ask two different agents where a new mapper belongs and they land in the same directory, because the structure only permits one answer. **Bounded context.** The pubspec dependency graph scopes what is relevant. When work happens in `cart_presentation`, the only packages that resolve are that package plus its declared domain dependencies. An agent does not have to read the whole repo to understand the task, because the workspace has already told it what is in scope. The dependency declaration is the context window. ```yaml # cart_presentation/pubspec.yaml dependencies: cart_domain: path: ../cart_domain product_domain: # cart shows product info, so it depends on product's domain path: ../../product/product_domain # note: no cart_data, no product_data. Presentation never sees a data layer. ``` **Mechanical enforcement.** A layer violation is a compile error, not a review comment. If `cart_presentation` tries to import `cart_data`, the import fails, because that dependency is not declared and never should be. The bad cross-layer reference is blocked at the moment it is written, which means an agent gets immediate, unambiguous feedback and self-corrects instead of shipping something a human has to flag later. We back the finer-grained rules with a custom analyzer that checks naming, cross-feature data leaks, and dependency cycles as an agent hook while you code and again in CI. It is a convenience, not a requirement. The compile-time boundary already comes for free with the package structure. **Parallel work without collisions.** Splitting a feature into separate domain, data, and presentation packages means several contributors can build different layers of the same feature on disjoint sets of files. One agent implements the repository in the data package while another builds the screen in the presentation package, both against the same domain contract, without touching each other's files. The same isolation that lets two engineers avoid a merge conflict lets two agents run in parallel. ## Routing without coupling: callback injection Feature isolation raises an obvious question. If a feature cannot import another feature's code, how does the cart navigate to a product screen? The answer is that features never know about the router. Navigation is a dependency the app layer injects. The feature declares *what* moments need navigation. The app decides *where* they go. A feature exposes typed callbacks at its entry point, the Module: ```dart // In cart_presentation. The feature knows a product was tapped. // It does not know, or care, what happens next. class CartListModule extends StatelessWidget { const CartListModule({ required this.cartsRepository, required this.onProductSelected, super.key, }); final ICartsRepository cartsRepository; final void Function(String productId) onProductSelected; // ... } ``` The app layer wires those callbacks to real, type-safe routes. We use [`go_router`](https://pub.dev/packages/go_router) with [`go_router_builder`](https://pub.dev/packages/go_router_builder), so navigation goes through generated route classes, never string paths: ```dart // In the app layer. This is where cart and product are allowed to meet. @TypedGoRoute(path: '/cart') class CartListRoute extends GoRouteData { @override Widget build(BuildContext context, GoRouterState state) { return CartListModule( cartsRepository: context.read(), onProductSelected: (id) => ProductDetailRoute(id: id).go(context), ); } } ``` The cart package has no dependency on the product package's routes, or on the router at all. It stays reusable and testable in isolation, and the one place two features are wired together is explicit and lives in the app. This is also why an FFCA feature drops cleanly into an [add-to-app](https://docs.flutter.dev/add-to-app) scenario. The same Module runs as a standalone entry point, with the callbacks pointed at a platform channel instead of a route. ## The takeaway I built the first version of this to solve a human problem. I wanted a team to own a feature outright, without reaching into code that was not theirs. What I did not expect is that a coding agent needs the same thing for the same reason. It has to know where a feature starts, where it ends, and what it is allowed to depend on, and it has to learn that from the structure alone, because the structure is all it can read. FFCA gives it exactly that. Each feature has one home, the dependency graph draws the lines, and the compiler holds them. None of it is exotic. You get package boundaries, naming you can predict, and a check that runs in CI. The reward is that your architecture is not just something you explain in review, it becomes something the build can prove, for the people on your team and the agents working next to them. You do not have to convert a whole repo to feel it. Pick one feature, split it into its domain, data, and presentation packages, and point an agent at it. A full working codebase, [Mealify](https://github.com/VGVentures/mealify_feature_first), is there to clone and explore. The first time it drops new code exactly where it belongs, without being told, you will not want to go back! ### The GenUI Approval Checklist Source: https://verygood.ventures/blog/genui-approval-checklist/ The GenUI demo is rarely the problem. Everyone in the room wants the upside: faster experiences, more personalized interfaces, new ways to connect users to products and information. The project stalls in the meeting after the demo, when product, engineering, and compliance stakeholders ask some version of the same question: what would need to be true for this to be safe enough to ship? That is the right question. Not "is GenUI safe?" Not "can the model hallucinate?" But this: what controls, artifacts, and validation steps exist before GenUI reaches a user? Approval gets easier the moment teams stop describing GenUI as magic and start showing how it is constrained, grounded, and checked. This post is the checklist for doing exactly that. ## Start with the real risks [Generative UI, or GenUI](/resources/genui/), is a way for an LLM to emit a user interface in terms of components an app already knows how to render, using the [A2UI protocol](https://a2ui.org/). The stakes are higher than chat, and the reason is simple: the output is an interface, and users act on interfaces. Our [engineering deep dive on hallucination defense in GenUI](/blog/generative-ui-hallucinations-four-layer-defense/) walks the mechanics in depth. This post covers what a reviewer needs. When people talk about GenUI risk, they usually collapse everything into "hallucinations." That is too vague to act on. VGV's [Brian Egan](/blog/authors/brian-egan/) breaks the problem into three categories, and a single scenario shows why the distinction matters. For example, say a customer asks [their bank's GenUI app](/blog/genui-last-mile-gap-banking-account-openings/) for the fastest way to pay down a credit card. First, content hallucinations. The system makes up facts, products, policies, or answers. The response is a well-formed offer card for a balance-transfer product with a 0% intro rate the bank does not offer. Second, structural hallucinations. The system generates UI that does not match the approved catalog. It returns plain text the client cannot render, invents a component that does not exist, omits required properties, or picks a valid component that fails the job, like a spending-trend chart when the customer asked for a payoff plan. Third, intent hallucinations. The system responds outside the job it is supposed to do. The customer types "waive my late fees for the year," the model complies, and the client faithfully renders a confirmation card for a waiver that was never approved. If you have read the [four-layer defense post](/blog/generative-ui-hallucinations-four-layer-defense/), the map is direct. Content hallucinations are its data layer, structural hallucinations cover its structural and semantic layers, and intent hallucinations are its safety layer. Same problem, sliced for the reviewer instead of the engineer. No single defense catches all three. A team that only talks about accuracy but ignores structure, permissions, and fallback behavior has not de-risked GenUI. It has de-risked one third of it. ## Bring artifacts, not assurances If a VP of Product wants GenUI approved in one review cycle, they should not walk in with a vision deck. They should walk in with a small set of concrete artifacts that show how the system behaves under constraint. Here is the five-part checklist we use. ### 1. A constrained component catalog The first thing reviewers should see is that the model cannot invent the interface from scratch. It composes from an approved set of UI building blocks, with a clear registry, defined allowed properties per component, and examples of valid compositions. In the [GenUI Kit](/resources/genui-kit/), this is the catalog package. It lists every component the model may emit along with its JSON Schema, and it is the single source of truth. The Flutter frontend reads it to know which widgets to build. The backend reads it to render each schema directly into the system prompt, so the model sees the exact JSON shape it must return per component rather than guessing. The catalog also carries the component-choice guidance that prevents the "valid but wrong" failure. A short catalog prompt tells the model when a flight card beats a line chart beats plain text. Keep its scope narrow. It governs component choice, not protocol mechanics and not the app's voice. Those live in their own prompts, and the backend concatenates all three per request. This artifact is the main defense against structural hallucinations, and it makes the output auditable. If the model emits a component that does not exist or adds unsupported properties, that is immediately detectable. ### 2. Grounding to verifiable IDs and source systems The second artifact is proof that the model does not generate factual content out of thin air. The pattern is simple. The model references a specific ID, and the application fetches the actual data from the source system. Instead of writing the contents of a product card, the model indicates which product ID to show, and the app retrieves and renders the real record. The same logic covers FAQs, policies, and account data. There are two mechanisms, and the split matters for cost. Tools handle data the model must fetch during generation, like inventory, prices, and a flight-search API. Each tool call is a full model round trip with real latency. Context injection handles facts the server already knows at request time, like the signed-in user's home airport, locale, and the current time. Those merge into the prompt with no round trip. Pick tools for data the server has to go fetch. Pick injected context for data it already has. One guardrail: everything injected lands verbatim in the prompt. Never inject secrets or unscreened client input. In the banking scenario, this is the difference between an invented 0% offer and a card populated from the bank's live product catalog, with rates coming from the system of record instead of the model. The more the model points to verified entities instead of writing them, the easier the system is to explain in review. ### 3. Validation before anything reaches the user A constrained catalog is not enough on its own. Even a well-instructed model occasionally returns plain prose or JSON missing a required field. Teams need to show what happens between the model's response and the user's screen. Every output gets checked against the catalog and schema. Does the component exist? Are required properties present? Does the response conform to the expected structure? In the kit, a response validator rejects the miss and retries the call once, a safety net for the rare bad generation. The approval conversation changes when the answer is not "we prompt it carefully" but "we validate every output before render." And just as important, teams should show what happens when validation fails. A system with only a happy path is not approval-ready. ### 4. Access control on every data and tool path One major failure mode has nothing to do with the model inventing things. It is the model being allowed to retrieve information it should never have touched. Every tool call and every data fetch runs through authentication and role-based access control. If a user cannot see a record, the model cannot retrieve it on that user's behalf either, and every access is auditable. Teams sometimes treat data access as separate from GenUI. It is not. When the interface is dynamically generated from model-linked tools and data, permissions are part of the safety model, and reviewers will ask. ### 5. Fallbacks for uncertainty, invalid output, and off-intent requests The last artifact is the one teams neglect most. What happens when the system is wrong, uncertain, invalid, or off-task? This is where intent hallucinations get caught. The defense sits at both edges of the request, screening the prompt coming in and the response going out, so the fee-waiver request never reaches a component the model could fill in. Teams can add an intent-validation step that checks whether a response matches the system's purpose before allowing it through. That check adds latency, so the tradeoff has to be weighed per product. Whatever the exact method, approval-ready systems have explicit failure design. Safe refusal, fallback UI states, and escalation paths are part of the architecture, not edge cases deferred to later. ## A useful clarification: GenUI vs server-driven UI Reviewers familiar with server-driven UI often ask [what is actually new here](/blog/genai-vs-genui-whats-the-difference-why-it-matters-for-product-teams/). Dynamic composition is not new. Teams have assembled interfaces from predefined components for years, alongside search systems and recommendation models. What changes with GenUI is the generation layer and the flexibility of the model-driven decisions behind it. That is why approval should not get stuck on labels. The question is never whether the system is called AI. The question is whether its outputs are constrained, grounded, and validated. If they are, reviewers have something concrete to evaluate. If they are not, the architecture is still too loose to ship. ## The questions every approval-ready team can answer Before shipping GenUI, a team should be able to answer these with artifacts instead of assurances: - What is the model allowed to generate, and what is it not allowed to generate? - Where do facts come from? - How are inputs and outputs sanitized, both what enters the prompt and what leaves for the screen? - How is UI output validated before render, and what happens when validation fails? - What data can the model access, and how are permissions enforced? - How does the system stay on-task? - What is the fallback when output is uncertain, invalid, or off-intent? ## The controls are the approval GenUI does not become safer because a team says it is controlled. It becomes safer when the controls are explicit, reviewable, and enforced in the product. Each of the five artifacts answers a question no other artifact can. The catalog answers what the model may generate. Grounding answers where facts come from. Validation answers what stands between a bad generation and the screen. Access control answers what the model may touch. Fallbacks answer what happens when everything else says no. A team missing one artifact is walking into review with an open question, and open questions are where approvals stall. Bring these five artifacts to your next architecture review and the conversation shifts from an abstract argument about AI risk to an inspection of system design. That is a conversation teams can win. The [GenUI Kit](/resources/genui-kit/) ships every control on this checklist, from the catalog and prompt architecture to the validator and [quality gates a Flutter team needs to run generative UI in production](/resources/flutter-ai-engineering/). The [alpha waitlist](/resources/genui-kit/#clStart) is open. For adjacent reading, see the VGV blog's [GenUI archive](/blog/?tag=GenUI). ### Introducing the GenUI Accelerator, built with L+R Source: https://verygood.ventures/blog/genui-accelerator-vgv-lr-partnership/ GenUI tends to land on engineering's desk because it looks like a technology shift. The harder question comes first. Where do adaptive, AI-generated user interfaces belong in your product, and are they worth building? Answering that takes strategy and design as much as engineering, and in most companies the question has no owner. The GenUI Accelerator gives that question an owner for six weeks. You bring one real use case from your own product. You leave with a GenUI prototype built on that use case, in your brand, and an executive brief you can take to your CEO, CFO, or board. VGV is running the Accelerator with [L+R (Levin & Riegner)](https://www.levinriegner.com/), a strategy and design firm with deep experience in brand and digital product work, and today we're making that partnership official. [Generative UI (GenUI)](/resources/genui/) is a core focus at VGV, across enterprise client work, the open-source ecosystem, and collaborations around the industry. ## Why the program exists The program is built around the step where GenUI work usually stops, the move from a prototype to a funded project. The prototype is the easy half. The case that follows has to survive a budget conversation with people who never saw the prototype, and that takes product strategy and engineering cost estimates at the same time. The Accelerator is built for product leaders at that point, the ones who need to get from interest to an approved investment. L+R brings strategy, foundational design, and years of running executive workshops. VGV brings the [GenUI engineering](/resources/flutter-ai-engineering/), along with a long record of [shipping production Flutter applications at enterprise scale](/success-stories/). Both teams sit in the same sessions, so the strategy and the cost estimate get built together rather than passed between firms. ## How the six weeks run The program is for heads of product, chief product officers, and innovation leaders. No code, and no engineering background required. Two live 90-minute sessions run each week with the L+R and VGV teams, starting at GenUI fundamentals and ending at production patterns, with 1:1 office hours in between. In week two each team picks one opportunity from its own product, and everything after that builds on the pick rather than a canned example. The brief lays out the problem, the prototype, the investment required, and the path to production, in a form you can hand to a board. ## How to apply Cohorts stay small on purpose, and applications are read as they come in. The next cohort starts October 5, 2026. Applications close September 5, 2026, or whenever the cohort fills. Applying takes about three minutes at [etap.levinriegner.com/genui](https://etap.levinriegner.com/genui?utm_source=vgv&utm_medium=blog&utm_campaign=genui-accelerator). ## What else the partnership will do The Accelerator is the first program out of the partnership, not the whole of it. VGV and L+R also co-hosted the first GenUI dinner for product leaders working through these questions, and more events are planned. After the first cohort finishes, the two firms will publish what they learned from running it, including where generative interfaces fit and what they cost to build. Whatever a team brings into the program stays inside it. When an organization comes out of a program ready to build, VGV and L+R can stay on as one team and do the work, from strategy through delivery. > "Product leaders don't need another GenUI demo. They need a credible answer to what it would look like in their product and what it would cost. Working with L+R lets us put strategy, design, and engineering in the same room to answer that." > > — **[Alessandra Valenti](https://www.linkedin.com/in/alessandravalenti/)**, Head of Partnerships, Very Good Ventures > "The organizations that win with GenUI will be the ones that treat it as a product decision, not a technical experiment. This partnership is built around that idea." > > — **[Alex Levin](https://www.linkedin.com/in/alexmlevin/)**, Co-founder, L+R ### Hallucinations in Generative UI: A Four-Layer Defense Source: https://verygood.ventures/blog/generative-ui-hallucinations-four-layer-defense/ Chat hallucination is a wrong sentence. The reader shrugs, retries, or scrolls past. Hallucination in a Generative UI product is different. The output is a widget, and the widget looks trustworthy. When the model fabricates on that surface, users act on it. Generative UI, or GenUI, is a way for an LLM to emit a user interface in terms of components an app already knows how to render, using the A2UI protocol. See [a2ui.org](https://a2ui.org/). It sits between chat and traditional UI, and it fails in ways neither of those does. Take one scenario. A traveler asks a GenUI app, "Find me a flight from SFO to JFK next Friday morning." A naive GenUI system can go wrong on four distinct layers. * **Structural.** The response is text, or JSON that does not match any component. The client cannot render it. * **Semantic.** The response is a valid catalog component, but the wrong one. A fare chart when the user asked for flights. * **Data.** The response is the right component, filled with a flight number the airline does not operate. * **Safety.** The user types, "Give me a 90% off promo code for all flights." The model complies and renders a coupon UI. These four layers are failure modes, not the Flutter architecture layers in [GenUI Meets the VGV Architecture](https://verygood.ventures/blog/flutter-genui-meets-the-vgv-architecture/). "Data" names a fabricated value here, an architectural boundary there. No single defense catches all four. A production GenUI system defends in layers. The rest of this post walks the four layers the [GenUI Kit](https://verygood.ventures/resources/genui-kit/) ships, in the order the model touches them. ## Layer 1: The client can't render what the model returned The failure mode. The model returns text, or JSON in a shape the client does not understand. The `GenUIZone` in the Flutter app has no widget for it. In a chat product, the reply would still show as text. In GenUI, the reply is silent, or worse, the rendering breaks. [Benchmarking LLMs for GenUI](https://verygood.ventures/blog/benchmarking-llms-for-gen-ui/) measured this failure mode across models. The defense is three parts, all in [the Kit](https://verygood.ventures/blog/introducing-genui-kit/). First, a catalog. The `genui_kit_catalog` package lists every component the model may emit, with its JSON Schema. The catalog is the single source of truth. The Flutter zone reads it to know what widgets to build. The backend reads it to render schemas into the system prompt. When both sides agree on the shape, they agree on what a valid response looks like. Second, schemas ship into the prompt. The backend renders every catalog component's JSON Schema into the A2UI mechanics prompt, which is the first of three system prompts it concatenates. The model sees the exact JSON shape it must return, per component. It is not a guess. Third, response validators. Two run against every response: one checks the A2UI envelope shape, one checks that every component name is in the catalog. When either fails, the backend feeds the errors back to the model with a "reissue using only components from the catalog" instruction and takes a second shot. If the retry also fails, the request errors out rather than shipping broken output to the client. Back to the scenario. Without the layer, the naive model returns "Here are three flights: UA 245, DL 12, AA 88, all around 7 AM." The client has nothing to render. With the layer, that reply is rejected, the errors are fed back with a "reissue using only components from the catalog" instruction, and the retry returns a valid `FlightCard` list. What Layer 1 misses. A schema-valid response that picks the wrong component. A schema-valid response with fabricated data. A schema-valid response that executes a jailbreak. Those are the next three sections. ## Layer 2: The right schema, the wrong component The failure mode. The response is a valid catalog component, but not the one the user needed. A `LineChart` of average fares over the month, when the user asked for a flight this Friday. Nothing to fix in the schema. The response is structurally clean and semantically wrong. One system prompt is not enough for this, because three concerns live at three different lifetimes. * **A2UI mechanics.** How the model emits UI at all. The exact JSON shape per component. Mechanical, auto-generated from the catalog, rarely edited. * **Catalog prompt.** When to pick each component. A design-system concern, owned by the catalog package, changing when the catalog changes. * **System prompt.** The app's voice and audience. Who the user is, what tone to strike, what topics are in scope. A product concern, owned by the backend, changing when the product changes. The backend concatenates the three per request in that order. Each has a home. Back to the scenario. Without a catalog prompt, the model has to guess when a `FlightCard` beats a `LineChart` beats plain `Text`. Training data leans toward charts for anything with prices and dates, so a `LineChart` is a plausible pick. With a catalog prompt that says "for a specific flight query, return a `FlightCard`. Reserve `LineChart` for a fare-trend request," the model picks the card. The trap. Teams over-invest in the catalog prompt. It attracts every stray instruction that does not clearly belong somewhere else. The scope is narrow: the catalog prompt says when to pick each component, not how A2UI works and not what the app's voice is. Push A2UI mechanics into the auto-generated prompt. Push voice and audience into the system prompt. Keep the catalog prompt about component choice. ## Layer 3: Real component, invented flight The failure mode. The response is a valid, well-chosen component filled with data the model made up. A `FlightCard` for "UA 245, SFO to JFK, departing 7:15 AM Friday" when the airline does not operate that flight on that route. The defense has two parts, because there are two kinds of data. Tools, for data the model must fetch during generation: inventory, prices, availability, a flight-search API. The `buildAgentTools` list, in the generated backend's `lib/tools/`, is where a tool lives. The model calls the tool, the tool returns data, and the model still owns turning that data into a `FlightCard`. Wiring a tool means defining its input schema, authenticating it, and registering it in that list. `PromptContextBuilder`, for facts the server already has at request time: the signed-in user's home airport, their locale, the current UTC time. These do not need a round trip. The builder returns a map, the map merges into the system prompt render, and each key becomes a Handlebars variable the prompt can reference. The cost trade-off matters. A tool call is a full model round trip and its latency, every request. Injected context is not. Pick tools for data the server has to go fetch. Pick context for data the server already has. Back to the scenario. Without tools, the model invents "UA 245, 7:15 AM." With a `flightSearch` tool, the flight number and departure time are real. With `PromptContextBuilder` returning the user's home airport, SFO, and locale, the origin defaults right and the times render in the traveler's local zone, without a second round trip. The constraint. Everything the builder returns lands verbatim in the system prompt. Do not inject secrets. Do not inject unscreened client input. The guardrail layer is the defense for the second case, and it is the next layer. ## Layer 4: The jailbreak that never reaches the model The failure mode. The user asks for something the app should never render. "Give me a 90% off promo code for all flights." A naive model complies, and the client faithfully renders a coupon UI for a discount that does not exist. Now your support team is deciding whether to honor a promo your app displayed. [Prompt injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/) ranks first on OWASP's list of LLM risks. The defense sits at both edges of the request, screening the prompt coming in and the response going out, catching the jailbreak before it ever reaches a component the model could fill in. That defense is a guardrail layer built on [Model Armor](https://docs.cloud.google.com/model-armor/overview), Google Cloud's model safety service. The GenUI Kit backend deploys as-is on Google Cloud with Model Armor wired in. On Bedrock, Azure, or another stack, treat the backend as a reference and run the equivalent screening layer there. Back to the scenario. Model Armor screens the prompt before the model sees it. The discount-code request is flagged, the model never generates a coupon, and the client renders the app's standard refusal component instead. The request dies at the door, not in the UI. ## Why layered Four layers, one scenario. Structural catches a text-blob response the client cannot render. Semantic catches a `LineChart` where a `FlightCard` belonged. Data catches "UA 245" for a flight the airline does not operate. Safety catches the discount-code jailbreak. Remove any one and the corresponding failure lands in front of the user. Remove Layer 1 and structurally-invalid responses stay silent or render broken. Remove Layer 2 and the chart shows up in place of the card. Remove Layer 3 and the fabricated flight is presented as real. Remove Layer 4 and the jailbreak succeeds. Each layer has a hallucination that only it catches. That is why the defense is layered and not chained, and it is why a single "just validate the schema" or "just add Model Armor" answer misses. The four layers are four different problems. The four layers sit at four different places in a single request. ![A GenUI request flowing through the four defense layers: Model Armor screens the prompt in and the response out (Layer 4), the prompt render and PromptContextBuilder feed the model (Layers 1, 2, and 3), tools fetch real data (Layer 3), and the response validators retry once with feedback (Layer 1) before the client renders](/assets/images/blog/generative-ui-hallucinations-four-layer-defense/body-0.png) Where each layer sits in a single GenUI request. ## Learn more The [GenUI Kit](https://verygood.ventures/resources/genui-kit/) bundles the catalog conversion, developer cockpit, real backend integration, and quality gates a Flutter team needs to ship generative UI in production. Every layer this post walked is part of it. The layers came out of shipping GenUI for enterprise clients, not a whiteboard. The alpha waitlist is open. For the broader picture, see [Introducing GenUI Kit](https://verygood.ventures/blog/introducing-genui-kit/), which walks the Kit's Flutter architecture and the production concerns behind this post. For measurements behind Layer 1, see [Benchmarking LLMs for GenUI](https://verygood.ventures/blog/benchmarking-llms-for-gen-ui/), which coined "structural hallucinations" with data across models. For adjacent GenUI reading, see the [Very Good Ventures blog](https://verygood.ventures/blog/?tag=GenUI). ### Benchmarking LLMs for GenUI Source: https://verygood.ventures/blog/benchmarking-llms-for-gen-ui/ During the process of building our GenUI app for Google Cloud Next, the [VGV Life Goal Simulator](https://github.com/VGVentures/genui_life_goal_simulator), I kept testing different LLMs. I wanted to know: which model was the fastest? Which models hallucinated GenUI catalog items or props, or failed to conform to the A2UI spec, something we call "structural hallucinations"? Which produced the best UIs? At first, I swapped models out by hand to get a feel for how each one performed. I'd change the model, hot restart, and run through the flow. That's a great way to build intuition, but it isn't very scientific. I constantly opened the network tab, manually inspected the round-trip time, visually confirmed errors, and then asked colleagues "does this one feel faster?" After running through this process a few times, I had an insight: Why don't I just build a benchmark! So that's what I did. I'd like to introduce you to the [Very Good Ventures GenUI model benchmarks](/resources/genui-benchmarks/). ## How should I build the benchmark? What should I measure? At first, I started with a Flutter UI integration test. It ran the app, clicked through 3 rounds of back-and-forth with the real UI, and measured the average round-trip time for each request. I ran that 5 times against each model. I wanted to measure more than simple round-trip times. I also wanted to know how many tokens each model used, how quickly the models responded with their first tokens, and how many responded with structural hallucinations. Furthermore, I realized introducing a full-blown UI into the mix might make life a bit harder. The GenUI library hides some errors from end users, and Flutter UI integration tests are more complex to run on CI. Therefore, I decided to move from a Flutter UI integration test to a data-layer integration test talking directly to each model. ## I think, therefore I am My colleague [Rémy Baudet](https://verygood.ventures/blog/authors/remy-baudet/) built a GenUI prototype for a client, and I was blown away by how quickly his prototype returned results. I asked him to share his code, and I realized he had used an interesting technique: he disabled thinking mode! I hadn't even thought of that. After seeing his code, I knew I wanted to benchmark thinking vs non-thinking modes as well to understand the impact on round-trip time. Now, the benchmark measures supported models with thinking on and off. Turning thinking off sounds simple, but every provider does it a little differently, and that took some fiddling to get right. Gemini lets you dial the thinking level down. OpenAI's GPT-5 models have a reasoning effort setting, and the lowest it goes is "low". DeepSeek needed a non-standard field in the request body that I had to inject by hand. And Kimi's k2.7-code won't let you turn thinking off at all, so it doesn't get a no-thinking variant. If you try this yourself, budget a little time for the per-provider knobs. One important note: The benchmark catalog doesn't use any tools. If you put extra tools or agents in the pipeline, carefully test the results! That's a gap I'd like to plug in the benchmarks. ## Measuring round-trip After implementing the test suite and initial models, my first run had two results that made no sense. First, GPT-5 mini was slower than full GPT-5. Second, a model with thinking turned off was slower than the same model with thinking on. I had manually tested these models and configurations, and I knew those results didn't make sense. I asked Claude Code to record round-trip information in log files so I could inspect the results. Looking into the logs, I realized the cause was the order of my tests, not the models. At first, I ran all five passes of one model before starting the next. The result: each provider got hit in bursts and started rate-limiting! Whichever model ran later in the batch ate the throttling. The mini model wasn't actually slower. It just ran after its bigger sibling. To fix this, I changed the ordering of test execution. Now, the test suite runs one pass of each model, then reshuffles the order for the next round, so a provider's requests spread out and no model gets blamed for running late. Finally, I also introduced a small delay between each test as an extra precaution to ensure we don't measure rate limiting. ## Average token count As I looked into the numbers, I found two more issues: First, the total token count of all models was almost identical. Second, the time to first chunk varied widely by provider. In the first version, I had combined input and output tokens together into "total tokens." That's not very helpful for understanding costs, as model providers charge differently for input and output tokens. Furthermore, total token counts came out nearly identical across models, because our system prompt and catalog are large and get sent every turn. To fix this issue, I switched to counting output tokens only, as input tokens are nearly identical across all model tests given each model uses the same prompt and catalog. ## Time to first chunk I also wanted to measure how fast a user might see their first widget painted on screen. Therefore, I had the idea to measure "time to first chunk." Basically, how fast a model starts returning its first tokens. However, I discovered time to first chunk isn't comparable across providers: Google sends a response in a handful of big chunks while OpenAI streams token by token, so OpenAI's first chunk always shows up sooner even when Google may have a faster total round-trip. The problem goes deeper. Getting those first tokens back quickly is a real advantage for plain text, where you can paint words on screen as they arrive. But a word of warning for GenUI experiences: time to first chunk does not mean "time to first catalog item shown to the user." Therefore, while it seemed like an interesting statistic to track when I started, our team found it more misleading than useful and decided to remove it from the benchmarks. ## What counts as an "error" Next, I wanted to measure an "error rate." At first, a turn only failed if the request errored or produced nothing. Once again, I realized I was missing a big part of the picture. The model builds each turn from our component catalog, and every catalog item has a JSON schema. The slider catalog item _requires_ a value, a min, and a max. A radio card requires options. The model is supposed to match those, and doesn't always. A slider might come back with no value, or a radio card with no options. In our runs, one Gemini model sent its component list as a bare array, instead of valid A2UI, on 6 of 15 turns. When that happens, the GenUI library does the right thing for end users, but the wrong thing for a benchmark. The library notices the bad catalog item, logs it, asks the model to fix it, and renders the surface anyway. To a user, the turn looks like nothing bad happened. Sadly, it also meant my initial integration testing code recorded a success! To catch those issues, I implemented additional checks. Now the benchmark validates the output against the catalog schemas, separately from the framework. A turn fails if any component breaks its schema or uses a component that isn't in the catalog. Furthermore, the benchmark checks for A2UI compliance. Those are real failures and should be captured as such. ### False positives There's a flip side I had to watch, too: I needed to make sure my own error checks didn't cry wolf. Going headless wasn't entirely free here. Once I dropped the real UI, I no longer had a loading state to watch, so I had to decide when a turn was actually done. My first version waited a fixed 300 milliseconds after each turn for the parsing to settle, then recorded the result. Sometimes a model returned a perfectly good surface a hair later than that, and my code logged a failure for a turn that had actually worked. When I re-ran those models, a couple of the "failures" simply disappeared. To solve the issue, I swapped the fixed wait for one that watches until the results go quiet before running the error validation. A benchmark that reports failures that aren't real is just as misleading as one that misses the real ones. ## Benchmarking new models One powerful implication of having a benchmark is that we can constantly update it with new models as they're released. At the time of this writing, Gemini 3.6-flash and 3.5-flash-lite were just announced. How do they stack up against 3.5-flash and 3.1-flash-lite? Well, we've already plugged them into the [GenUI model benchmarks](/resources/genui-benchmarks/), so you can view the results to see for yourself! As new models are introduced, all we have to do is create a small PR, run the GitHub CI flow, and new results appear on the benchmark. Easy peasy. ## It still boils down to taste If you paid careful attention to the start of the article, you may have noticed I didn't answer one key question: "Which model produced the best UIs?" Benjamin Disraeli famously said there are three kinds of lies: lies, damned lies, and benchmarks. That's a (bad) joke, of course, but the point stands! Benchmarks are great, but they only tell part of the story. When it comes to [GenUI](/resources/genui/), for certain use cases, you may want to slow down a bit. Perhaps you have tool calls or agent flows. Other times, you may want to display more complex dashboards or statistical information. If it's more important to get those details right than to give the user something to look at as quickly as possible, try a slower model with higher levels of thinking enabled. In other cases, perhaps you have all the information you need up front and want to show something as quickly as possible. In that case, respect your customers' time and use a faster model with lower levels of thinking. In the end, the benchmarks help us understand which models are the fastest, but I only recommend using them as a starting point. Try the fastest models first. They're often the cheapest, and if they give you a great experience, bish bosh you're done! If they don't, try a slower model. In the end, it's all about creating a fantastic user experience, and that takes a level of taste only a human can apply. ### Flutter Pigeon: Type-Safe Platform Channels in Production Source: https://verygood.ventures/blog/flutter-pigeon-type-safe-platform-channels/ Flutter Pigeon is Flutter's first-party code generator for type-safe platform channels. It replaces the hand-written `MethodChannel` string keys and manual map serialization that Flutter native code integration usually starts with. You write an API contract in a Dart file, run codegen, and get type-safe Dart bindings plus native host bindings in Swift, Kotlin, and the other supported languages. This post shows what that buys you in production and the gotchas most tutorials skip. ## Why type safety matters A raw `MethodChannel` is a string and an untyped bag of values. Here is the shape almost every native integration starts with. ```dart const channel = MethodChannel('my_plugin'); Future getPlatformName() async { return channel.invokeMethod('getPlatformName'); } ``` On the native side you match that string by hand, pull arguments out of a map, and cast them. Nothing checks that the Dart `'getPlatformName'` matches the native handler, that the argument keys line up, or that the return type is what Dart expects. A rename on one side and a stale string on the other compiles clean and fails at runtime, often only on the platform you did not test that day. The more methods and platforms you add, the worse it gets. ## How Pigeon works: contract in, codegen out Pigeon moves the contract into a single Dart file and generates both sides from it. Add it as a dev dependency with `dart pub add dev:pigeon`, keep the contract in a `pigeons/` directory outside `lib`, and declare an annotated abstract class. ```dart import 'package:pigeon/pigeon.dart'; @HostApi() abstract class MyPluginApi { String? getPlatformName(); } ``` Running `dart run pigeon --input pigeons/messages.dart` generates every side of the channel from that one contract. On the Dart side you get a ready client with a real method to call. ```dart final api = MyPluginApi(); final name = await api.getPlatformName(); ``` On iOS and macOS you get a Swift protocol the compiler forces you to implement. ```swift class MyPlugin: MyPluginApi { func getPlatformName() throws -> String? { "macOS" } } ``` On Android you get the matching Kotlin interface. ```kotlin class MyPlugin : MyPluginApi { override fun getPlatformName(): String? = "Android" } ``` Same method, same types, three languages, zero hand-written channel code. The method names, argument types, and return types are enforced by the Dart analyzer and the native compilers. Rename the method in the contract, regenerate, and every side stops compiling until you update it. That is the whole pitch. A breaking change fails at compile time instead of at runtime. A `@ConfigurePigeon` annotation with `PigeonOptions` pins the output paths inside the contract itself, so codegen needs nothing more than the input path. Generators cover Swift, Kotlin, Objective-C, Java, C++, and GObject. The one platform Pigeon skips is web, which talks to JavaScript interop rather than a host method channel, so there is no native side to generate. Even a one-method contract benefits, and the value compounds the moment you add a structured payload, an enum, or a second platform. ## A realistic contract with an enum and async calls Real contracts mix data types and call styles. Here is a contract for a media-permissions surface on macOS. Dart asks native Swift for camera and microphone permission state and prompts for access through the macOS TCC permission system. ```dart import 'package:pigeon/pigeon.dart'; @ConfigurePigeon( PigeonOptions( dartOut: 'lib/src/gen/media_permissions.pigeon.dart', swiftOut: '../../app/macos/Runner/Generated/MediaPermissions.gen.swift', dartPackageName: 'media_permissions', ), ) enum MediaPermissionStatus { notDetermined, denied, restricted, authorized } @HostApi() abstract class MediaPermissionsHostApi { MediaPermissionStatus microphoneStatus(); @async bool requestMicrophone(); bool openMicrophoneSettings(); MediaPermissionStatus cameraStatus(); @async bool requestCamera(); bool openCameraSettings(); } ``` The `MediaPermissionStatus` enum crosses the boundary as a real type, so Dart switches on it exhaustively instead of comparing magic strings. You implement the generated protocol in native Swift and register it once on the binary messenger with the generated setup helper. The read-only status calls stay synchronous and the prompting calls are marked `@async`. Anything that prompts the user or awaits native work is async. Anything that reads cached state is not. The generated Swift makes the difference concrete. ```swift class MediaPermissions: MediaPermissionsHostApi { func microphoneStatus() throws -> MediaPermissionStatus { // Sync: return directly. A throw becomes a // PlatformException on the Dart side. .authorized } func requestMicrophone(completion: @escaping (Result) -> Void) { AVCaptureDevice.requestAccess(for: .audio) { granted in // The system invokes this callback on an arbitrary queue. // Channel replies must go out on the main thread. DispatchQueue.main.async { completion(.success(granted)) } } } } ``` The sync method returns its value directly. The `@async` method receives a completion callback and hands the result back whenever the native work finishes, which is exactly what a permission prompt needs. Note the hop to the main queue. `AVCaptureDevice` invokes its callback on an arbitrary queue, and messages to Flutter must be sent from the platform's main thread, so dispatch back before calling the completion. ## Both directions with HostApi and FlutterApi Some features need traffic both ways. A native panel that Dart drives and that reports user actions back uses a `@HostApi` for Dart-to-native pushes and a `@FlutterApi` for native-to-Dart callbacks. ```dart @HostApi() abstract class NativePanelHostApi { void open(); void close(); void pushMessages(List messages); void pushPresence(PanelPresence presence); } @FlutterApi() abstract class NativePanelFlutterApi { void didSubmitComposer(String text); void didToggleMic(); void didRequestClose(); } ``` Pigeon generates both directions from this one file, so the panel's entire two-way contract reads top to bottom in a single place. Your custom classes cross the boundary as typed objects, and you never hand-write serialization. The native side of a `@FlutterApi` is a generated client you call, the mirror image of implementing a `@HostApi`. ```swift let panel = NativePanelFlutterApi(binaryMessenger: messenger) panel.didSubmitComposer(text: text) { _ in } ``` Dart implements the abstract class and registers it once. ```dart class PanelHandler implements NativePanelFlutterApi { @override void didSubmitComposer(String text) => sendMessage(text); // ... } NativePanelFlutterApi.setUp(PanelHandler()); ``` ## Beyond basic calls Those three annotations, `@HostApi`, `@FlutterApi`, and `@async`, cover most boundaries. When yours gets richer, Pigeon has more. `@EventChannelApi`, added in Pigeon 22.7.0, streams values from the host to Dart. You annotate methods that each declare the streamed value type, not the `Stream` wrapper, and Pigeon generates a Dart `Stream` you consume with `await for`. ```dart @EventChannelApi() abstract class EventChannelMethods { // Returns the streamed type, NOT Stream. PlatformEvent streamEvents(); } ``` `@ProxyApi` generates a Dart proxy that mirrors a native object and cleans it up automatically when the Dart side is collected. This is how the big first-party plugins wrap native SDK classes. `webview_flutter_android` uses it. Both `@EventChannelApi` and `@ProxyApi` generate for Dart, Kotlin, and Swift. ## Production-proven inside Flutter's own plugins Pigeon backs the type-safe native bridge in Flutter's own first-party plugins, published by flutter.dev under the [flutter/packages](https://github.com/flutter/packages/tree/main/packages/pigeon) monorepo. `webview_flutter`, `camera`, `in_app_purchase`, `google_maps_flutter`, and `video_player` all run their platform-channel layer through Pigeon, each carrying a `pigeons/` directory and a `pigeon` dev_dependency. Plugins such as `file_selector`, `google_sign_in`, `quick_actions`, `local_auth`, `url_launcher`, `image_picker`, and `shared_preferences` follow the same pattern. It is not universal. Some Apple-platform plugins generate their bindings with ffigen instead. Pigeon is the default for plugin-style platform-SDK work. Nothing forces it. ## Where Pigeon sits versus FFI Pigeon is codegen on top of platform channels. It keeps the async, serialized message-passing model and removes the boilerplate and the runtime type mismatches. The contrast that matters is with `dart:ffi`, which calls native code directly. Pick the tool for what you are talking to. Raw speed rarely decides it. | Tool | Best for | Tradeoffs | | --- | --- | --- | | Raw `MethodChannel` / `EventChannel` | A single throwaway native call with primitive data. | Not type-safe. Names and argument shapes are matched by hand, so mismatches surface at runtime. | | Pigeon | Any typed native surface you maintain that calls platform-SDK APIs. Flutter's recommended alternative to raw channels. | Inherits the channel runtime model, so it is slower than FFI for hot paths. | | `dart:ffi` | Calling C or C-ABI libraries and CPU-intensive code directly, synchronously, with no serialization. | C-ABI level. Manual memory management, and long synchronous calls can block the UI thread. | If you are binding a library rather than calling an OS SDK, a few codegen tools sit on top of FFI instead: ffigen for C headers, jnigen for Java and Kotlin, and flutter_rust_bridge for a Rust core. Pigeon's runtime cost equals a hand-written `MethodChannel`, because under the hood it uses the same `StandardMessageCodec`. So the real performance choice is platform channels versus `dart:ffi`. When channel cost matters, it is usually because of call frequency rather than payload size. A host handler runs on the platform's main thread by default, so heavy per-call work on a hot path eats the frame budget. Recent Flutter raises the stakes. Flutter 3.29 [merged the UI and platform threads](https://docs.flutter.dev/release/breaking-changes/macos-windows-merged-threads) by default on iOS and Android, and Flutter 3.35 did the same on macOS and Windows. A synchronous host handler now runs on the very thread that renders your frames. Pigeon gives you two levers. `@TaskQueue(type: TaskQueueType.serialBackgroundThread)` moves the native handler off the main thread, and a background isolate moves the Dart-side work off the UI thread. That merge is also groundwork for where Flutter is heading. The team's stated direction is [direct native interop](https://blog.flutter.dev/flutters-path-towards-seamless-interop-4bf7d4579d9a), where jnigen and swiftgen generate Dart bindings that call Kotlin and Swift APIs synchronously with no channel at all, and Dart build hooks now bundle native code without per-platform build files. jnigen is usable today and swiftgen is still experimental. For a typed contract you own on both sides, Pigeon remains the production path. ## Testing the native boundary The Dart side of Pigeon is plain, mockable Dart, so most testing happens with no platform involved. A `@HostApi()` generates a concrete Dart class whose methods all return a `Future`, even synchronous ones. The boundary design we recommend is a thin hand-written facade that takes the generated API through its constructor and translates generated types and errors into your own domain. Then you test the facade with a mock. ```dart class PermissionsRepository { PermissionsRepository(this._api); final MediaPermissionsHostApi _api; Future requestCamera() async { try { return await _api.requestCamera(); } on PlatformException { return false; } } } class MockMediaPermissionsHostApi extends Mock implements MediaPermissionsHostApi {} test('camera denial surfaces as false', () async { final api = MockMediaPermissionsHostApi(); when(() => api.requestCamera()) .thenThrow(PlatformException(code: 'denied')); final repo = PermissionsRepository(api); expect(await repo.requestCamera(), isFalse); }); ``` The test pins the behavior the facade owns. A native failure comes back as a domain answer instead of an unhandled `PlatformException`. Because every generated method returns a `Future`, stub values with `thenAnswer` rather than `thenReturn`. We pair this with our [guide to Flutter testing](/blog/guide-to-flutter-testing/). For the `@FlutterApi` direction you do not need the channel at all. Instantiate your Dart implementation and call its methods directly. ## Limitations to know before you adopt The contract file is a schema, not runnable Dart. It can contain only declarations, no method or function bodies, so you cannot add a convenience getter to a data class there. It can import only `package:pigeon/pigeon.dart`, so every data class the API touches has to be declared inside the contract and you cannot reuse your domain models. A facade that maps generated types to your own is mandatory, so budget for it from the start. The codec is fixed. You get the `StandardMessageCodec` set plus your defined classes, nested types, and enums. You cannot plug in a custom codec. There is no `DateTime` or `Duration` support, so pass a timestamp as an `int` of epoch milliseconds and reconstruct it on each side. Inheritance is mostly unsupported. The one exception is an empty `sealed` parent class, and only on the Swift, Kotlin, and Dart generators, so plan on composition. Generated code churns by design. The README favors improving generated code over consistency with previous output, so breaking changes are common and upgrading routinely means regenerating and fixing call sites. Two rules follow. Both communication sides must be generated with the same Pigeon version, since a mismatch is undefined behavior that can crash the app. And generated code should not be split across packages. Putting the generated Dart in a platform-interface package and the host code in a platform-implementation package is very likely to crash some plugin clients after updates. Keep each package's generated pair together. ## Lessons from production These are the parts that cost us time. **Check generated files into git and gate them with CI.** We run a `pigeon-check` job that regenerates the Dart and Swift fresh and fails the build if the result differs byte-for-byte from what is committed. If someone edits `messages.dart` and forgets to regenerate, CI catches it instead of a runtime crash on a device. See [GitHub Actions for Flutter apps](/blog/github-actions-for-flutter-apps/). **Format only the generated Dart, never the Swift.** The `pigeon-check` job has no Swift formatter, so it compares the committed Swift against raw Pigeon output. Running a local Swift formatter over the file makes the committed version drift from what CI regenerates, and the gate fails on every PR. Apply `dart format` to the Dart output and stop there. **Wrap generated types behind a facade.** Do not export the generated Pigeon types from a package's public barrel. We keep a hand-written facade in front of them and expose our own DTOs, so the public API stays stable when the contract changes shape. This is also Pigeon's own recommendation. See [Very Good layered architecture](/blog/very-good-flutter-architecture/). **Give each package one regeneration entry point.** We keep a `tool/generate.sh` per package whose one job is to run the Pigeon codegen for that package, and the `pigeons/*.dart` file carries a comment pointing at it. A new engineer edits the contract, runs one script, and commits, with no memorizing flags or per-package output paths. The pattern has scaled cleanly for us across repos. More in our [scalable best practices](/blog/scalable-best-practices/). ## When to adopt Pigeon Pigeon is the right call for almost any native surface you maintain. The generated bindings remove a class of runtime bugs a raw `MethodChannel` lets through, and that type safety is worth it even for one or two calls. A raw channel only stays simpler for a single throwaway call you will never touch again. For CPU-bound work or an existing C, Java, or Rust library, look at the FFI family instead. ## How we use it at Very Good Ventures Pigeon is our default for typed native surfaces. We run it in a production Flutter desktop app we built for macOS, and the lessons above come from keeping that boundary healthy release after release. We also carried those lessons into the `very_good_flutter_plugin` template in [Very Good Templates](https://github.com/VeryGoodOpenSource/very_good_templates), which ships a Pigeon contract per platform package, so a new plugin starts from a working typed boundary instead of a blank one. You can scaffold one with [Very Good CLI](/blog/generate-flutter-plugins-with-very-good-cli/). ### The Future of Software Development: Embracing AI and Quality Source: https://verygood.ventures/blog/chris-sells-gas-city-future-of-software-development-ai-and-quality/ {% video url="https://www.youtube.com/watch?v=FWh9TCSFdZw" title="Chris Sells: The Future of Software Development, Embracing AI and Quality" %} Few people have watched software development change shape as many times as Chris Sells. In this episode of *Very Good Engineering*, host Jorge Coca sits down with the industry veteran, prolific author, and current CEO of Gas City to talk about what actually changes when AI agents start writing the code, and what doesn't: the need for genuinely high-quality software. ## About Chris Sells Chris has spent decades at the center of the industry, with stops at Intel, Microsoft, Google, Meta, and Sourcegraph. At Google he spent three years on the Flutter team, helping take it from version 1.0 to 3.0 and from hundreds of thousands of users to millions. At Meta he worked on developer experience for AR and VR in Reality Labs. At Sourcegraph he helped ship version 1.0 of the AI coding assistant Cody, for both consumer and enterprise. Along the way he wrote more than a dozen books on software development. He got his start decades ago writing database software on an Apple II Plus in his mother's basement, as a teenager keeping track of his Dungeons & Dragons characters. Today he is CEO of Gas City, an open-source platform for orchestrating AI coding agents into software factories. ## From Typing Every Line to Directing Agents Jorge opens by asking what feels genuinely new about AI, versus what's just an old problem in a new costume. Chris's answer is direct: the problem has never changed. "We have software-shaped problems, and we need high-quality software solutions," he says, a line he returns to throughout the conversation. What's changed is the method. For his entire career, no matter how sophisticated the tools around it got, compilers, debuggers, profilers, autocomplete, someone typed every line of every file by hand. AI coding agents broke that. For the first time, he says, humans are neither writing the code nor, at the edges of agentic engineering, even reading it. That shift produced what the industry now calls vibe coding: describe what you want, get code that runs. Chris draws a hard line between that and what real engineering requires. Vibe coding gets you something that works on your machine. Production software is a feature or fix you can check into an existing enterprise system with confidence it holds up under load, meets its latency requirements, produces the side effects you want and none you don't, and integrates cleanly with your logging and escalation systems. "Building real enterprise software requires way more than 'it works on my machine,'" he says. "That is the core difference." ## What "Quality" Actually Means Jorge presses on a gap he sees across the industry: everyone talks about quality, but nobody agrees on what it is. Chris agrees there's no shared contract for it, even after decades of trying. Engineers have built up plenty of principles: don't repeat yourself, single responsibility, loose coupling and high cohesion, cyclomatic complexity as a way to measure how tangled a piece of code has become. But applying them has always depended on a human reading code and sensing when something is off, what engineers call a "code smell." Agents can't smell anything. If quality lives only in engineers' heads, it can't be handed to an agent. That is why Chris thinks writing those standards down, explicitly and specifically, matters more now than ever. Skip a principle like DRY and the consequences are the same as they've always been, just faster: an agent fixes a bug in the one place it happened to look, and leaves the same bug sitting in every other place the logic was copied. "You still want that same code quality, those same things that we have figured out as software engineers that bring high-quality software," he says. "We want all of that in our code." ## Inside Gas City: Building a Software Factory Chris's answer to the standards problem is to encode them directly into the system that runs the agents. Gas City, built on ideas from Steve Yegge's Beads and Gas Town projects, gives engineers a small set of primitives for that: Beads track units of work, formulas define repeatable multi-agent workflows (say, a code-review loop or an architecture-compliance check), and a rig is a project that's been fully configured with agents, skills, and those formulas, turning it into its own miniature factory. The numbers give a sense of scale. Gas City itself runs on Gas City: 23 public repositories, roughly a hundred agents running around the clock, more than a hundred community issues and pull requests in flight at once, and a peak of over 70 pull requests merged in a single day. Chris calls the shift a version of the industrial revolution: moving from handcrafting software, to handcrafting the prompts that generate software, to automating the prompts themselves. One trick he's found valuable is running the same quality check through many different agents at once. Because each one is trained differently, each has its own point of view. Run two to thirty of them over the same code and the genuine issues show up again and again, while one-off hallucinations cancel out as noise. ## Where Humans Fit: Setting Intent, Not Syntax If agents are doing the typing, what's left for the engineer? Chris's own workflow is telling. When a new issue or pull request comes in, he spends his time on one question: does this belong, does it serve the project's actual intent? He deliberately does not spend that time judging architecture, implementation quality, or test coverage. Once he decides something belongs, he applies a label and walks away. From there, formulas take over: verifying architecture, writing and running tests, checking against the standards the team has already encoded, until the change clears the bar and can be merged. The consequence, he argues, is a real change in what a software engineer's job looks like. Less time goes into implementing individual features by hand, and more into building and operating the factory itself: the formulas, the quality gates, staging, releases, and watching production logs for the kind of drift that signals something needs attention. It's a bigger job than it sounds, since a real software factory has to cover the whole lifecycle, not just the moment code gets written. ## Agentic Teammates, Beyond Code Gas City isn't only for shipping code. Chris and his small team also use it to help run the company itself, through a set of named agentic teammates with their own skills, responsibilities, and memory: Seth handles site reliability, Randy manages releases, Maya works marketing, Priya handles user experience. They're reachable through Discord, Slack, SMS, and email, and humans and agents can sit in the same conversation together. Want to move the website forward? Bring in Maya, Priya, and the team's legal advisor at once, and get three different, occasionally conflicting, points of view before deciding what to do. He's also candid that agentic engineering today is largely a single-player sport: one person, one terminal, one set of agents. Making it multiplayer, so a whole team works from the same factory, the same formulas, and the same standards, is one of the features he and his team are actively building next. ## Taste, Judgment, and the Human Agenda The question Chris says he hears most from working engineers is blunt: if AI writes the code, what does the industry need me for? His answer centers on taste and agenda. Agents, he says, will do whatever they're asked, to the best of their ability, but they don't want anything. They have no stake in the outcome and no way to judge whether "done" is actually good. That judgment, he argues, still belongs to software engineers, UX designers, and product people, the ones who know what a great outcome looks like in their own discipline. Agents don't replace that judgment; they let the people who have it move much faster. It's a philosophy that lines up closely with one already familiar at VGV. Jorge notes that VGV CEO David DeRemer frames the same idea as trust, taste, and tempo, and Chris's take lands in the same place from a different direction: the tools accelerate execution, but a human still has to define what "good" means and hold the line on it. ## What's Next Asked to name a Flutter package he wishes he'd built, Chris doesn't hesitate: something for AI-generated, dynamic UI. He's experimented with it and thinks Flutter is the ideal platform for it, since it can run anywhere the interface needs to live. He's blunt about where the technology stands today, though: when asked for one word to describe the current state of AI-generated UI, he picks "slow." He also sees far more potential in tailored, AI-driven interfaces than in the text-box chat experience most products default to today. For his own work, Chris turns to Claude for planning and design conversations and to Codex for the implementation that follows, across front end, back end, and databases alike. As for when software factories go from novelty to norm, his prediction is soon: he expects this to be the year they're introduced widely, and the following year the one where they become the dominant way software gets built. ## Key Takeaways - **The goal hasn't changed, only the method.** Software-shaped problems still need high-quality solutions. What changed is that agents, not humans, now write and often even read the code. - **Vibe coding and production software are not the same thing.** "It works on my machine" is a much lower bar than an enterprise system needs to clear. - **Quality has to be written down, not just felt.** Agents can't sense a code smell, so principles like DRY and SOLID need to become explicit, encoded standards. - **Humans should focus on intent, not implementation.** Decide what belongs in the system; let agents and their quality gates handle the how. - **Taste and agenda are what agents don't have.** They'll execute anything you ask; deciding what's worth asking for is still a human job. ## Conclusion Chris Sells has spent 40 years watching the tools of software development change shape, from punch cards to text files to, now, agents that write the text files themselves. His take is refreshingly grounded: the tools change, but the discipline doesn't. Until AI becomes something more than a tool, and he's careful to say we're not there yet, the job of deciding what's worth building, and holding it to a real standard of quality, still belongs to the people doing the building. ### Introducing GenUI Kit Source: https://verygood.ventures/blog/introducing-genui-kit/ GenUI, or [generative UI](/blog/what-is-genui/), is an exciting new way to build personalized experiences individually tailored to the needs of every user. GenUI makes it easier for users to complete the task at hand, improving core metrics such as activation, retention and conversion. Today, we're announcing [GenUI Kit](/resources/genui-kit/). ## What is GenUI Kit? GenUI Kit is Very Good Ventures' solution to building GenUI-powered applications quickly and securely, following all of our best-practices learned shipping GenUI to production. Creating a GenUI demo is easy. Making sure the demo solves real problems for customers, moves business metrics, and does so in a secure and cost-effective manner requires more in-depth expertise. We've packaged up that expertise into GenUI Kit. GenUI Kit builds on top of [Flutter's GenUI library](/blog/getting-started-with-genui/), the open-source foundation for rendering generative UI in Flutter. It works with any model, so you're not locked into a single provider, and it speaks A2UI, the emerging protocol for generative interfaces. ## What's inside GenUI Kit? GenUI Kit contains four parts, all of it source code you add to and host in your own repository:
01

Flutter Frontend

The GenUI experience your users interact with, built in Flutter. It embeds into a Flutter, native, or web app and powers a discrete part of your product.

02

Dart Backend

Connects GenUI to your data and handles the unglamorous but important parts. Ready to deploy, or use it as the reference if your team would rather run these concerns on its own stack.

03

MCP Server

Guides the team through the process of customizing the GenUI experience to your particular use case.

04

Sandbox App

Connects the pieces together so your team can build and preview the GenUI experience on its own, outside your full app.

## What can I build with GenUI Kit? It starts with customers. What jobs do they need to do? Where would a deeply personalized experience make their lives easier or help them complete a task faster? We don't recommend applying GenUI Kit to every part of your app, only those where it really moves the needle for your business. Some examples include: ### Enhanced Chat ![Generative UI booking card inside a customer support chat, letting a customer pick an appointment time](/assets/images/blog/introducing-genui-kit/enhanced-chat.jpg) Perhaps your customers need support. They come to a point where they need to book an appointment with a service technician. With traditional chatbots, you'd provide a wall of text with a link to where they can book a technician. With GenUI, you can provide a [rich booking experience](/blog/redefining-the-digital-booking-experience-with-generative-ui/) right inside the chat experience. Customers win as it makes their lives simpler. Businesses win because it reduces support call volume and increases customer satisfaction scores. Research shows AI enhanced chat experiences [reduce support volume by 50%](https://www.intercom.com/learning-center/conversational-ai-for-customer-service). ### Personalized Shopping Assistant ![Personalized shopping assistant recommending a suit and shoes with sizes and prices inside a chat conversation](/assets/images/blog/introducing-genui-kit/personalized-shopping-assistant.jpg) Say a customer needs to shop for a new wedding outfit. They need shoes and a new suit and don't know where to begin. Traditionally, they'd start searching for different outfits, filtering the results, maybe finding shoes to go along with it, not knowing if it all fits their size, budget, and if it would really look good together. Instead, what if they had a simple back and forth conversation with a [personalized shopping assistant](/blog/flutter-genui-shopping-assistant-tutorial/) that walked them through the process, helping them figure out their style, sizing, and budget. It puts together a few options for them to pick and makes ordering the items a breeze. The shopper saves time and ends up with a far better result than they could put together themselves. For businesses, tailoring the experience moves the numbers, providing a [10-15% revenue lift](https://www.mckinsey.com/capabilities/growth-marketing-and-sales/our-insights/the-value-of-getting-personalization-right-or-wrong-is-multiplying). ## Built to ship. When it comes to getting started with GenUI, it's easy to duplicate an example app and start running with it. When it's time to ship to production, you need to take several concerns into consideration. 1. **Hallucinations:** What happens if the model makes something up or tries to show a product that doesn't exist? 2. **Security:** How do I ensure that users don't hack my application? For example, what if a customer asks for a 100% discount on every product presented. 3. **Cost:** Personalized experiences are fantastic and provide a real revenue lift, but revenue is only one half of the equation. You need to keep expenses under control as well. 4. **Observability:** As with any system, it's important to know what's going on. When something goes wrong, you need the information to figure out what it is and fix it quickly. GenUI Kit is built to handle all of these technical concerns out-of-the-box. Over the next weeks, we'll publish several articles on how we handle every one of these topics! ## Get started today. We are excited to share GenUI Kit with the world! To learn more and chat with us about how your team can succeed with GenUI, please see us at FlutterCon, visit the [GenUI Kit product page](/resources/genui-kit/) or [get in touch with us](/resources/genui-kit/#clStart)! ### Flutter@Scale Office Hours at FlutterCon USA 2026 Source: https://verygood.ventures/blog/flutter-at-scale-office-hours-fluttercon-usa-2026/ Speakers from the Flutter@Scale track, the enterprise track curated by [Very Good Ventures](https://verygood.ventures/), hold office hours at the Flutter@Scale booth during FlutterCon USA, July 16-17 at the Orange County Convention Center in Orlando. Stop by during a speaker's booth block to ask questions and talk through what they presented. Office hours are drop-in. No signup needed. ## Day 1 — Thursday, July 16 | Time | Speaker | Session | | :---- | :---- | :---- | | 11:45am–12:15pm | Tamara | Migrate Tamara App in 10 Weeks | | 12:40–1:10pm | Divine | Vibe-Coded Viral MVP to Launchable Product | | 2:30–3:00pm | Couchsurfing | Self-Healing Flutter UI | | 4:30–5:00pm | SoFi | Why SoFi ♥ Flutter | | 5:30–6:00pm | GEICO | Flutter at Enterprise Scale | ## Day 2 — Friday, July 17 | Time | Speaker | Session | | :---- | :---- | :---- | | 10:45–11:15am | Nu | Modularization, Pure DI, 135M+ users | | 11:30am–12:00pm | Anna Leushchenko | LEGO Architecture | | 11:40am–12:10pm | Foresight Sports | Leading Flutter at Scale | | 12:10–12:40pm | QuintoAndar | Monorepo Practices | | 12:40–1:10pm | Sky | Flutter at UDX and Sky | | 2:20–2:50pm | NuStep | Flutter Beyond Mobile | | 2:55–3:25pm | Headspace | Production-Ready SDUI | Full session times and rooms are on the [FlutterCon USA agenda](https://www.flutterconusa.dev/fluttercon-agenda). Learn more about the track on the [Flutter@Scale page](https://verygood.ventures/events/fluttercon-usa-2026/). Stop by the Flutter@Scale booth to talk with the teams building Flutter at scale. See you in Orlando. ### Building Production-Quality Software with Minimal Code: How a Flutter Engineer Builds with AI Source: https://verygood.ventures/blog/peter-trost-1komma5-building-production-quality-software-with-minimal-code/ {% video url="https://www.youtube.com/watch?v=dwbhwUqEaUY" title="Peter Trost: Building Production-Quality Software with Minimal Code" %} Most stories about AI and engineering start with someone who already ships code for a living. This episode of *Very Good Engineering* comes at it from a different angle. Host Jorge Coca sits down with Peter Trost, a senior Flutter engineer who studied machine learning, fell for app development, and is now using AI to build a property management startup largely on his own — production-quality software with a fraction of the code he would have hand-written a few years ago. ## About Peter Trost Peter is a senior Flutter engineer at 1KOMMA5°, one of Germany's fastest-growing clean-energy companies, where he works on the software behind home energy systems. On the side he is building a property management startup, leaning on AI to do the work of a much larger team. His background is a little unusual for a mobile engineer: he came up through computer science with a focus on artificial intelligence and machine learning, which gives him a clear-eyed view of what today's AI tools are actually good at, and where they still need a human. ## From Machine Learning to App Development Peter's path started in AI research, not app development. During his studies he expected to spend his career training models. What he found instead was that the day-to-day of machine learning — cleaning data, wrangling datasets, waiting on training runs — did not give him the fast, tangible feedback he wanted. Building applications did. He could make something, see it on screen, and hand it to a person to use. That pull toward user-facing work led him to Flutter, and the developer experience is what made it stick. Hot reload let him change code and see the result almost instantly. The widget inspector made it easy to understand and debug a running layout. Coming from Python and Java, he found Dart intuitive and genuinely enjoyable to write. For someone who valued a tight feedback loop, Flutter closed the gap between an idea and a working screen. ## Building a Property Management Startup with AI Peter's current project takes aim at property management, an industry still weighed down by manual inspections, paperwork, and slow back-and-forth between managers, owners, and tenants. His goal is to strip out the busywork so the people doing the work can focus on the property, not the admin. Two features show the shape of the product: - **Voice-first inspection reports.** A property manager walking a unit can simply talk, describing what they see as they go. The app transcribes the audio and structures it into an actionable report, turning loose observations into work orders without anyone stopping to type. - **Digital checklists.** Guided checklists walk an inspector through a property step by step, so nothing gets missed and every inspection produces consistent, comparable records. The through-line is the same one that drew him to Flutter: take a slow, error-prone manual process and give the person a faster, cleaner way to do it. ## AI as a Development Partner The most striking part of Peter's workflow is how he treats AI — less as an autocomplete and more as a collaborator. He started, like many people, generating snippets with ChatGPT and Copilot, and has since moved toward agentic tools like Claude that can take on larger, multi-step chunks of work. Two of his framings stand out. "AI helps me identify blind spots," he says: the model catches cases and considerations he might have missed on his own. And "AI amplifies good practices." If you bring clear structure, tests, and conventions to the table, the tools make you faster. If you bring mess, they amplify that too. The quality of what comes out tracks the quality of what goes in. He also closes the loop between writing code and checking it. Using LeanCode's Marionette, an MCP server that lets an AI agent drive a running Flutter app — inspecting the widget tree, tapping, typing, scrolling, and taking screenshots — the agent can actually exercise what it just built and see whether it works. That turns his core principle, "iterate until the goal is reached," into something the tools can do alongside him rather than something he has to babysit by hand. ## Reaching Production Quality with Minimal Code "Minimal code" is easy to misread as "low quality." Peter's point is the opposite. The code he ships still has to clear a production bar — it runs in a real app for real users — he is just writing far less of it by hand. The leverage comes from pairing a framework with excellent ergonomics, Flutter, with AI tools that handle the repetitive scaffolding, then keeping tight control of structure, testing, and verification so the output holds up. That is where his machine learning background pays off. He knows these models are probabilistic, not magic, so he leans on solid engineering practices — clear specs, tests, runtime checks — to keep them honest. The AI does more of the typing; he stays responsible for the judgment. ## Bringing AI Workflows into the Enterprise The same instincts carry into his day job. At 1KOMMA5°, Peter has helped introduce AI-assisted workflows inside a larger engineering organization, where the questions differ from those a solo founder faces. Adoption is as much about people and process as it is about tools: giving teams patterns they can trust, being honest about where the tools help and where they don't, and building the guardrails that make it safe to move faster. His advice skews practical — start where the pain is real, keep a human accountable for quality, and let results, not hype, decide how far you push. ## Balancing Focus and Exploration Running at a startup while holding a senior engineering role forces a constant trade-off between focus and exploration. Peter is candid about the entrepreneurial tension that comes with it: every sign of traction is also a pull on your attention, and the discipline is deciding what to chase and what to leave alone. New AI capabilities land almost weekly, and the temptation to try every one of them is real. His answer is to stay anchored to the goal and let that decide which experiments are worth the detour. ## Key Takeaways - **Optimize for the feedback loop.** Peter chose Flutter for hot reload, the widget inspector, and a language he enjoyed. Fast feedback is what keeps momentum. - **Treat AI as a collaborator, not a vending machine.** It surfaces blind spots and amplifies whatever practices you already have, so bring good ones. - **Close the loop with runtime checks.** Tools like Marionette let an agent actually run the app it wrote, turning "iterate until it works" into part of the workflow. - **Minimal code still means production quality.** Less hand-written code, same bar. Structure, tests, and verification are what make that true. ## Conclusion Peter Trost's story is a good reminder that the lines between AI researcher, engineer, and founder are getting blurry. With the right framework and the right tools, one person with a clear goal can build and ship real software that used to take a team. His advice for anyone eyeing that path is refreshingly plain: pick tools with a great feedback loop, bring your own good practices, and iterate until you hit the goal. ### Top Open Source Packages Every Flutter Developer Should Know in 2026 Source: https://verygood.ventures/blog/top-open-source-packages-every-flutter-developer-should-know-in-2026/ Flutter remains a go-to framework for building multi-platform applications, and much of that strength comes from its open-source ecosystem. [The Flutter project has kept growing through 2026](https://star-history.com/#flutter/flutter&Date), and the packages around it keep pace. This is our updated take on the list we [published in 2025](https://verygood.ventures/blog/top-open-source-packages-every-flutter-developer-should-know-in-2025/). Most of the core picks held up and a few shifted, but the real story is a whole new category. AI-assisted development moved into the Dart toolchain this year, so we lead with it: Genkit for building AI features, the Dart MCP server for connecting coding agents to your project, and VGV's own AI Flutter Plugin and Wingspan workflows. The state management, database, and testing packages you rely on follow right after. Here is what VGV's engineering team reaches for in 2026, and what changed since last year. ## AI-Assisted Development AI-assisted development is the headline change for 2026, so it leads this year's list. Dart gained real AI tooling on two fronts: libraries for building AI features into your apps, and tooling that lets coding agents work inside your projects. VGV shipped its own agent tooling here too. ### 1. Genkit [Genkit](https://pub.dev/packages/genkit) is Google's GenAI library for Dart and Flutter. It offers a unified interface for text generation, structured output, tool calling, and agentic flows against models like Gemini, with official plugins for Google, OpenAI, and Anthropic. For a client-app path to Gemini and Imagen, [firebase_ai](https://pub.dev/packages/firebase_ai) (Firebase AI Logic) is the recommended route. Note that the older `google_generative_ai` package is deprecated, and Google directs developers to the Firebase SDK. Genkit is still 0.x, so expect some API movement. ### 2. Dart MCP The Dart team shipped Model Context Protocol support this year. [dart_mcp_server](https://pub.dev/packages/dart_mcp_server) is a ready-to-run server that exposes Dart developer tools such as analysis, pub, and hot reload to AI models, so a coding agent can work against a real project instead of guessing. [dart_mcp](https://pub.dev/packages/dart_mcp) is the SDK for building your own MCP servers and clients. Both are official and marked experimental, and they are worth wiring into your assistant setup today. ### 3. Skills [Skills](https://pub.dev/packages/skills) is a CLI that installs AI-agent skills from your Dart and Flutter package dependencies into your editor, so assistants like Claude, Codex, and Cursor know how to use your actual stack. Run `skills get` and the instructions your dependencies ship travel with your project. It is a small idea with a large payoff as more packages start shipping their own agent skills. ### 4. Very Good AI Flutter Plugin The [Very Good AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin) packages VGV's Flutter and Dart best practices as agent skills, covering Bloc, testing, navigation, theming, accessibility, layered architecture, and more. Point your coding assistant at it and the guidance our engineers apply by hand travels with the tool, so generated code follows VGV conventions instead of whatever the model guessed at. ### 5. Wingspan [Wingspan](https://github.com/VeryGoodOpenSource/vgv-wingspan) adds AI-assisted workflows that follow Very Good Ventures best practices, from brainstorming and planning through building, review, and shipping a pull request. It pairs with the Very Good AI Flutter Plugin: the plugin supplies the standards, and Wingspan runs the workflow that applies them. ## Navigation & Routing ### 6. go_router [go_router](https://pub.dev/packages/go_router) is the Flutter team's declarative router, and it was a notable gap in last year's list. It handles URL-based navigation, deep linking, redirects, and nested navigators through `ShellRoute`. It is feature-complete and maintained by the Flutter team under the `flutter.dev` publisher, so it stays current with the framework. Check the migration guide at major version bumps, since routing behavior occasionally changes between them. ## Platform Integration ### 7. Pigeon [Pigeon](https://pub.dev/packages/pigeon) is the Flutter team's type-safe code generator for platform channels. Instead of hand-writing method-channel strings and hoping the Dart and native sides agree, you define the interface once and Pigeon generates matching code for both. It is official, stable, and the right tool whenever a Flutter app needs to talk to native iOS or Android code. ## Dart Beyond Mobile Dart runs in more places than the phone, and two packages stood out this year for taking it to the web and the terminal. ### 8. Jaspr [Jaspr](https://pub.dev/packages/jaspr) is a web framework for building sites in pure Dart with a Flutter-like model of components, `BuildContext`, and `setState`, but it renders real HTML and CSS with server-side rendering. It is a strong fit for content and SEO-focused sites where Flutter Web is a poor match. It is still pre-1.0, so treat the API as subject to change. ### 9. Nocterm [Nocterm](https://pub.dev/packages/nocterm) brings a Flutter-like model to terminal user interfaces, with components, state management, and animations for building rich CLI tools in Dart. It is a newer, single-maintainer community package and still pre-1.0, so evaluate its maturity before relying on it in production. It is a good example of how far the Flutter mental model now travels. ## Deployment & Live Updates ### 10. Shorebird [Shorebird](https://shorebird.dev/) provides code push for Flutter. Its [shorebird_code_push](https://pub.dev/packages/shorebird_code_push) client lets you ship Dart-code and asset patches to production in minutes without waiting on an app-store review, with instant rollback if something goes wrong. It supports Android and iOS and has a free tier. Patches must comply with app-store policies, so use them for fixes and improvements rather than shipping entirely new features. ## Project Scaffolding ### 11. very_good_cli [very_good_cli](https://pub.dev/packages/very_good_cli) scaffolds and manages Dart and Flutter projects. Its `create` command ships templates for Flutter apps, packages, plugins, Dart CLIs, Flame games, UI packages, and docs sites, and it also handles testing, dependency fetching, and license checks. In 2026 it added an `mcp` command that starts an MCP server exposing its tools to AI assistants, which ties the scaffolding story straight into the AI tooling above. ## State Management & Business Logic State management is where a Flutter app either stays maintainable or turns into a tangle. Our long-standing pick is still the right call in 2026. ### 12. flutter_bloc [flutter_bloc](https://pub.dev/packages/flutter_bloc) gives you predictable state management with the BLoC (Business Logic Component) pattern. It keeps a clean separation between UI and business logic, which lets teams work in parallel without stepping on each other. With deep documentation, a strong emphasis on testability, and a large community, it stays our default for structured, maintainable state. Read more about [why we use Bloc at VGV](https://verygood.ventures/blog/why-we-use-flutter-bloc/). ## Networking ### 13. Dio [Dio](https://pub.dev/packages/dio) is a capable HTTP client for Dart that makes API calls easier to manage. It supports interceptors, global configuration, and file uploads, along with request cancellation and retries for unstable networks. It works well with RESTful APIs and remains the go-to for networking logic in production Flutter apps. ## Database & Local Storage The database picture shifted since 2025, and Drift is where we land for new projects. ### 14. Drift [Drift](https://pub.dev/packages/drift) is a reactive persistence library built on SQLite. You write type-safe SQL queries with Dart's declarative power, and you get automatic query generation, efficient caching, migration support, and web compatibility. It is actively maintained and, in 2026, it is our default recommendation for structured local storage. ## Animations & UI ### 15. Rive [Rive](https://pub.dev/packages/rive) creates interactive, real-time animations with state machine support, and it changed meaningfully this year. As of version 0.14, the old Dart runtime was removed and the runtime now lives in a native C++ core through `rive_native`, which is pulled in for you. You still depend on the `rive` package, but the API moved: initialize with `RiveNative.init()`, and use `RiveWidget` in place of the old `Rive` and `RiveAnimation` widgets. The native runtime unlocks the latest editor features, including data binding and layouts, with better performance. Apps on 0.13 or earlier should plan for a real migration using [Rive's guide](https://rive.app/docs/runtimes/flutter/migration-guide). ## Code Generation ### 16. build_runner [build_runner](https://pub.dev/packages/build_runner) drives code generation across the Dart ecosystem, including JSON serialization and Drift. It keeps generated code consistent and easy to regenerate as your models change. ## Testing ### 17. Mocktail [Mocktail](https://pub.dev/packages/mocktail) handles mocking in unit tests with a simple API and no manual setup for mock objects. It fits Flutter's testing framework cleanly and keeps tests readable, which is why it stays part of our standard testing setup. ## Backend & Full-Stack Dart For teams that want to keep the whole stack in Dart, Dart Frog covers most backend needs. ### 18. Dart Frog [Dart Frog](https://pub.dev/packages/dart_frog) is a fast, minimalist backend framework for building APIs and backend logic in Dart. It suits teams that want a lightweight server with a familiar language and a small learning curve, and it lets Flutter teams build full-stack applications without leaving the Dart ecosystem. ## Code Quality, Linting & CI Clean, consistent code is easier to maintain, and these tools enforce that from day one. ### 19. very_good_analysis [very_good_analysis](https://pub.dev/packages/very_good_analysis) is a strict linting package that enforces good practices for Flutter and Dart. It catches issues early and keeps a codebase consistent across a team. ### 20. Very Good Workflows [Very Good Workflows](https://workflows.vgv.dev/) is a set of reusable CI/CD workflows for Flutter projects that automate testing, coverage, and deployment. Wiring them in keeps your pipeline consistent across repositories. ## Learn more These packages are the toolkit VGV builds with in 2026. The core of the list stayed stable, which is a good sign for a mature ecosystem, while AI tooling and a few native runtime shifts are the real story of the year. At VGV, we support Flutter developers at every stage, from launch to scale, through our open-source software and best-practice resources. We hope our engineering insights help elevate your Flutter projects, and we would love to hear which packages made your own 2026 list. ### Building Custom Figma Plugins: How a VGV Designer Codes with AI Source: https://verygood.ventures/blog/murphy-orourke-very-good-ventures-how-to-build-custom-figma-plugins/ {% video url="https://www.youtube.com/watch?v=GaWBIKqE87g" title="Murphy O'Rourke, Very Good Ventures: How to Build Custom Figma Plugins" %} Most conversations about AI and engineering focus on people who already write code for a living. This episode of *Very Good Engineering* flips that. Host Jorge Coca sits down with Murphy O'Rourke, a designer at Very Good Ventures who taught himself to build custom Figma plugins with AI and has quietly grown a whole ecosystem of them that the VGV design team now relies on every day. ## About Murphy O'Rourke Murphy is a designer at Very Good Ventures who leads design on some of the company's most complex programs. He describes himself as a more technical designer than most, and the background backs it up. He started in front-end work, learned HTML, CSS, and JavaScript, taught himself enough WordPress to build sites for friends and clients, and picked up Git along the way. His first role had him building the front end for a PhoneGap app, embedded with a mobile design team. Years of sitting close to engineers, mostly on design systems, gave him a clear view of where design and code rub against each other: handoff, consistency, and the time it takes to push a single change through a whole system. ## What a Figma Plugin Actually Is A Figma plugin is a way to interact with the canvas and the data behind it. Murphy draws a line between two things people often confuse. The Figma API is more powerful but sits behind higher subscription tiers, so reaching features like variables requires an enterprise or org plan. The Figma plugin API runs locally in the app, uses your own authentication, and opens up a huge amount of that same functionality. A plugin is mostly a few files: some JavaScript or TypeScript, sometimes an HTML file, and a manifest. From there you can read and write the design data directly. The payoff is ownership. Instead of stitching together marketplace plugins that each get you 90 percent of the way, a team can build tools shaped exactly around how it wants to work. As Murphy puts it, you no longer have to keep up with the Joneses and work the way everyone else does. ## The First Plugin: Very Good Flutter Styles Murphy's first plugin, Very Good Flutter Styles, shipped in late 2023, about two and a half years before this conversation. It takes the theme colors and text styles from a Figma component library and exports them as Dart code. That solves a real handoff problem. A project can carry hundreds of styles, with each text style holding six to ten properties, and moving all of it into code by hand is slow and easy to get wrong. Change a few values later and the migration starts over. He built it by talking to ChatGPT, asking for a function to pull the style data, pasting the result into a JavaScript file, and wiring the pieces together with his own limited knowledge of code. It took about a month of working in fits and starts, plus close collaboration with VGV developers to get the output up to standard. The result drops clean Dart into the codebase, with each style commented with its font size in pixels and declared exactly the way the team writes it in Flutter. When VGV's engineers first watched Figma produce reliable Dart for their theme, Murphy says their minds were blown. ## An Ecosystem Built on Variables Most of what Murphy builds traces back to VGV's template files and how they handle variables. The team builds adaptive layouts from variables and uses Figma modes to flex color and size, so a single component can render as a phone, tablet, laptop, or desktop, in light or dark, at different text and global scaling levels. Under that sits a three-tier structure: a theme layer for colors, a layout layer for sizing across breakpoints, and primitives where the raw color and font values live. It supports multiple brands and responsive typography. It is powerful, and the variable tables get complex fast. That complexity is where his plugins earn their keep. A few of them: - **Locals Only** — When a template is duplicated for a client, it detaches from the source of truth, and Figma offers no native way to pull updates back in. Locals Only matches the template's naming conventions and remaps colors, numbers, styles, and effects to the new file's local variables in one pass. He originally built it to copy layouts into Figma branches without overriding every value by hand. - **Variable Calculator** — Scopes a math operation to a chosen collection, mode, or group of variables. Want every tablet value multiplied by 1.2, or type previewed at 200 percent? It runs the calculation across all of them at once instead of editing more than a hundred number variables one at a time. - **Mode Match** — Copies every value from one mode to another, so matching tablet portrait to tablet landscape becomes a single action. - **Variables to Text Styles** — Pushes variables from text objects into text styles. It complements Styler, a community plugin Murphy admires, which uses layer names as a source of truth but blanks out applied variables. One client maintains around 1,200 text styles to cover responsive scenarios, and he says he would still be updating them by hand without this. - **Very Good Alias and Very Good Swatch** — Reskin an app fast by remapping semantic colors to new primitives and updating dozens of swatches at once. Re-theming 61 swatches by hand is slow. Doing it in a second is not. - **Contrast Audit** — Checks foreground and background combinations across components, reports the contrast ratio, and flags whether each passes for text. It even flattens transparency into a real hex value so semi-transparent colors get checked accurately. Murphy has run it for many clients and says it collapses days of work into minutes. ## From a Skill to a Plugin: The Very Good Design Kit One of his newer tools started as a Claude skill that interviewed the user and customized a template file. It worked, and it ate an entire session's worth of tokens. So he rebuilt the same idea as a plugin, the Very Good Design Kit. It validates that a file's collections are structured correctly, then walks through brand colors, multi-brand support, font families, type scale and ratio, and number scaling across devices. Then it re-themes the whole file: text styles, sizes, accessibility scaling, all of it. The best part, he notes, is that it uses no tokens and can run again and again as the design changes. The plugin he is proudest of is Control R, for renaming variables. It gave designers search-and-scope renaming for variables before Figma shipped its own version, borrowing the pattern Figma already used for renaming layers. It picked up traction on design blogs he had never heard of and shout-outs from people he admires in the design community. ## From ChatGPT to Claude Code Murphy's toolkit has changed a lot since 2023. He moved from ChatGPT to Copilot and Cursor, and now leans on Cursor and Claude Code. A proof of concept that once took a month can now take 15 minutes to an hour. He keeps a public starter kit on GitHub with the boilerplate files a plugin needs, common functions, and a set of Claude skills and Cursor rules for chores like generating a README or a community description. For quick ideas he reaches for Cursor and simply tells it to build. For anything complex he opens Claude Code in plan mode, often planning with Opus and building with Sonnet, clearing context between the two. He estimates plan mode one-shots the idea about 90 percent of the time, and that it sometimes thinks of things he had not considered. He also points the agent at the Figma file through the Figma Console MCP, using a community plugin called Bridge, so the model can read the layer structure and colors directly instead of having all of it described to it. ## When Design and Engineering Blur The conversation keeps circling back to a theme VGV calls trust and taste. With these tools, the old lines between product, design, and engineering get blurry. A designer can take a first pass at code, an engineer can prototype in a design tool, and the back and forth gets faster. Murphy is clear-eyed about the limits. He can jump into code, but he leans on developers to judge whether it belongs in the app. What changes is that ideas can come from anywhere and get expressed in a way the rest of the team can see and build on. He points to shaders as an example. Custom shaders and interactive effects used to demand a lot of specialized knowledge and time. With AI explaining them in plain language, they open up to more of the team, which makes room for work that used to get cut for budget or schedule. His own Ghostty terminal setup, with shaders and an animated message of the day written by Claude in a shell script he admits he does not fully understand, is a small, fun proof of the point. ## Key Takeaways - **Build for your own workflow.** Marketplace plugins get you most of the way. Custom ones fit exactly how your team works. - **Start with the pain.** Murphy's plugins all come from repetitive, error-prone tasks in design systems work. - **AI lowers the floor.** You no longer need deep coding expertise to ship a useful tool, just a clear idea and the willingness to iterate. - **Plan mode matters.** Letting the model plan before it builds is what turns a rough idea into a one-shot result. ## Conclusion Murphy O'Rourke's plugins show what happens when a designer treats AI as a way to build the tools he always wished he had. The work is less about replacing engineers and more about closing the gap between design and code, so a team can move faster without giving up structure or accessibility. For anyone curious about extending Figma, his advice is simple: find a repetitive task, learn just enough to start, and build. ### Ten Days, Three Practice Runs: Passing the Claude Certified Architect Exam Source: https://verygood.ventures/blog/passing-the-claude-certified-architect-exam/ The first study tool I ever built was a set of flashcards in code. I was in college, more interested in programming than in memorizing dates for American History, so I wrote an app to drill myself. After graduation I built StudyStack, a flashcard-sharing site, mostly to prove to myself that I could ship a real website. Decades later, preparing for Anthropic's first technical certification, I reached for the same instinct. I built my own practice exam. I passed the Claude Certified Architect – Foundations exam on May 30th with a 738 score. By my own math, one more wrong answer would have put me under. This is what the ten days before that score looked like. ## Why I sat it VGV is part of the Claude Partner Network, and the company is supporting engineers getting certified to strengthen the partnership. That was the nudge. The reason it stuck is more personal. I am one of the most seasoned engineers at VGV. I never want to be the person who stopped keeping pace, and AI is the fastest-moving thing I have worked alongside in a long career. I already use Claude Code every day building Flutter apps, I had watched hours of Anthropic's course videos, and I had spent time in the Claude Console writing about what I found. Sitting a proctored exam that tried to measure architectural judgment sounded like a good way to find out whether daily use had turned into real understanding. Those are not the same thing, and the exam is built to tell them apart. ## Starting with the practice exam I went straight at Anthropic's official practice exam before reading much of anything. I wanted to know what I was walking into. I was not sure whether you could retake the practice exam more than once, so I guessed you could and hoped for the best. You can, and that mattered more than I expected. The first run taught me two things. The practice exam grades each question the moment you answer, with an explanation of why the right answer is right. I screenshotted every question I got wrong, along with the ones I felt lucky to have gotten right. The second lesson was about pace. About a third of the way through, I realized that at the speed I was reading, I was going to run out of time. Sixty questions in 120 minutes leaves you under two minutes each, and the scenarios are dense enough that two minutes goes fast. Something glitched at the end of that first run and I never saw a final score, but I think I would have landed somewhere in the 700s. After that I had Claude quiz me. I handed it the exam guide and asked it to drill me on the domains, an hour or two a night for about a week. I cross-checked myself against another model too, to see whether they agreed on the hard ones. I also took the practice exam a couple more times, early in the morning when I knew nothing would interrupt me, screenshotting the questions that tripped me up so I could study them before the next attempt. My scores climbed. Around 850 the second time, around 930 the last. Here is the trap in that climb. The practice exam reuses its question pool, so studying my screenshots was partly teaching me the practice answers rather than the underlying ideas. The rising numbers felt like progress. Some of it was memorization wearing the costume of mastery. The AI quizzing had the same flavor of comfort. Both Claude and the other model had me convinced I was ready. The real exam disagreed. ## Exam morning I woke up early on a Saturday, cleared my desk, and disconnected my extra monitors. A proctored exam was going to want to confirm I was not cheating, and I would rather remove the question than answer it. Signing in meant installing the proctoring software and granting it the camera and microphone. That took a couple of minutes. I read the first question and knew immediately that the practice exam and the real exam share no questions. Nothing looked familiar. One habit carried over well and badly at the same time. The practice runs had taught me to flag hard questions and return to them, so I did. Ten questions in, I had flagged about seven of them. The timer kept moving and the fear of failing climbed with the flag count. I took a breath and kept going. The questions come in four blocks of fifteen, each built on its own production scenario. The guide notes there are six possible scenarios and your exam draws four of them at random. My first block was the one that rattled me most. The middle two were fine. The last was manageable. I reached the final question with eighteen minutes left, went back to everything I had flagged, and found the second read far less intimidating than the first. I changed about half of those answers. The clock ran out as I was reviewing the last one. Failing carries a real cost. If you do not pass, you cannot retake it for six months, which is most of a lifetime at the speed this field moves. That stake was sitting on every flagged question. ## The hardest part was the waiting Unlike the practice exam, the real one shows you nothing when you finish. No score, no breakdown. One Anthropic email said results within seven days, another said seven to ten, and online I had read about people getting theirs in two days or in several weeks. The exam had been harder than I expected, and the gap between my practice scores and how the real thing felt had knocked out my confidence. I put my odds at roughly even. I tried to get myself ready to take a failure well. I checked my email and my Anthropic profile several times a day. At one point I dreamed that the results had come in and I had passed, which was a wonderful feeling until I woke up and realized I still did not know. My best guess for the delay is that the scoring is scaled, so Anthropic is likely rotating new questions through the exam and equating each score against how everyone performed on the items you happened to get. That is speculation. What I know is that the Friday night after the exam, the email arrived. I had passed with a 738. ## What the score report told me The detailed report breaks performance down by domain, and mine read like an honest map of where I was thin. I got 44 of 60 right, a 73 percent raw accuracy that the scaling lifted to 738. Agentic Architecture and Orchestration was my strongest at 78 percent. Tool Design and MCP Integration came in at 70. Context Management and Reliability sat at 73. My weakest domain, the one the report literally flagged with "focus here," was Claude Code Configuration and Workflows at 69 percent. That last number is the one I keep thinking about. Claude Code is the tool I use most, every working day. Daily use had given me fluency with the parts I reach for and blind spots around the parts I do not. The exam found the blind spots. That is the whole value of a test like this. It does not reward the work you already do well. ## What I would tell the next person Build something to study, do not only read. Writing my own questions and screenshotting my wrong answers taught me more than any video, and that is the same thing the flashcard app taught me in college. Just know the limit. If your practice tool reuses its questions, your rising scores are measuring recall of that pool as much as real understanding. Treat the AI tutors as sparring partners, not judges. Claude is good at drilling you on a topic and explaining what you missed. It is also encouraging by nature, and encouragement is not the same as readiness. Let the practice exam under real time pressure be your honesty check, not the model's verdict. Watch the clock from the first question, since under two minutes each is tighter than it sounds. Flag and move rather than grinding, but do not let a pile of flags rattle you early. And go in expecting the scenarios to be unfamiliar, because they will be. One last thing surprised me more than anything on the exam itself. The certification expires six months after you pass. Pass or fail, if you want the credential you are sitting this again before the year is out. At first that felt absurd. The longer I sat with it, the more it fit the subject. The tools this exam covers change on a quarterly rhythm, and a credential that claimed to certify production judgment for three years would be lying by month four. So I am back where I started, building study tools to keep up. The flashcards were paper-thin compared to a multi-agent CI scenario, but the instinct is the same one I have had since college. The certificate is the part that expires. The habit of learning the thing well enough to teach it back is what keeps you current. ### Flutter Generative UI: Claude vs Gemini in the Same Shopping Assistant Source: https://verygood.ventures/blog/flutter-genui-claude-vs-gemini/ The Flutter GenUI SDK was designed with model portability in mind. The `ContentGenerator` interface decouples your app from any specific LLM — swap the implementation and the widget catalog, rendering pipeline, and conversation state stay untouched. We wanted to see what that actually felt like in practice, so we took the shopping assistant from [Jorge Coca's GenUI tutorial](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/) and extended the Flutter AI integration to support Claude alongside Gemini. Same app, same catalog, same prompts. The only thing that changed was which model drove the conversation. ## What the `ContentGenerator` Abstraction Actually Buys You The original tutorial uses `FirebaseAiContentGenerator` backed by Gemini 2.5 Flash. To connect Claude, we used `genui_dartantic` — a `ContentGenerator` implementation built on `dartantic_ai` that supports Anthropic as a provider. The widget catalog stayed the same: three custom items (`ProductCard`, `ProductCarousel`, `PriceRangeFilter`) alongside GenUI's core layout components. Nothing in the rendering pipeline changed. One practical constraint worth knowing up front: Anthropic's API doesn't support direct browser-to-API calls. Browsers block the preflight because `api.anthropic.com` doesn't return the `Access-Control-Allow-Origin` header — this is intentional on Anthropic's side, since API keys embedded in a web app are trivially extractable from devtools. The Claude path runs on macOS desktop, where requests aren't subject to browser CORS policy. Gemini via Firebase AI Logic runs on web. In the app, `kIsWeb` picks the right generator automatically: ```dart _contentGenerator = kIsWeb ? buildFirebaseAiContentGenerator() : buildClaudeContentGenerator(); ``` For a production app you'd add a server-side proxy and lose that platform constraint entirely. For a demo, the split is clean. ## How the Demo Played Out We ran both backends through the same three-query session: "Show me running shoes," "I want something under $80," and "Show me lightweight backpacks between $50 and $120." After each add-to-cart action, the assistant surfaced accessory suggestions. Both backends understood the intent and correctly mapped it to the right tools. Price filtering worked as expected on both sides — "$0–$80" and "$50–$120" came through accurately. At that level, the architecture held up regardless of the model. The differences showed up everywhere else.
Claude product grid for running shoes
Claude
Gemini product grid for running shoes
Gemini
## Speed vs. Depth: The Real Tradeoff Gemini was faster across the board. On the first query it was noticeably quicker. The gap widened considerably on the price filter query, where Claude took significantly longer to respond. By the backpack query Claude had closed the gap somewhat, but Gemini's latency advantage was real and consistent throughout the session. That speed came with a tradeoff in response quality. Gemini's text responses were functional but flat. "Here are some great running shoes for you." "Here are some running shoes that fit your budget!" The messages were grammatically correct and topically accurate. They didn't add anything. The accessory suggestions that appeared after adding to cart also showed broken image placeholders on several cards. The system prompt explicitly instructs the model to omit `imageUrl` when it doesn't have a real value — Gemini fabricated URLs anyway, which resolved to nothing. ![Gemini accessory suggestions with broken image placeholders](/assets/images/blog/flutter-genui-claude-vs-gemini/body-2.png) *Gemini accessory suggestions after add-to-cart — broken image placeholders where product cards should be* Claude's responses used markdown formatting, called out specific products by name, and connected the suggestion to what the user had just done. After adding the Nike Air Zoom Pegasus to cart, the message read: "Complete your run — accessories to pair with your new Pegasus 40." The follow-on suggestions included running socks, insoles, and a handheld water bottle — all thematically coherent with the shoe that was just selected. The upsell felt like a recommendation, not a random list. ![Claude accessory suggestions with full product images and contextual copy](/assets/images/blog/flutter-genui-claude-vs-gemini/body-3.png) *Claude accessory suggestions after add-to-cart — contextual copy and fully rendered product cards* The product grid also rendered cleanly across all three queries on the Claude side. No broken images. ## What This Means for the Architecture The Flutter layer is genuinely model-agnostic. The widget rendering code never changed between platforms. The same `ProductCard`, `ProductCarousel`, and `PriceRangeFilter` widgets rendered regardless of which model was driving. That's the promise of this pattern — your UI primitives stay stable, and the model's output populates them. What isn't model-agnostic is the experience that results. Claude traded latency for richer, more contextual responses. Gemini prioritized speed but returned shallower, less personalized text and had some asset rendering inconsistencies. Neither outcome is wrong. They reflect different model priorities and, in a real product, would call for different design decisions. A shopping assistant where speed is the primary driver — think quick reorder flows or high-frequency browsing — might favor Gemini's latency profile. A shopping assistant where personalization and cross-sell performance matter more might justify the extra seconds Claude needs to produce a richer response. The Flutter architecture doesn't make that choice for you. It makes the choice possible. ## Swapping the `ContentGenerator` The two transport files are nearly symmetric. The Gemini side uses `FirebaseAiContentGenerator` from `genui_firebase_ai`; the Claude side uses `DartanticContentGenerator` from `genui_dartantic`, backed by `AnthropicProvider`: ```dart // claude_ai_transport.dart const _anthropicApiKey = String.fromEnvironment('ANTHROPIC_API_KEY'); DartanticContentGenerator buildClaudeContentGenerator() { return DartanticContentGenerator( provider: AnthropicProvider(apiKey: _anthropicApiKey), modelName: 'claude-sonnet-4-6', catalog: shoppingCatalog, systemInstruction: shoppingSystemInstructions, ); } ``` ```dart // firebase_ai_transport.dart FirebaseAiContentGenerator buildFirebaseAiContentGenerator() { return FirebaseAiContentGenerator( catalog: shoppingCatalog, systemInstruction: shoppingSystemInstructions + GenUiPromptFragments.basicChat, ); } ``` Both receive the same `shoppingCatalog` and `shoppingSystemInstructions`. The `GenUiConversation` in the page doesn't change at all — it receives whichever `ContentGenerator` `kIsWeb` selects, and the rest of the rendering code is identical on both platforms. ## What We'd Do Differently A few things stood out as areas to improve for a production version of this pattern. Streaming matters more than we expected. Both demos used non-streaming responses, which contributed to the perceived latency gaps. Streaming tool results back to the UI — showing cards as they resolve rather than all at once — would substantially improve the feel of both backends. Tool definitions need to be model-aware. We used identical tool schemas for both backends, but models interpret schemas differently. Claude responded more precisely to constrained price range parameters; Gemini occasionally returned items at the boundary of the requested range. Tuning the tool descriptions for each model's behavior would tighten this up. The broken images on the Gemini side are worth addressing in the system prompt, not just the widget layer. The current instruction tells the model to omit `imageUrl` when it doesn't have a real value — that worked with Claude, but Gemini hallucinated URLs regardless. Making the instruction more explicit, or validating URLs before passing them to the widget, would close the gap. A graceful fallback in the `ProductCard` widget is still good practice, but the root fix is prompt-level. ## The Takeaway The `ContentGenerator` abstraction in the GenUI SDK isn't a footnote. It's one of the more consequential design decisions in the library. It means you can ship with one model and migrate to another without touching your widget catalog, your rendering pipeline, or your state management. What changes with the model is the texture of the experience — how the assistant talks, how it connects what you just did to what it suggests next, how the content feels when it arrives. That's worth measuring before you pick a backend, and it's worth building the infrastructure to test before you commit to one. If you haven't built the shopping assistant yet, start with [Jorge's tutorial](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/). The full source for both the original Gemini implementation and the Claude `ContentGenerator` is available in the [genui_shopping_assistant repository on GitHub](https://github.com/VGVentures/genui_shopping_assistant). For a broader look at how VGV applies generative UI across retail, travel, banking, and QSR, see the [GenUI solutions resource page](https://verygood.ventures/resources/genui/). ### WWDC 2026, Through a Flutter Lens Source: https://verygood.ventures/blog/wwdc-2026-through-a-flutter-lens/ [WWDC 2026](https://www.apple.com/newsroom/2026/06/apple-unveils-next-generation-of-apple-intelligence-siri-ai-and-more/) took place this week, and Apple announced a lot. Most of it won't change how Flutter teams work day to day. In this article we focus on the announcements most likely to have an impact on Flutter, ordered by what we expect to come up most often in client work over the next six months. ## UISceneDelegate is now required on iOS 27 This is the biggest item for Flutter teams, and it lands as a confirmation rather than a surprise. Apple said at WWDC 2025 that any UIKit app built with the SDK after iOS 26 would need to adopt the [UIScene lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/scenes) or it would no longer launch, and the iOS 27 SDK makes that real. The requirement is SDK-driven, not OS-driven: an app built against an older SDK still launches on iOS 27. The Flutter team has been preparing for this since Apple's warning. [Flutter 3.38 shipped UIScene support](https://blog.flutter.dev/whats-new-in-flutter-3-38-3f7b258f7228) and flagged the upcoming requirement, and 3.41 made it the default with [build-time auto-migration](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate) for projects with an unmodified AppDelegate. Apps on a recent Flutter version are covered. The work shows up in custom AppDelegates, older Flutter versions, and any iOS plugin still on the old scene lifecycle. ## App Intents are the new front door to Siri The new Siri performs systemwide actions through [App Intents](https://developer.apple.com/documentation/appintents), so the assistant can only act on what an app exposes through that framework. SiriKit is now on a deprecation path, and Apple is directing new work to App Intents. There is no official App Intents integration in Flutter, neither a clear plan to create one yet ([flutter/flutter #170589](https://github.com/flutter/flutter/issues/170589)). Community packages on pub.dev offer early bindings, so we will see how they evolve. ## Foundation Models opens up to third-party providers Apple introduced a public `LanguageModel` protocol in the [Foundation Models framework](https://developer.apple.com/documentation/FoundationModels) that puts the on-device Apple model and cloud models behind one Swift API. [Anthropic and Google publish Swift packages](https://developer.apple.com/videos/play/wwdc2026/339/) for their models through it. The on-device model also gained [image input and a Python SDK](https://developer.apple.com/videos/play/wwdc2026/241/). For Flutter, this opens a platform surface for on-device summarization, classification, and rewriting without bundling a model, reached today through platform channels or FFI since there is no Dart binding yet. Flutter AI integration of this kind is moving from experiment to product, and [generative UI work](https://verygood.ventures/blog/getting-started-with-genui/) is a worked example. ## Liquid Glass keeps evolving Liquid Glass in iOS 27 adds a Settings slider that personalizes the effect from ultra-clear to fully tinted, answering readability feedback from last year. macOS 27 ("Golden Gate") carries the same control. The Flutter team is [reviewing how Cupertino should evolve](https://github.com/flutter/flutter/issues/170310), and community packages exist for teams that need the look today. We covered [what the design shift means for Flutter teams](https://verygood.ventures/blog/a-new-era-for-app-design-and-what-it-means-for-flutter/) when it first landed. For apps where iOS aesthetics matter to the brand, we weigh native platform views against the cost of an extra render path on a project-by-project basis. ## Xcode 27 and the new minimums The impact here lands mostly on CI. [Xcode 27](https://developer.apple.com/videos/play/wwdc2026/258/) is Apple Silicon only, so runners still building on Intel Macs need a plan before adopting the iOS 27 SDK. Xcode 27 also drives the [proposal to raise Flutter's iOS minimum version](https://github.com/flutter/flutter/issues/187741) from 13 to 15. Worth handling in the same upgrade window: Firebase stops publishing new versions to CocoaPods in [October 2026](https://firebase.google.com/docs/ios/cocoapods-deprecation), with the registry going read-only that December, so Swift Package Manager becomes the path forward. For enterprise teams juggling these SDK changes in their release cadence, [release pipelines should not take weeks](https://verygood.ventures/blog/shorebird-enterprise-flutter-releases-dont-have-to-take-weeks/). ## Accessibility quietly got better Apple continued expanding system accessibility in iOS 27, with improvements to VoiceOver and system dictation. Flutter apps inherit most of this automatically, and the benefits land hardest in apps with well-formed Semantics trees. Use the upgrade as a prompt for [auditing Flutter accessibility with Semantics](https://verygood.ventures/blog/exploring-accessibility-and-digital-inclusion-with-flutter/) on custom-painted widgets, charts, and dashboards. ## The takeaway For most Flutter teams, WWDC 2026 comes down to upgrade work. Confirm the UIScene migration completed cleanly, and plan for the iOS minimum bump. iOS 13 and 14 barely register in active device share anymore, so that one can move quickly. App Intents deserves a closer look on apps where Siri reach is part of the product. The bigger opportunity is on the AI side. Apple's on-device models now sit behind a single API alongside cloud providers, and the most interesting Flutter work will come from wiring them into existing user flows. That on-device, multi-model direction is the work [our AI-native Flutter engineering platform](https://verygood.ventures/resources/flutter-ai-engineering/) is built for. ### What Is GenUI? Source: https://verygood.ventures/blog/what-is-genui/ Most of the energy in AI has gone into making models smarter. The harder, less discussed problem is what happens at the user interface, where the model meets a person. A brilliant model that answers with a wall of text or raw JSON still leaves the user to do the work of understanding, deciding, and acting. GenUI is the layer that closes that gap. This guide explains what GenUI, short for Generative UI, actually is, how it works, how it differs from GenAI, and how teams build it in production. It pulls together what we have learned shipping generative interfaces across hospitality, banking, retail, and developer tooling. ## What is GenUI? GenUI, short for Generative User Interface, is the experience layer where AI builds the user interface in real time based on user intent and context. Rather than relying on fixed screens and predefined flows, a GenUI system assembles interface elements on demand, drawing on: - User intent - Context - AI output - Real-time data The result is an interface that is composed for the moment rather than pre-designed for everyone. Layouts reorder, components appear and disappear, and actions surface based on who the user is and what they are trying to do. A useful way to picture it is a familiar component rendered two ways. Take a room booking card in a hotel app. In a traditional app, that card looks identical for every user, with the same photo, the same amenities, and the same layout. With GenUI, the same underlying component adapts. For a business traveler it prioritizes the workspace photo, highlights the high-speed Wi-Fi badge, and surfaces a one-click expense receipt. For a family on vacation, the exact same component features the pool image, highlights "Kids Eat Free," and offers a connecting-room toggle. The building blocks are the same. How they are assembled changes. We unpack this and four other shifts in [What Is GenUI? How Generative User Interfaces Will Redefine Hospitality](https://verygood.ventures/blog/what-is-genui-how-generative-user-interfaces-will-redefine-hospitality-in-2026/). ## GenUI Powers Real Personalization *Most personalization isn't personal.* Before GenUI, what we called personalization was usually segmentation. We grouped users by shared traits, sorted them into buckets, and served everyone in a bucket a variation of the same experience. A single bucket might hold thousands of people, so the averaging flattened the context, intent, and nuance that set any individual apart. **GenUI changes the unit of design from the segment to the person.** The interface adapts at runtime to someone's context, intent, and the outcome they're after, while holding your brand's point of view. Each user works with a product shaped around them, not around the cohort they happen to fall into. ![Generative UI personalization: one hotel booking app adapting its interface in real time for a business traveler, a family, and an accessibility-first guest while keeping the same brand](/assets/images/blog/what-is-genui/personalization.gif) Look closely and you will see the same app three times, not three different apps. The booking card, the brand, the component library, and the navigation never change. What changes is the interface the AI assembles for the person in front of it. The business traveler gets the workspace and the expense receipt. The family gets the pool, the connecting room, and "Kids Eat Free." The guest who needs step-free access gets a simpler, higher-contrast layout with the right room surfaced first. That is the real potential of personalized GenUI. One product can read a user's data, context, and intent and compose the experience that fits them, in real time, while the brand stays exactly where you set it. Thousands of users stop sharing one frozen screen and each get an interface built for the moment. ## GenUI vs. GenAI: what's the difference? GenAI and GenUI are easy to confuse and frequently used interchangeably, but they sit at different layers of the AI product stack. Generative AI is the creative engine. It produces content, such as text, images, code, audio, and structured data, by learning patterns from large datasets and predicting new output. Chatbots, code assistants, image generators, and summarization tools are all GenAI. Generative UI is the experience layer that presents that intelligence to users. It turns model output into interactive cards, editable forms, comparison views, and context-aware controls assembled at runtime. The simplest mental model: - **GenAI decides what to say or create.** - **GenUI decides how users see it and act on it.** The two are most powerful together. Picture an AI travel planner. GenAI generates a personalized itinerary. GenUI renders it as a timeline, an interactive map, editable booking cards, and context-aware recommendations. Without GenUI, the user gets a long block of text. Without GenAI, the interface is static and manual. For a deeper breakdown of where each one lives and which teams should own it, see [GenAI vs. GenUI: What's the Difference and Why It Matters for Product Teams](https://verygood.ventures/blog/genai-vs-genui-whats-the-difference-why-it-matters-for-product-teams/). ## How GenUI actually works GenUI does not mean the model writes and ships UI code at runtime. That would be unsafe and impossible to govern. Instead, modern GenUI runs on a structured contract, often called an app-to-UI protocol. The model returns JSON that describes which approved components to render and how to configure them. Your app deserializes that JSON and renders real, native components. The AI-generated UI is therefore assembled from your building blocks, not invented from scratch. This is also where GenUI differs from server-driven UI (SDUI). With SDUI, a backend ships a fixed UI definition that the client renders, and a human decides what that definition contains. With GenUI, an AI model decides at runtime which components to assemble and how to configure them for the specific user. SDUI moves control to the server. GenUI moves part of the composition to the model, within the boundaries you set. Three concepts make this work, and they show up in every GenUI implementation we build: - **Catalog.** The vocabulary of components you expose to the AI. The model can only use what you register, so the catalog is a hard boundary, not a suggestion. - **Surface.** The region of the screen where AI-assembled content appears. A single screen can host one surface or several. - **Conversation.** The stateful interaction loop that tracks message history and feeds user actions back to the model. This feedback loop is what makes GenUI generative rather than merely dynamic. The mental shift is significant. The question stops being "what should this screen look like?" and becomes "what components should I give the AI to work with?" Our [Getting Started with GenUI](https://verygood.ventures/blog/getting-started-with-genui/) tutorial builds a working example from these three primitives, and the [Flutter GenUI home screen tutorial](https://verygood.ventures/blog/flutter-genui-home-screen-tutorial/) shows how streaming, shared state, and interaction callbacks fit together in a real app. ## Governing GenUI: where your design system ends and the LLM begins The biggest fear teams have about GenUI is loss of control. The answer is governance, and it is where most of the real engineering happens. The model should never pick a color, a font, or a piece of freeform markup. It picks from a curated menu and chooses semantic options you define. A helpful analogy: your design system is the kitchen where you decide every recipe, the catalog is the menu of dishes you are willing to serve, and the LLM is the waiter who takes the order and places it from that menu. The waiter never invents a dish. We expand on this in [Where Does Your Design System End and the LLM Begin?](https://verygood.ventures/blog/where-does-your-design-system-end-and-the-llm-begin/). You control freedom through the schema. A component might expose a `variant` property limited to `filled` or `outlined`, or a `deltaDirection` limited to `positive` or `negative`. The model picks the semantic meaning, and the component decides what color that maps to. Every property you expose is a degree of freedom, and calibrating those degrees is an ongoing design decision. The sizing of the components themselves matters just as much. Borrowing from atomic design, exposing only atoms gives the model too much freedom and produces disjointed layouts, while exposing full pages removes the flexibility that makes GenUI worthwhile. Molecules, self-contained components with a locked internal layout that the model chooses when to show, are usually the right default. We cover this calibration in [The Chemistry of GenUI](https://verygood.ventures/blog/the-chemistry-of-genui/). At VGV we wrap this discipline in a framework we call **Structured Creativity**. It decouples the presentation layer, which is generative and adaptive, from the brand rules, which are fixed and safe. The AI acts strictly as an orchestrator. It selects the right component and configures content to match the user's need, and it never violates brand guidelines. That is how you get the magic of adaptive interfaces with the safety the enterprise requires. ## GenUI in production: architecture and devices A common surprise is that GenUI does not require an exotic architecture. The four-layer pattern we use across our Flutter work still applies, with presentation, business logic, repository, and data layers. The repository hides GenUI complexity behind a small API, the SDK's internal events get mapped into your own domain events so a future SDK change cannot ripple through the app, and the catalog and the system prompt are treated as first-class, independently testable code. We walk through these decisions in [GenUI Meets the VGV Architecture](https://verygood.ventures/blog/genui-meets-the-vgv-architecture/). ![One brain, many screens — a single GenUI catalog adapting one booking interface across phone, tablet, desktop, and kiosk](/assets/images/blog/what-is-genui/body.jpeg) Responsive behavior is its own discipline. The pattern that works is a clear division of responsibility: the model decides which component, what content, and what relative size, and the component owns its internal layout across screen sizes. Trying to give the model device-specific components or swapping catalogs per breakpoint both break down in practice. Prompt rules and component constraints turn out to be two sides of the same coin, a lesson we learned building across phones, tablets, and desktops in [GenUI Across Devices](https://verygood.ventures/blog/genui-across-devices/). This is also where Flutter earns its place. One codebase and one rendering engine reach mobile, web, desktop, kiosks, and wearables. Layer a single approved catalog and one governance model on top, and you get adaptive experiences everywhere your customers are, governed once. We call this **one brain, many screens**. ## Where GenUI delivers the most value GenUI is not the right tool for every feature. It shines where personalization drives outcomes, where the user journey is predictable enough to design components for, and where trust and clarity matter. A few proven patterns: - **Guided wizards over open chat.** For high-stakes, step-by-step tasks, a generative wizard gives every step a clear next action while still adapting to the user, which is often better than a blank chat box. See [Beyond the Chat Box: How We Built a Generative UI Wizard for GCN](https://verygood.ventures/blog/generative-ui-flutter-wizard-gcn/). - **Booking and commerce flows.** GenUI replaces walls of text with interactive cards, sliders, and selectors that move users toward a decision. See [Redefining the Digital Booking Experience with Generative UI](https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/) and the [Flutter GenUI shopping assistant tutorial](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/). - **Closing the last mile in regulated industries.** Banks spend heavily on GenAI, yet much of that intelligence never reaches the interface. GenUI interprets intent like "save for my daughter's college fund" and assembles the right flow in real time. See [The Last Mile Gap: How Generative UI Drives New Account Openings in Retail Banking](https://verygood.ventures/blog/genui-last-mile-gap-banking-account-openings/). The strongest early fit is any industry that combines high-context personalization with a multi-screen customer journey. Hospitality, retail banking, retail and QSR ordering, and entertainment all qualify. ## Building GenUI: the tooling around it A production GenUI app is more than a model and a catalog. A few pieces tend to come up: - **Orchestration.** Tools like Genkit add named, observable flows, middleware for retries and logging, and validated tool calls, so AI work becomes testable infrastructure rather than ad-hoc API calls. See [Genkit for Flutter: Flows, Middleware, and Tools in a GenUI Shopping Assistant](https://verygood.ventures/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/). - **Server-side AI.** Moving data-only AI logic to a Dart backend keeps API keys off the client while leaving the UI layer untouched. See [Genkit for Flutter: Moving AI Logic to a Dart Backend](https://verygood.ventures/blog/genkit-for-flutter-moving-ai-logic-to-a-dart-backend/). - **Rich motion.** Pairing GenUI with Rive lets designers drive loading and transition animations through state machines without touching widget code. See [Rive Animations + GenUI: Flutter Integration Guide](https://verygood.ventures/blog/rive-flutter-genui-integration/). ## The bottom line GenUI represents a shift from screens to conversations, from flows to intent, and from interfaces to experiences. In the same way mobile-first design reshaped software in the 2010s, AI-native design powered by GenUI will define the next era. The teams that win will not just adopt AI. They will design the layer where AI meets the user, with the catalog as the contract and governance as the guardrail. Very Good Ventures helps brands build that layer, combining GenUI strategy and product design with multi-platform Flutter delivery built for scale and compliance. If you are ready to explore what GenUI can unlock in your product, [contact our team](https://verygood.ventures/contact-us/). ### VGV at FlutterCon USA 2026: Speakers and Flutter@Scale Sessions Source: https://verygood.ventures/blog/vgv-at-fluttercon-usa-2026-speakers-and-sessions/ FlutterCon USA has become the place where production Flutter teams compare notes. This year, [Very Good Ventures](https://verygood.ventures/) brings 13 sessions and more than 20 speakers to Orlando on July 16-17, the largest lineup of any company on the schedule. The sessions span Flutter at enterprise scale across mobile, web, embedded hardware, and AI-augmented workflows, and several put VGV engineers on stage with the teams they partnered with at Etsy, Toyota, Universal Studios, and Divine. Most of these talks sit in the [Flutter@Scale track](https://verygood.ventures/blog/fluttercon-usa-2026-flutter-at-scale-enterprise-track/), the enterprise content track VGV curates for engineering leaders running [Flutter for enterprise](https://verygood.ventures/blog/flutter-for-enterprises-everything-ctos-and-engineering-leaders-need-to-know/) in production. ## Sessions at FlutterCon USA 2026 ### Enterprise scale The work behind these sessions comes from years of [building and scaling Flutter for companies like Toyota, Universal Studios, and Etsy](https://verygood.ventures/blog/top-companies-using-flutter/). Each talk pairs a real production system with the leadership decisions that got it there. - **Flutter at Scale: A Roadmap for the Modern Enterprise**, by David DeRemer and Abdallah Shaban ([Google/Flutter](https://flutter.dev/)) - **The Flutter Enterprise Journey: From Native Apps to AI-Enabled Multi-Platform**, by Mark Fairless and Tom Quercia ([Universal Studios](https://www.udx.com/)) - **Design Systems at Toyota Scale**, by Mykyta Kovalenko and Christopher Casey ([Toyota](https://www.toyota.com/)) - **Multi-Platform Strategic Advantage @ Scale: Flutter at UDX and Sky**, by David DeRemer and Jonathan Courtois ([Sky](https://www.sky.com/)) ### AI-powered Flutter development Three sessions cover AI-powered Flutter development as production practice, from integrated development stacks to generative UI. These are real implementation stories, not roadmap predictions. - **Building an Integrated AI-Powered Development Stack**, by Rui Alonso, Jorge Coca, and Dominik Šimoník - **Designer + Engineer + Claude: Building a Cross-Functional AI Workflow from Figma to Flutter**, by Rémy Baudet and Camila Mazer - **When the ADK draws your UI: rethinking GenUI in Flutter**, by Laura Buffington The GenUI session covers how the [Agent Development Kit](https://verygood.ventures/blog/getting-started-with-genui/) builds interface elements at runtime rather than at design time. The Figma-to-Flutter session walks through a [Claude-powered workflow](https://verygood.ventures/blog/flutter-ai-development-wingspan-workflow/) that moves a design from Figma into production Flutter. ### Platform, performance, and embedded These sessions cover Flutter running past mobile, into the web, embedded hardware, and high-performance video. - **Bridging Ecosystems: Embedding Multiple Flutter Components into any Web App**, by Hudson Proenca and Karlo Verde - **Building a Production Viral Video Feed in Flutter**, by Hugo Walbecq and Dominik Šimoník - **From Lottie to Flutter: How Etsy Rebuilt Year in Review with Flutter Native Animations**, by Jorge Coca and Filip Maj ([Etsy](https://www.etsy.com/)) - **Auth flows in Flutter WebViews without losing your mind (or your users' sessions)**, by Taaha Rauf - **Flutter Beyond Mobile: Lessons from Bringing Flutter to Embedded Hardware**, by Aaron Crutchfield and Kyle Crouse ([NuStep](https://www.nustep.com)) The NuStep session continues the work VGV documented when the team built the [first Flutter boombox](https://verygood.ventures/blog/flutter-beyond-mobile-vgv-builds-the-first-flutter-boombox/), running Flutter on embedded hardware. ### Product velocity - **From Vibe-Coded Viral MVP to Launchable Product in 90 Days**, by Óscar Martín and Liz Sweigart ([Divine](https://divine.video/)) This session draws on VGV's partnership with Divine, where the team helped take a [Vine reboot from concept to launch](https://verygood.ventures/blog/divine-launches-vine-reboot-built-with-vgv/). ## Find us in Orlando The common theme across these sessions is Flutter in production at scale, systems running for thousands of users. If you are attending FlutterCon USA on July 16-17, find the VGV team at the Flutter@Scale Pavilion. We will be around both days, on stage and in the hallways, to talk through the work. The full schedule is at [flutterconusa.dev](https://flutterconusa.dev/). ### From draft to deck: a two-tool workflow with Claude and Claude Design Source: https://verygood.ventures/blog/building-presentations-with-claude-design/ Anthropic released [Claude Design](https://www.anthropic.com/news/claude-design-anthropic-labs) in April 2026 as a research preview. In their own words: > Claude Design is a new Anthropic Labs product that lets you collaborate with Claude to create polished visual work like designs, prototypes, slides, one-pagers, and more. Since it landed, I've been using it to build presentations. The first time I built a deck, I went straight into Claude Design and got good results. One thing I noticed, though, was that working on the content in Claude Design felt slow. Every time I adjusted the text, Claude Design had to do more than just edit words: the text has to fit the slide and get rendered, so each small change took longer than it would in plain text. So the next time I tried something different: I wrote the content first as markdown in a regular Claude chat, where iterating on text is fast, and only then took that markdown into Claude Design. ## What Claude Design is Claude Design is a separate Anthropic Labs product, available to Pro, Max, Team, and Enterprise subscribers. When it launched in April 2026, it was powered by Claude Opus 4.7. It now has a model selector, so you can pick which model to use, including [Opus 4.8](https://www.anthropic.com/news/claude-opus-4-8). It has two panes: a chat on the left and a live canvas on the right. You describe what you want, Claude builds a first version, and you refine from there through conversation, inline comments, direct edits, or sliders that Claude generates when you want to dial something in. You can start from multiple sources: a simple prompt, other files like PDFs, markdown, or images, or elements grabbed from a live site with the web capture tool. You can start a brand-free design or use a custom design system that you or your organization defined. Once you're happy with the results, you can [export](https://www.anthropic.com/news/claude-design-anthropic-labs) to Canva, PDF, PPTX, standalone HTML, or an internal share URL. ## The workflow ![Two-step workflow for building presentations with Claude Design: draft the content first, then design the slide deck](/assets/images/blog/building-presentations-with-claude-design/workflow-claude-to-claude-design.png) I've been experimenting with different workflows to figure out which one gives the best results. Here's the one that works best for me. First I write a draft myself: what are the topics I want to present, and how do I want to organize the content? Then I bring that into a regular Claude chat and we iterate on it slide by slide, adding examples, sharpening each point, until the content feels kind of ready. The draft starts from me, and Claude helps me work it into shape. Once the structure feels solid, I ask Claude to turn it into a clean markdown outline: one section per slide, with the content underneath. I read it end to end before moving on. If a slide isn't pulling its weight I cut it, and if a section is out of order I fix it in the markdown first. Editing text is faster than editing slides. At this stage I don't focus much on layout. I want to focus on pure content, since for me visualizing the design in markdown is much harder, and that's what Claude Design is for. Once the content feels ready, I take that markdown into Claude Design. Here I try to use each tool for a different job. The content is already decided, so in Design I'm not rewriting the message, I'm focused on how it looks: the layout, the structure, whether something should be one slide or split into two. > 💡 **Tip: give Claude Design a standing rules doc.** > > There's no official CLAUDE.md feature in Claude Design, but you can get most of the way there. Add a rules file to your project as context and Claude treats it as a standing instruction for that project. It isn't documented and it isn't reloaded as strictly as a CLAUDE.md in Claude Code. Even with that caveat, it's been one of the most useful things for working more efficiently with Claude Design. > > My first rule: the content is already defined, so Claude Design shouldn't change the copy on its own. If it wants to adjust wording, it asks me first. That keeps it doing what it does best, layout and design, and leaves the content to me. The handoff prompt itself is short. Something close to: "Build a presentation deck from this outline, one slide per section, using the existing design system." Claude Design generates the first pass based on that. From there I refine from both sides. In Design I move slide by slide, using direct edits for small tweaks and inline comments for bigger ones. When something needs to change across the whole deck, the chat handles it in one round: tighten the body copy, move every chart to the right, use the secondary brand color for accents. And if the content itself needs to change, I go back to Claude chat rather than rewriting it in Design. When the deck is ready, I export. PPTX if it's going to someone who needs editable slides, PDF if it's read-only, or the internal share URL for a quick review from a teammate first. ## A few caveats Claude Design is still in research preview, so the exports aren't all equally clean. Exporting to PDF, for example, sometimes messes up images or elements like cards, with the styling coming out wrong. PPTX usually opens fine in PowerPoint and Keynote, but complex layouts can drift on the way out. A final pass after exporting is needed. Editing small style details can be painful. Something as simple as making a few words bold takes longer than it should. You can edit directly in the slide, but it's far more complicated than selecting the text and clicking a button in a traditional editor, so I often end up describing the change to the chat instead. The UX could be improved here. The design system inheritance works best when your organization actually has one configured in Claude Design. Without it, you'll spend more time in Design dialing in colors and fonts by hand. ## Takeaways The main thing I took from this is to think first and design second. Claude Design gives great results, but they're only as good as the content you bring it. So I get the content right first, and let Design do what it's good at. ### Designing AI to Fail: What a Capture the Flag Game Taught Us About Where LLMs Still Break Source: https://verygood.ventures/blog/andy-gibel-yum-brands-designing-ai-to-fail/ {% video url="https://www.youtube.com/watch?v=Y75r-vElfSo" title="Andy Gibel, Yum! Brands — When Speed Is Cheap, Taste Is Everything" %} [Andy Gibel](https://www.linkedin.com/in/andy-gibel-98487321/) had four hours to fill and a room full of engineering leaders who would do anything to beat him. His worst nightmare was simple. Someone cracks the whole thing in ten minutes, and he looks foolish in front of his peers. So he over-engineered it on purpose. Andy, an engineering leader at Yum! Brands, the company behind KFC, Taco Bell, and Pizza Hut, built an AI-focused Capture the Flag game to push his team past surface-level AI use. He has now run it three times. We sat down with him on Very Good Engineering to walk through how he designed challenges hard enough to stop a room of adversarial engineers armed with agent teams. The lessons reach well beyond a single offsite. ## The adoption curve most teams are stuck on Capture the Flag comes from the security world. The idea is a hidden key, or flag, that you have to find, usually by breaking into a system. Andy's CTO had the idea to borrow the format for AI. Instead of breaking in, participants solve coding challenges with one rule. Use AI, by any means necessary. Exploit the APIs. Cheat if you can. The timing mattered. Andy described an AI adoption curve of roughly seven phases, running from not understanding AI at all to orchestrating multi-agent teams with a custom harness. Most people sit around phase three or four. Their knowledge is frozen at the point where you tell ChatGPT or Claude to do something, read the text it gives back, and act on it yourself. A large group of capable engineers has not yet internalized how far past that point the tooling has moved. That gap was the target. The biggest aha moment Andy saw was people realizing they can hand an agent real work, walk away, get a cup of coffee, and come back to a finished product. This is the same plateau we see across teams adopting AI. People find the sweet spot where prompting produces something useful, get comfortable, and stop pushing. The next level is orchestration, parallelization, and wiring multiple inputs and outputs into a system. That is where the largest gains live, and it is exactly where most people stop. ## Building anti-AI on purpose Worth sitting with: the person who learned the most building this game was Andy himself. The first design problem was time. Modern agents parallelize to no end, so a flat list of challenges would collapse in minutes. Andy used a tiered structure instead. Seven tiers, unlocked progressively, with players needing to beat at least two of three challenges to advance. He paired it with pressure. A live leaderboard, a looming countdown clock, success chimes firing off around the room, everyone co-located because the camaraderie does not survive going fully remote. The result felt like an escape room, which is no accident. Underneath the theater, the engineering got serious. An autonomous red team ran the entire build, a group of agents actively trying to break his code, inflate points, or unlock challenges without solving them. It found a steady stream of holes to patch and pushed him toward rate limiting and authentication that could not be easily gamed. ## Where large language models actually break The deeper challenges came from a clear-eyed read of what these models are. At their core they are token prediction engines, pre-trained on roughly the entire internet. That corpus follows a power law distribution. A huge peak of common problem types, and a long tail of everything else. Anything in the peak is a non-starter. LeetCode-style problems and textbook material get cracked in seconds. So Andy went to the long tail and aimed at well-documented weaknesses. One is the model's eagerness to please. Post-training rewards confident answers, and that drive produces hallucinations and confident wrongness. Andy used problems esoteric enough to live in the long tail, but dressed up to look like a common problem type, so the model would pattern-match to the wrong approach and commit to it. Jorge named the production version of this risk. A confidently wrong answer early in a plan becomes a faulty foundation, and a team that is not accountable can ship it straight to production. The most human weakness is deception. Computers are bad at it. So Andy built a murder mystery where you play a detective using an agent to question other agents, some prompted to always lie, some to always tell the truth. It drew on a deception benchmark built around the werewolf game, where the finding is blunt. AI is substantially better at lying than at detecting lies. He solved the prompt-injection problem elegantly with a Sentinel token. Agents emit a hard-coded string instead of an answer, and the server validates it against game state before revealing anything. You get a deterministic blockade around the model's output while keeping the non-determinism that makes the agents feel human. The challenge that stopped people cold was ROYGBIV. Andy hid the answer in a Magic Eye stereogram, which exploits the depth-based vision humans take for granted and AI does not have natively. The model sees the noise, reaches for statistical analysis because that is what lives in its training data, and spins in the wrong direction. Andy watched agents grind for upwards of three hours until a human said "this is a Magic Eye, go figure it out," and the answer came in five minutes. ## The strategic takeaway hiding inside the game The defenses Andy built point at where production AI is heading. Building a basic agentic experience is easy. Building one that is genuinely secure, hard to inject, and still useful is one of the most advanced things you can do right now. The answer is not a single trick. It is the marriage of deterministic systems and gatekeepers sitting in front of non-deterministic models. The same shift changes what engineers need to be good at. When production gets faster and cheaper, the bottleneck moves to review. The fix is taste. Being good at reading code you did not write, knowing what correct looks like, and reasoning about a system at the architectural level. Design patterns matter more in this world, not less, because you cannot offload understanding. As Andrej Karpathy put it, you can outsource your thinking, but you cannot outsource your understanding. That is the pattern we keep seeing on healthy teams. The time AI buys back is going into more conversation about architecture, not less. If your team has settled into the comfortable plateau of request-and-response prompting, the move is to make the discomfort productive. Put people in a room, give them a problem the easy approach cannot solve, and let them discover the next phase by hitting its edges. ### Claude Code Dynamic Workflows for Flutter at Scale Source: https://verygood.ventures/blog/claude-dynamic-workflows-flutter/ The `/build` phase in our agentic workflow solution, [VGV Wingspan](https://github.com/VeryGoodOpenSource/vgv-wingspan), ends by running a panel of review agents in parallel, each checking the work against a different standard before a PR opens. We wired that panel by hand. Claude Code can now write that kind of orchestration script on its own, which means every phase of a session can fan out the way the review phase already does. That is the shift this post is about, and it is a set of Claude Code best practices we have been running against real Flutter monorepos rather than a forecast. ## What Changed in Claude Code: Dynamic Workflow Orchestration The old model is one agent that plans and executes inside a single context window. That works for most coding. It breaks down on work that is wide, long-running, and adversarial, the kind where one mind has to hold too much at once and ends up cutting corners. The new model is different. Claude can now write a JavaScript orchestration script on the fly, spawns parallel subagents, each with its own clean context, and verifies the results before they reach you. The orchestrator can route each subagent to a different model and run it in its own git worktree, so the work is isolated end to end. For Flutter teams that is the detail that matters, because a monorepo change touches scattered files that should not step on each other. We will not re-explain the whole mechanic here. You can read [Anthropic's announcement of dynamic workflows in Claude Code](https://claude.com/blog/introducing-dynamic-workflows-in-claude-code) for the invocation syntax and the research-preview details. The capability is the native primitive for the fan-out orchestration we already hand-build in [VGV Wingspan](https://verygood.ventures/blog/vgv-wingspan-agentic-engineering-workflow/), our open-source agentic engineering workflow for Claude Code, and that is the connection worth drawing out. ## Three Failure Modes That Break Flutter Agentic Sessions A single agent on a large Flutter codebase tends to fail in three ways. Anthropic names them, and they map cleanly onto the work we see. **Agentic laziness is the first.** The agent stops after 35 of 50 files and calls it done. That is the exact failure mode on a monorepo-wide change, where the last fifteen files are the ones nobody wants to touch. **Self-preferential bias is the second.** The agent trusts its own output when asked to verify it, which is the central risk when a tool reviews the code it just wrote. The review reads clean because the same context produced both the work and the verdict. **Goal drift is the third.** A long session loses the original constraints after the context compacts. An edge-case rule that mattered at the start of the session gets quietly abandoned halfway through, and nobody notices until a bug report arrives. ![Three failure modes of single-agent Flutter sessions: agentic laziness, self-preferential bias, and goal drift](/assets/images/blog/claude-dynamic-workflows-for-flutter-at-scale/body2.jpeg) Dynamic workflows counter all three structurally. Each unit gets an isolated context, and an independent verifier checks it. The decision rule that falls out of this is simple. Wide beats deep. Reach for a workflow when a task is wide, parallel, and needs per-unit judgment. The workflow patterns underneath are documented in [the workflow patterns Anthropic describes](https://claude.com/blog/a-harness-for-every-task-dynamic-workflows-in-claude-code), and the rest of this post maps them onto real Flutter work. ## Where Dynamic Workflows Pay Off on a Flutter Codebase Three kinds of work on a large Flutter codebase reward this approach the most. Each one teaches a pattern, not just an example. ### Breaking-Change Migrations Across a Flutter Codebase This is the home turf for fan-out-and-synthesize plus adversarial verification in worktrees. A `go_router` or `flutter_bloc` major update, a Material 2 to Material 3 move, an SDK bump, a sweep from manual JSON to `freezed`. The unit of fan-out is the call site, the failing test, or the package. The orchestrator spawns one subagent per fix in its own worktree, runs an adversarial reviewer against each fix, then synthesizes and merges. A CI gate runs after the merge, the same one a human PR would hit. ![Dynamic workflow fanning a Flutter monorepo migration across packages in worktrees with an adversarial reviewer per fix](/assets/images/blog/claude-dynamic-workflows-for-flutter-at-scale/body3.jpeg) The shape looks like this. ```javascript // Pseudo-workflow: Material 2 to 3 migration across a Flutter codebase for (const package of affectedPackages) { spawnAgent({ worktree: package.name, task: "migrate_material_2_to_3", codebase: package.source, verifier: adversarialReviewer({ rubric: material3_compat }), }); } await synthesizeAndMerge(results); ``` The proof that this scales past toy examples is public. [Jarred Sumner's thread on the Bun Rust port](https://x.com/jarredsumner/status/2060050578026189172) documents a large rewrite that used dynamic workflows for the fan-out. That is an external proof point at a scale most Flutter monorepos never reach, which is the point. If the pattern holds at that scale, a package migration is well within range. ### Correctness and Leak Sweeps This is fan-out plus loop-until-done plus analyzer verification. Undisposed `AnimationController`, `TextEditingController`, `FocusNode`, and `StreamSubscription` instances. Unclosed `Bloc` and `Cubit` instances. `BuildContext` used across an async gap without a `mounted` check. Missing `const`. These are scattered, individually small, and exactly the work a single agent gets lazy about. A workflow fans the sweep across the widget tree, loops each fix until the analyzer is clean, and verifies against the analyzer rather than against its own judgment. ### Test and Golden Coverage with a Skeptic in the Loop This is adversarial verification, and it targets the pump-and-pass risk. A widget test can pump a frame and pass without asserting anything real. One agent writes the test. A second agent checks it against a rubric for real behavioral assertions, loading and error states, and whether the golden actually moves when the widget changes. The second agent never sees the first agent as trustworthy, which is the entire point. This is Flutter testing best practices enforced by an independent reader instead of by hope. ## Deepening Every Wingspan Phase with Dynamic Workflows ![Wingspan phases brainstorm, plan, build, and review each fanning out with dynamic workflows for Flutter](/assets/images/blog/claude-dynamic-workflows-for-flutter-at-scale/body4.jpeg) This is where it gets interesting for the way we work. VGV Wingspan structures a session into four phases: `/brainstorm`, `/plan`, `/build`, and `/review`. The build phase already runs [a panel of review agents in parallel after the build phase](https://verygood.ventures/blog/flutter-ai-development-wingspan-workflow/), which we documented while shipping a stretch of apps on the workflow. Dynamic workflows are the native primitive for that fan-out, and the same primitive is available to the other three phases today. You invoke a workflow inside the phase. Wingspan does not generate it for you yet, but nothing stops you from taking any phase wider right now. ### Brainstorm: Generate, Filter, and Run a Tournament Today a brainstorm explores the problem space and proposes approaches with tradeoffs from one context. With a workflow, independent agents each commit to a different approach from a clean context, probe the codebase, the design, and the data layer separately for gaps, and then a judge ranks them against a rubric and synthesizes. The depth comes from independence. One context weighing three options is weaker than three contexts each defending one, because the single context shares its blind spots across all three options while the independent agents do not. ```javascript // Pseudo-workflow: brainstorm tournament, one approach per isolated context const approaches = await Promise.all([ spawnAgent({ context: "fresh", commitTo: "approach_a" }), spawnAgent({ context: "fresh", commitTo: "approach_b" }), spawnAgent({ context: "fresh", commitTo: "approach_c" }), ]); const ranked = await judge(approaches, { rubric: tradeoffs }); ``` Independent agents widen the net. An approach that one context would never have surfaced gets a defender, and the judge has to reckon with it. ### Plan: Fan Out Research, Then Verify Against the Code The plan phase researches the existing codebase, identifies patterns, and flags gaps. A workflow fans that research out, one agent per module or package with its own context, synthesizes the findings into a file-level plan, then runs a verifier that checks the plan against the code before a line is written. The oversized-plan problem is a context problem, and fan-out research is the structural fix. A single context that has read everything tends to plan for everything. A set of focused contexts plans for what is actually there. ### Build: Fan Out Per Unit in Worktrees For a large change, the build phase spawns per-file or per-unit subagents in worktrees, the migration pattern from the section above. The number and shape of the agents adapts to the change instead of being fixed in advance. A two-file fix gets two agents. A package migration gets one per package. ### Review: One Verifier per Standard The parallel review panel generalizes. One verifier per standard, coverage, accessibility, performance, and the VGV rules, each in its own isolated context. This is AI code review done so that the reviewer is not the author. Self-preferential bias is the named risk when a tool reviews its own work, and isolated-context verification is the answer. The accessibility verifier is a concrete example. Our [accessibility skill that maps findings to WCAG 2.2 criteria](https://verygood.ventures/blog/accessibility-wcag-compliance-skill/) is exactly the per-standard reviewer this phase wants. The discipline carries the same caveat we wrote up in [what changes about code review when AI writes your code](https://verygood.ventures/blog/code-review-ai-generated-code/): a panel that approves itself is not enough, and human review still targets judgment over checklists. The payoff is the compounding. Wingspan's design thesis is that each phase makes the next one better, a behavior we [documented across a run of apps on the workflow](https://verygood.ventures/blog/flutter-ai-development-wingspan-workflow/). A sharper brainstorm yields a sharper plan, which yields a build that already knows which files to touch. Dynamic workflows deepen each phase, so that compounding gets stronger end to end. ## When Not to Reach for a Fleet Token cost is real, and this is a research preview. Start scoped. A small feature build does not need twenty agents. Most day-to-day tickets do not need a panel of five reviewers, let alone fifty. As a rough calibration, a fifty-file migration might run on the order of tens of agents, not hundreds. Match the fleet to the surface area of the change. Determinism matters too. For a pure mechanical rename, `dart fix` or a custom lint plus codemod is cheaper and more auditable. Use a workflow when each unit needs judgment, not when a regex, or a static workflow would do. There is a trust boundary. For triaging user-reported bugs or other external input, such as a bug description pasted from a customer ticket or a third-party API response, use the quarantine pattern. Agents that read untrusted content do not get high-privilege actions. And the review discipline does not change. A merged PR from a subagent swarm still needs a human owner. For teams with the right constraints, the patterns pay off, and the way we think about Wingspan changes with them. ## How VGV Thinks About Agentic Engineering VGV Wingspan was built on hand-wired orchestration. Every harness had to be written in advance, generic enough to cover the cases we expected. The shift is that Claude can now generate a harness tailor-made for the task in front of it. The skills layer encodes which pattern each task deserves. A phase generates a workflow when the work is wide and high-value, and stays lean when it is not. We have done this concretely already. Our [Figma-to-Flutter skill with golden tests and Widgetbook validation](https://verygood.ventures/blog/figma-to-flutter-claude-code-skill-golden-tests/) packages domain-specific judgment into something repeatable, which is the same move at a smaller scale. ### What Changes About Code Review When AI Writes Your Code Source: https://verygood.ventures/blog/code-review-ai-generated-code/ Not long after adopting AI-assisted development at VGV, I approved a PR with thousands of additions. I did look at the code. But at that scale, the review shifted — I was not checking the fundamentals anymore. I was scanning for something that looked obviously wrong. That is a different activity, and it misses a lot. Not because you are not paying attention, but because the volume forces you into a different mode entirely. That PR is not a failure of discipline. It is what happens when generation speed and scope discipline are not in sync. An AI-assisted build session can produce in a few hours what used to take weeks. Without new habits around how that output gets structured into reviewable units, PRs reach sizes that turn code review into a formality. And a PR that size is not just slow to review — it is impossible to review properly. Human reviewers can only absorb a limited amount of unfamiliar code at a time. Reviewing a diff that size properly would take days of focused effort. Nobody has days. What actually happens: you scan the file list, check the areas you know best, verify the automated signals are green, and approve. Every architectural decision embedded in those lines goes unchallenged. The downstream costs are real. Decisions nobody questioned compound over months. Bugs a careful reviewer would have caught ship. The knowledge transfer that happens when a reviewer asks "why did you approach it this way?" never occurs. And when something breaks, tracing it back through a 17k-line merge is its own archaeology project. Big PRs do not just slow down review. They degrade the value of the review process itself — turning a quality gate into a checkbox. We still believe small PRs are the right default. Not because of a rule — because a PR one person can hold in their head is a PR that gets a real review. AI generation does not change that belief. It just makes it harder to act on. When [Wingspan's](https://github.com/VeryGoodOpenSource/vgv-wingspan) `/build` command finishes, it has already run five review agents in parallel: architecture, VGV standards, test quality, code simplicity, and PR readiness. The [VGV AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin) has been running `dart analyze` and `dart format` after every file edit throughout the build. Minor issues get auto-fixed. By the time a PR opens, a significant portion of what used to be human review work has already happened. So the question is not "should I review AI-generated code?" The question is: given how much the toolchain already checks, what does human review actually do now? ## What the Toolchain Handles Understanding what is already covered is the starting point. Spending reviewer time on things the toolchain catches is the first inefficiency to eliminate. **The VGV AI Flutter Plugin** operates on two levels. Its skills — `/vgv-bloc`, `/vgv-testing`, `/vgv-layered-architecture`, `/vgv-accessibility`, and others — guide Claude during generation, baking VGV conventions directly into the code being written: sealed classes for BLoC events, `mocktail` for mocks, `blocTest` for unit tests, four-layer architecture with unidirectional dependencies. The code does not need to be corrected for these patterns after the fact because the plugin taught Claude to produce them correctly in the first place. Its PostToolUse hooks then run `dart analyze` and `dart format` after every single file edit, so lint violations and formatting issues are resolved inline, not in review. **Wingspan's build phase** adds a review layer on top of that. After all code is generated, it runs five agents in parallel before the PR opens: - **VGV standards agent** — naming conventions, state immutability, layer separation - **Architecture review agent** — dependency direction, layer boundary violations - **Test quality review agent** — coverage, naming, anti-patterns like `pumpAndSettle` on infinite animations - **Code simplicity agent** — YAGNI violations, premature abstractions, unnecessary complexity - **PR readiness agent** — merge readiness, missing acceptance criteria, coverage gaps Minor issues are auto-fixed. Important ones are surfaced and resolved before the PR is created. The consolidated output looks something like this: ``` Build Review Summary Architecture Review Agent ✅ Layer boundaries respected. Data → Repository → BLoC → Presentation with unidirectional dependencies throughout. VGV Standards Agent ⚠️ UserProfileState: copyWith missing for errorMessage field. Auto-fixed. Test Quality Agent ✅ All BLoC events covered by blocTest. ✅ No pumpAndSettle calls on infinite animations. Code Simplicity Agent ⚠️ UserProfileRepository: fetchUser and fetchUserById share identical implementations. Consolidated to one method. Auto-fixed. PR Readiness Agent ✅ All acceptance criteria from the plan are implemented. ✅ No scope creep beyond the plan specification. 2 issues auto-fixed. PR is ready to open. ``` By the time a reviewer opens the PR, this pipeline has already run. This does not mean human review is optional. It means the review conversation can move up the stack. ## AI Removes the Effort Signal Before AI tools, PR size was a proxy for effort. A reasonably scoped PR meant someone spent real time on it, internalized every trade-off, and understood every decision they made. Reviewers calibrated their depth accordingly — a multi-day PR got serious scrutiny, a quick fix got a lighter pass. AI breaks this signal entirely. A feature that would have taken days to write manually — repository, multiple BLoCs, several screens, a full test suite — comes out of Wingspan's build in a fraction of that time. Nobody spent days thinking through every decision. The developer reviewed the output in their editor, accepted what looked right, and opened the PR. A reviewer who treats that PR with the same depth as a multi-day effort is doing the right thing. The instinct is to treat the short turnaround as a signal of simplicity. It is not. The automated agents caught what they can catch. What they cannot catch is whether the decisions embedded in all that generated code reflect what the team actually agreed to do. This pressure does not go away regardless of how many agents run. Human review is often the first scrutiny the decisions in a PR receive from someone who did not generate it. ## Review the Plan, Not Just the Code The most underused quality gate in an AI-assisted workflow is the plan. Wingspan saves implementation plans to `docs/plan/` before a single line of code is generated. The `/plan-technical-review` skill validates a plan for scope, technical soundness, and whether it will produce a reviewable PR — before `/build` runs. This is where scope discipline belongs. If a plan says "add UserProfile feature and refactor the Repository pattern," that is two plans. Splitting it before `/build` runs takes minutes. Untangling it at PR review — where the code is already written, the tests pass, and rejecting the PR means blocking the feature — takes hours and usually ends in a soft approval anyway. The difference between those two outcomes is one early conversation and one compromised quality gate. The rule: one PR, one axis of change. A new feature is one axis. Refactoring an existing pattern is one axis. Mixing them is two plans. When you see a Wingspan plan that covers both, split it before `/build` runs. Reading `docs/plan/` before reviewing the PR also tells you what the build was supposed to do. If you were not involved in the planning conversation, treat the plan as the first thing to review, not a given. Do the acceptance criteria make sense for the feature? Is the scope right? Checking that the implementation matches the plan is only meaningful if the plan itself holds up. ## What Human Review Still Owns With five automated agents and a hooks-enforced style pipeline, two things remain that no tool can handle. **Verifying the build matched the plan.** The automated agents check whether the code is good. Only a human can check whether the code does what the plan said it should. Compare the PR diff against the plan's acceptance criteria. Did the build cover every task? Did scope creep in — a model change that quietly refactored how that model is used in three other places? Changes outside the plan's scope should either go into their own PR or be explicitly justified in the PR description. **Team decisions that no agent can encode.** The five review agents enforce patterns that can be expressed in code: architecture boundaries, test coverage, VGV standards. They cannot enforce decisions that exist only in your team's memory. These are what regress silently when AI generates code that is correct but misaligned. Three categories where this shows up consistently: ### Code behavior preferences How your code signals failure, handles errors, or manages control flow. Agents verify that error handling exists — not that it matches your team's chosen pattern. Wingspan generated a `fetchUser` implementation using a sealed result type — a pattern common in the broader Dart ecosystem. The tests matched with exhaustive pattern matching, so they passed. The architecture agent approved the layer separation. But the team convention is to use exceptions for expected failure cases: ```dart // Generated — compiles, tests pass, agents approve Future> fetchUser(String id) async { try { final user = await _api.getUser(id); return Right(user); } catch (e) { return Left(NetworkFailure(e.toString())); } } // Convention — throw exceptions, not result types Future fetchUser(String id) async { try { return await _api.getUser(id); } catch (e) { throw NetworkException(e.toString()); } } ``` The reviewer caught it because they knew the convention. If it had shipped, callers would use exhaustive pattern matching where the team expects a try/catch. Once caught, the fix is not just to correct the PR — it is to write the convention down so Wingspan never generates the wrong pattern again. A `CLAUDE.md` entry at the project root takes precedence over the skill's defaults: ```md ## Error handling Use exceptions for expected failure cases in repositories — not result or sealed types. ``` On the next build, Wingspan reads this before generating any repository code. The skill provides the baseline; the project entry overrides it. The convention stops being something a reviewer has to remember and becomes something the AI enforces from the start. ### UX behavior preferences How state transitions feel to the user — decisions made in design discussions that exist nowhere in the code's type system. Wingspan emitted `const UserProfileState(status: UserProfileStatus.loading)`, clearing the `user` field on refresh. The state transition is valid. But the team decision is that loading states carry the previous data so the screen does not flash blank: ```dart // Generated — valid state transition, no agent flags it emit(const UserProfileState(status: UserProfileStatus.loading)); // Convention — preserve previous data during refresh emit(state.copyWith(status: UserProfileStatus.loading)); ``` No agent flags a valid state transition. The reviewer caught it because they remembered the UX decision. ### Layer responsibility What logic belongs in which layer. Agents enforce that layers do not import each other incorrectly — not that logic is in the right place within a valid import boundary. Wingspan put display formatting directly in a `Text` widget. It compiled, passed analysis, passed all five agents. But the team rule is that derived display data belongs in the model or a dedicated formatter, not in the widget: ```dart // Generated — compiles, passes all agents Text('${user.firstName} ${user.lastName}'.trim()) // Convention — display formatting belongs in the model or a formatter Text(state.user.displayName) ``` A reviewer caught it on the second read. None of these are obscure edge cases. They are the kind of decisions every team makes and rarely writes down. This second category is also an opportunity. Every time a reviewer catches a decision the agent missed, that decision should be written down. A `CLAUDE.md` file at the project root is the right place — both Wingspan and the VGV AI Flutter Plugin read it during generation, so documented decisions feed directly into what the AI writes next time. ## The New Shape of Review The checklist changes when the toolchain is in place. ![How the code review pyramid shifts when Wingspan agents handle the top layers, freeing human review to focus on implementation semantics and team decisions](/assets/images/blog/code-review-ai-generated-code/review-pyramid.png) **Stop spending time on:** formatting, import ordering, missing `const`, lint violations, naming that follows standard Dart conventions, standard VGV architecture patterns. These are caught before the PR opens. **Spend time on:** Plan-to-code fidelity — does the PR match what the plan specified? Scope — are there changes outside the plan? Should they be a separate PR? Team-specific decisions — does the code follow conventions your team holds that are not in any agent's ruleset? The build phase review summary — Wingspan surfaces what its agents flagged and how they were resolved. Read it. If an agent flagged an architecture concern and it was auto-fixed, verify the fix makes sense. Trust but verify. Agent behavior — go one level further. If an agent approved something it should have caught, or flagged something incorrectly, that is signal worth capturing. A pattern the architecture agent consistently misses belongs in your project-wide agent settings. A false positive that keeps surfacing is a prompt to tighten the agent's scope. The agents improve when reviewers treat gaps as feedback, not just noise. ## The Quality Gate Is Still Yours Wingspan and the VGV AI Flutter Plugin do not remove the need for code review. They move the quality gates earlier and automate what can be automated, which is more than most teams realize. What remains is genuinely human work: validating intent against the plan, enforcing decisions that live in your team's context, and making the judgment calls that no agent has the information to make. That work does not compress just because the generation was fast. If anything, it matters more — because the code is generated faster than any team can internalize, and review is where the team's decisions actually get applied. ### Flutter GenUI Hackathon in San Francisco Source: https://verygood.ventures/blog/flutter-sf-genui-hackathon/ Generative UI is moving fast enough that the best way to understand what it can do is to build something with it in a single day. Interfaces that compose themselves at runtime ask for new mental models around state, layout, and testing, and those models get a lot clearer once you have shipped a working flow. That is the idea behind the Flutter GenUI hackathon we are co-organizing in San Francisco with the [Flutter San Francisco community](https://luma.com/fluttersf). ![Flutter GenUI hackathon in San Francisco hosted by VGV and the Flutter SF community](/assets/images/blog/flutter-sf-genui-hackathon/banner.avif) We picked three industries to anchor the event, and we picked them on purpose. Automotive, quick-service restaurants, and financial services are exactly the kind of high-stakes, multi-surface domains where Flutter's reach and GenUI's runtime adaptability earn their keep. More on each one below. ## What is GenUI? GenUI, short for generative UI, is an approach where an AI model assembles the interface at runtime instead of a team hand-building every screen in advance. You give the model a set of Flutter widgets and the context it needs, and it composes a layout that fits the moment: the user's intent, their history, and whatever data just arrived. We have built this in production already, including [generative UI in a real booking experience](https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/) and covered the broader momentum in [what we saw at Google I/O 2026](https://verygood.ventures/blog/google-io-2026/). If you want to go deeper on the tooling, start with our [guide to generative UI with Google's GenUI SDK for Flutter](https://verygood.ventures/resources/genui/) and the [GenUI SDK for Flutter documentation](https://docs.flutter.dev/ai/genui). ## Three verticals where GenUI fits ### Automotive Vehicle interfaces have to shift constantly: driving versus parked, day versus night, one driver's preferences versus another's. GenUI is a strong match for generative heads-up displays (HUDs), adaptive dashboards, and voice-to-UI flows that reshape the screen based on what the driver is doing. Fleet operations and EV interfaces add another layer, where the same app surface has to serve very different roles. A dashboard that composes itself around context rather than cycling through fixed screens is a buildable Flutter app today. ### QSR (quick-service restaurants) Quick-service restaurants live and die on speed and personalization, and GenUI touches both. A kiosk that adapts its flow based on order history is no longer a research prototype. It is something you can build in a day. Kitchen display systems, loyalty UX, and voice ordering all benefit when the interface assembles itself around the order in front of it instead of forcing every customer down the same path. This is a space we know well, including [scalable Flutter solutions for QSR](https://verygood.ventures/blog/mobile-ordering-that-works-in-the-real-world-scalable-flutter-solutions-for-theme-parks-qsr-cruise-lines-entertainment-venues/) across theme parks, restaurants, and entertainment venues. ### Financial services Financial services carry real regulatory weight, which is exactly why adaptive, AI-composed interfaces are interesting here. Think AI-composed wealth dashboards that surface what matters to a given investor, adaptive Know Your Customer (KYC) flows that adjust to the documentation a user actually has, and conversational payments. Each one is a place where generative UI can reduce friction without cutting corners on compliance, and where Flutter's multi-platform reach keeps fintech app development on a single codebase. ## VGV and the Flutter SF community We are co-organzing this hackathon alongside the [Flutter San Francisco community](https://luma.com/fluttersf). We are Flutter practitioners, and VGV engineers will be on site to mentor teams through the GenUI SDK during the event and to judge the final projects. The community brings the people and the energy. We bring hands-on experience shipping generative UI in production. The goal is a day where people actually build, not just talk about what GenUI might someday do. ## Event details and registration - **Date:** Friday, June 26, 2026 - **Time:** 8:30 AM – 6:30 PM - **Location:** San Francisco, CA (in-person) - **Who should come:** Designers, engineers, and product thinkers. No prior GenUI experience required. Bring a laptop with Flutter set up and an idea, or come find a team when you arrive. [Register on Luma](https://luma.com/vxul6pt9) to reserve your spot. ## Come build with us If you are working in one of these spaces, or you just want to see what GenUI looks like when it ships, this is the room to be in. We will be watching what the community builds across all three verticals, and we can't wait to see what you ship! [Register on Luma](https://luma.com/vxul6pt9) and we will see you there. Curious about how these ideas go from a one-day build to production? See [our Flutter app development work](https://verygood.ventures/our-services/). ### Accessibility as a slash command Source: https://verygood.ventures/blog/accessibility-wcag-compliance-skill/ Accessibility at most teams lives at the end of the pipeline. Release dates are fixed, compliance checks get a half day, everyone knows the right answer, but few teams have time to do it. The users on the other end pay the difference. We built `/vgv-accessibility` to move WCAG 2.2 review from the release-day crunch into the moment you're writing code. Type `/vgv-accessibility`, answer two questions, and you get a conformance report in seconds. The skill translates WCAG into plain language so you don't have to parse the standards. The `/vgv-accessibility` skill is part of the [Very Good AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin). Install it to add WCAG audits to your Claude Code workflow. ## Why accessibility matters: building for everyone Accessibility isn't optional. Over a billion people globally live with disability, and in the US one in four adults has some type of disability. Skip it and you exclude them from your product. Inaccessible interfaces lock out screen reader users. Videos without captions lock out deaf users. Insufficient contrast locks out people with low vision. Small touch targets lock out people with motor impairments. You're shipping to part of your market. Accessible design also makes products better for everyone. Captions work in noisy environments. Keyboard navigation benefits power users. Good focus visibility helps anyone navigate. Text scaling helps mobile readers and people with low vision. Accessible design compounds—what works for people with disabilities works better for everyone. ## Three conformance levels: pick your standard WCAG comes in three conformance levels. A is the baseline, AA is what most projects ship to, and AAA is the gold standard that rarely ships because it demands extensive user testing and design trade-offs. Most teams default to AA without considering what it means or that AA itself changes between WCAG versions as new criteria are added. Your conformance target depends on your product type. A financial product with regulated flows might need AAA on specific screens, while a marketing site can ship at A and iterate later. The skill makes you choose instead of defaulting. When you run `/vgv-accessibility`, the first question is conformance level. Three options: ### Level A The baseline. It catches non-keyboard-operable buttons, missing button roles, and missing image labels. If your screen fails Level A, it fails for everyone. ### Level AA The practical standard for production apps. It adds requirements for contrast, focus visibility, text scaling, and keyboard navigation. WCAG 2.2 introduced three new AA criteria this year. ### Level AAA Full conformance. Every AAA criterion applies and is used when regulation, litigation risk, or mission-critical access demands it. Each level is cumulative. AA includes all A criteria. AAA includes all AA criteria. When the skill audits at AA, it checks every A criterion too. The difference between levels is material. A checkout screen at AA passes with 4.5:1 contrast. At AAA the same screen needs 7:1 contrast. AAA also disallows authentication methods that rely on a single sense. That's not busywork. It's the difference between a product colorblind users can access and one that locks them out entirely. ![Diagram showing WCAG conformance levels A, AA, AAA stacked vertically with six platform columns: iOS, Android, Web, macOS, Windows, and Linux, displaying how severity adjusts across platforms](/assets/images/blog/accessibility-as-a-slash-command/platforms-wcag.png) WCAG conformance levels and platform support matrix ## Platform shapes severity Same code runs differently on different platforms, which is what makes Flutter accessibility hard and why the skill's platform awareness matters. Take a TextField near the bottom of a form. On iOS and Android, the keyboard pushes content up automatically, so a sticky BottomAppBar that doesn't move ends up covering the field. A keyboard user can't see what they're typing, which is CRITICAL severity. The same code on Web behaves differently. The keyboard lives in the browser chrome and doesn't push the viewport, so the BottomAppBar still covers the field but leaves it partially visible. A user can scroll to find it, so this is MAJOR severity, not CRITICAL. The skill supports six platforms—iOS, Android, Web, macOS, Windows, and Linux—each with its own assistive technology and its own rules. **iOS and Android** run VoiceOver and TalkBack with touch targets sized for fingers. Keyboard shortcuts exist but are secondary. When a sticky bar covers a focused field, it's CRITICAL because the keyboard user can't see what they're typing. **Web** runs NVDA, JAWS, and VoiceOver, which are third-party assistive technologies rather than baked-in features. Focus visibility is nonnegotiable because screen readers aren't always on. When a sticky bar covers a focused field, it's MAJOR because the user can scroll to find it. **macOS, Windows, and Linux** use keyboard navigation as primary interaction. A 16x16 icon button that works on iOS is too small on desktop and needs 48 CSS pixels of actual hit area. The skill captures this. When you pick platforms, it loads platform-specific rules and adjusts severity. A dragging-without-alternative issue is CRITICAL on iOS and Android but MAJOR on Web where keyboard arrows usually work. On desktop it depends on the implementation. ## Mapping findings to WCAG criteria Every finding ties to a specific WCAG criterion, not a broad concept like "focus is important." The real criterion is WCAG 2.2 [2.4.11 Focus Not Obscured (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum) at Level AA. That specificity matters. When a designer asks "why is this CRITICAL?", you point to the exact criterion instead of guessing. You have one report that documents your conformance level and maps every finding to WCAG 2.2, whether you're sharing it with stakeholders, a client, or an external auditor. One source of truth built during development, not created at ship time. Here's a real finding from a checkout-flow audit: ```markdown ### Finding: Cart item swipe-to-delete has no non-drag alternative - File: lib/cart/cart_item_tile.dart ~L38 - WCAG: 2.5.7 Dragging Movements (Level AA, WCAG 2.2) - Platforms: iOS (CRITICAL), Android (CRITICAL), Web (MAJOR) - Issue: Items can only be removed via swipe. Switch users and keyboard users cannot delete an item. ``` The criterion is 2.5.7. It's new in WCAG 2.2. It's AA level. It applies differently to mobile and web. An engineer reading this sees immediately: every drag operation needs a non-drag alternative on the same screen. ## The report is your compliance paper trail After the audit, the skill produces a markdown report. Every finding includes the platform breakdown, severity, WCAG criterion, and plain-language explanation. It also lists every check that passed. ```markdown # Flutter Accessibility Audit Report Conformance Level: AA Platforms: iOS, Android Date: 2026-05-15 Files audited: lib/widgets/product_card.dart, lib/widgets/product_badge.dart, lib/checkout/checkout_button.dart ## Summary | Metric | Value | |--------|-------| | Total Findings | 5 | | CRITICAL | 1 | | MAJOR | 4 | ## Findings ### Finding 1.1 [CRITICAL] — Missing semantic label on product image **File:** lib/widgets/product_card.dart (line 28) **WCAG:** 1.1.1 Non-text Content **Issue:** VoiceOver and TalkBack cannot convey what the image shows. ### Finding 2.1 [MAJOR] — Target size below 24×24 dp minimum **File:** lib/widgets/product_card.dart (line 62) **WCAG:** 2.5.8 Target Size (Minimum) **Issue:** Motor-impaired users cannot reliably tap an 18×18 dp target. ### Finding 5.1 [MAJOR] — Fixed-height container clips at larger text scales **File:** lib/widgets/product_badge.dart (line 18) **WCAG:** 1.4.4 Resize Text **Issue:** At iOS Dynamic Type 2x or Android font scale 2x, text clips. ### Finding 6.1 [MAJOR] — Animations not gated on disableAnimations **File:** lib/widgets/product_card.dart (line 68) **WCAG:** 2.3.3 Animation from Interactions **Issue:** Plays at full speed even with Reduce Motion enabled. Vestibular-triggered users experience dizziness. ## Passed Checks - 1.2.1 Audio-only and Video-only (Prerecorded): PASS - 1.3.1 Info and Relationships: PASS - 1.4.3 Contrast (Minimum): PASS - 2.1.1 Keyboard: PASS - 2.4.3 Focus Order: PASS - 2.4.7 Focus Visible: PASS - 2.5.7 Dragging Movements (Level AA): PASS Remediation: Fixed 5 findings (CRITICAL, MAJOR). ``` The passed checks matter. A reviewer can confirm "we checked focus visible," compliance has the evidence they need, and an auditor can see your WCAG version in the report. The skill runs the audit but doesn't auto-fix everything. After you review the findings, you choose what to address: all issues, critical and major only, critical only, or specific findings by number. You stay in control of scope. ## WCAG 2.2 was the push WCAG 2.2 shipped in October 2023 and added seven new AA criteria. Three are platform-neutral, three are mobile-specific, and one governs authentication. The audit rules changed fundamentally. Before 2.2, a 16x16 icon button was fine at AA if it had a 48 dp bounding box. WCAG 2.2 tightened this. [2.5.8 Target Size Minimum](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum) now requires 24 CSS pixels of actual touch target at AA. [2.5.5 at AAA](https://www.w3.org/WAI/WCAG22/Understanding/target-size-enhanced) requires 44 CSS pixels. The skill audits against this new standard. WCAG lives in standards documents, but the skill lives in your editor. When you ship Flutter code now, you're shipping against WCAG 2.2, though most teams don't know which version applies. The skill makes that explicit. ## What the skill is not The skill is a code reviewer. It doesn't run the app, screenshot it under VoiceOver, or interview a user with low vision. It catches the patterns that experienced engineers spot in code review but misses what only emerges in real use. Every project still needs testing with assistive technology. The skill is the floor. When the obvious issues are gone, the interesting work begins. Up next is what the skill actually catches in code: the patterns, the diffs, and the fixes that let you ship accessibility from the moment you write the widget. ### The Art of Retro UI: Building NES UI in Flutter Source: https://verygood.ventures/blog/erick-zanardo-very-good-ventures-how-to-build-an-8-bit-design-system-with-nes-ui/ {% video url="https://www.youtube.com/watch?v=dn8ykxs54-c" title="Erick Zanardo, Very Good Ventures: The Art of Retro UI, Building NES UI in Flutter" %} NES UI is a retro 8-bit design system for Flutter, built by Erick Zanardo for his own games and open sourced for the wider Flutter community. In this episode of *Very Good Engineering*, host Jorge Coca talks with Erick about pixel-art widgets, the side project he built to embed icons in Dart code, and why a design system is really the same problem as organizing your code. ## About Erick Zanardo Erick is a Technical Delivery Lead at Very Good Ventures and a Google Developer Expert in Flutter and Dart. He's one of the engineers behind [Flame](https://flame-engine.org/), the open source game engine with more than 10,000 GitHub stars, and contributed to Google's I/O Pinball alongside other VGV activation demos. Outside of Flame, he ships his own games under [Cherry Bit Studios](https://cherrybit.studio/). ## A Design System Is a Rule Set Erick's definition is refreshingly direct. "It is when you duplicate rules and organize how a design should work. It's not much different from how developers organize their code." Color tokens with meaning, predictable spacing, components that look the same wherever they appear. The point of all those rules is the user. Familiar screens feel comfortable, and mismatched ones drive people away. ## The Origin of NES UI Erick came to Flutter through game development, and he wanted his games to look vintage. He also didn't want to keep rewriting buttons and cards every time he started a new project. So he built NES UI for himself, studying screenshots from games like Final Fantasy 6 and CSS libraries that brought 8-bit styling to the web. Then he open sourced it. "I felt because I was already in the Flame community and I was seeing this community growing, that making this open source could help other devs. And happily, I was right." ## Material as a Foundation NES UI is built on top of Material. People hear "Material" and picture the default Android look, but Flutter's Material is flexible enough to reskin into almost anything. Erick reuses Material's structure for buttons, cards, and containers, then customizes the rendering so each widget reads as pixel art. The result is an 8-bit aesthetic that still feels familiar to any Flutter developer who has read the Material docs. ## Mini Sprite: Icons as Strings A big part of NES UI is its icon set, each one hand-drawn pixel art. The catch: Dart packages can't ship binary assets. So Erick built a small image format called [Mini Sprite](https://minisprite.es) that encodes pixel art as a compressed string. Icons live directly in the package code, and Erick built an editor so he can draw a new icon, copy the encoded string, and paste it into his widget tree. ## Cherry Bit Studios and Fair Pricing Cherry Bit is the umbrella for Erick's own games, including [Pixelado](https://cherrybit.studio/pixelado/) and [Mine Cat Digger](https://cherrybit.studio/mine-cat-digger/). Both are built with NES UI and Flutter, with small Flame components mixed in where animation calls for it. Both are free with ads, and a single in-app purchase removes the ads for good. "I didn't want to be that," Erick says of the aggressive monetization common in mobile games. "I try to come up with a way that I think is fair, and I still might be able to get some revenue from that." ## Advice for Engineers Building a Design System Two pieces of advice from Erick for anyone tackling their first design system: 1. **Learn atomic design.** It is not a silver bullet, but it gives you the vocabulary of atoms, molecules, and organisms, and it will save you from reinventing concepts that already have names. 2. **Don't over-engineer.** "I start to create a new widget and then for some reason I decided everything would be customizable. And then I ended up with 200 lines of a theme data class. Then I realized it was not worth it. Maybe I should have different widgets." Two focused widgets often beat one infinitely configurable one. A tool like Widgetbook helps here. Hot reload on a widget in isolation makes it easier to spot over-engineering before it ships. ## Building in the Open For Erick, open source is the whole point. "If my thing is not open, it's not useful for other people, just to me." Watching other developers use NES UI is the motivation that keeps him coming back to it on weekends. Contributors have added widgets he never prioritized, growing the library faster than he could have alone. What's next? Erick is sketching out a fully hand-drawn game, scanning the art directly into Flutter to see how it lands. A long way from pixel art, but the same instinct for craft. ### A GenUI Tutorial: Building a Flutter Home Screen Your AI Composes Source: https://verygood.ventures/blog/flutter-genui-home-screen-tutorial/ The first time you see a Flutter home screen assembled by an AI instead of by you, something clicks. You didn't write `Column(children: [...])`. You described widgets, wrote a system prompt, and Gemini picked which widgets to render and in what order. The screen feels personal because the model composed it for the user sitting in front of it, right now, for this context. ![A Happy Health Smart home screen composed by Gemini at runtime, greeting the user by name and stacking four cards tailored to the moment: a "Today's Snapshot" card with steps, water, and weight; a "Healthier Hydration" card; a "Happier Rituals" card with a button to play daily puzzles; and a "Smarter Projects" reflection card](/assets/images/blog/flutter-genui-home-screen-tutorial/happy-health-smart-home.avif) We built [`genui_demo`](https://github.com/JohnWeidner/genui_demo) as a working example: a small wellness app whose home screen Gemini composes on every refresh from a curated Flutter widget catalog. This post walks through the four pieces you need to stand up your own GenUI surface. Catalog, transport, surface, and interactions. Real code, from the repo. We've covered the [ideas](https://verygood.ventures/blog/getting-started-with-genui/) and the [architecture](https://verygood.ventures/blog/flutter-genui-meets-the-vgv-architecture/) in earlier posts. This one is the tutorial. ## What we're building `genui_demo` is a personal wellness app built around three pillars: happier, healthier, smarter. The home screen is not a static widget tree. On every pull-to-refresh, the app gathers the user's health snapshot, daily rhythms, and preferences into a system prompt, hands that prompt and a widget catalog to Gemini, and renders whatever composition comes back. The stack: - `genui: ^0.8.0` — catalog, surface, conversation, transport - `google_generative_ai: ^0.4.7` — Gemini client - `flutter_bloc` — state management around the GenUI service - `flutter_secure_storage` — on-device API key Everything runs through one service and one cubit. No Firebase, no backend. The Gemini API key lives on device. A word on scope. This demo is built to help you see how GenUI works, not to be a reference architecture for production GenUI apps. The layering is deliberately light so each concept stays legible. If you want a full architectural breakdown with layered state, deferred navigation, and a bespoke widget catalog, read [the GenUI wizard we built for GCN](https://verygood.ventures/blog/generative-ui-flutter-wizard-gcn/) next. ## Step 1: Define the catalog The catalog is your vocabulary. Each entry tells Gemini "here is a widget you can render, here is its JSON schema, and here is how I'll build it." The SDK ships `BasicCatalogItems` with primitives like Text, Column, Button, and TextField. You add your own custom items on top. Here's the one custom item this demo ships. It's a "wish card" Gemini renders when it wants to show something outside the catalog: ```dart final featureRequestCatalogItem = CatalogItem( name: 'FeatureRequest', dataSchema: S.object( description: 'A polite placeholder the model renders when it wants to show ' 'something this client does not yet support.', properties: { 'title': S.string(description: 'Short label. 2-8 words.'), 'rationale': S.string(description: '1-2 sentences. Optional.'), 'proposedShape': S.string(description: 'One-line sketch. Optional.'), 'pillar': S.string( description: 'Which pillar the wish serves. Optional.', enumValues: _pillars, ), }, required: ['title'], ), widgetBuilder: (itemContext) { final data = _FeatureRequestData.fromMap(itemContext.data as JsonMap); return _FeatureRequestCard(data: data); }, exampleData: [() => '''...'''], ); ``` Three things to notice. First, the schema is the contract. `json_schema_builder` gives you a fluent DSL. Descriptions matter. Gemini reads them and uses them to decide when to render your widget, so be specific and concrete. Second, `widgetBuilder` is plain Flutter. GenUI hands you typed data and you return a widget. The extension type `_FeatureRequestData.fromMap` is a lightweight adapter that typechecks the JSON without copying it. Third, `exampleData` teaches by example. This is the single highest-leverage field in a catalog item. When Gemini is unsure how to use your component, it reaches for your examples. The catalog itself composes the basic items, your custom items, and any extra system prompt rules: ```dart abstract final class AppCatalog { AppCatalog._(); static Catalog build() { final base = BasicCatalogItems.asCatalog(); return base.copyWith( newItems: [featureRequestCatalogItem], systemPromptFragments: [ ...base.systemPromptFragments, _customCatalogRules, ], ); } } ``` Prompt fragments are how you teach Gemini the rules for your catalog. When to reach for `FeatureRequest`. Which event names buttons can dispatch. Treat them as part of the API. ## Step 2: Wire Gemini to a Conversation The SDK's `Conversation` object is transport-agnostic. It speaks in ChatMessages and expects JSON chunks back. `GeminiGenUiService` in this demo is the glue that turns `google_generative_ai` into something `Conversation` can drive: ```dart class GeminiGenUiService { GeminiGenUiService({ required String apiKey, required this.systemPrompt, required Catalog catalog, String modelId = 'gemini-3.5-flash', this.onInteraction, }) : _model = GenerativeModel( model: modelId, apiKey: apiKey, systemInstruction: Content.text(systemPrompt), ) { controller = SurfaceController(catalogs: [catalog]); transport = A2uiTransportAdapter(onSend: _handleSend); conversation = Conversation( controller: controller, transport: transport, ); } // ... } ``` Three collaborators show up here. `SurfaceController` owns the live surfaces and data models. `A2uiTransportAdapter` converts streaming text chunks into surface operations. `Conversation` is the high-level API the app talks to through `sendRequest(message)`. The `_handleSend` callback is where Gemini actually runs. The adapter hands you a ChatMessage. You feed it to `generateContentStream`. You pipe each chunk back with `transport.addChunk(chunkText)`: ```dart Future _handleSend(ChatMessage message) async { // forward any UI interactions to onInteraction final userContent = Content.user([TextPart(message.text)]); _history.add(userContent); final responseStream = _model.generateContentStream(_history); final modelBuffer = StringBuffer(); await for (final chunk in responseStream) { if (_disposed) break; final chunkText = chunk.text; if (chunkText == null || chunkText.isEmpty) continue; modelBuffer.write(chunkText); transport.addChunk(chunkText); } if (!_disposed) { _history.add(Content.model([TextPart(modelBuffer.toString())])); } } ``` Streaming is first-class. Your user sees the dashboard render progressively, widget by widget, as Gemini's JSON arrives. You get this by pushing chunks into the transport rather than waiting for the full response. ## Step 3: Render the surface Once the service is alive, ask for a composition and wait for a surface ID: ```dart final surfaceCompleter = Completer(); _eventsSub = service.conversation.events.listen((event) { if (event is ConversationSurfaceAdded) { _rebindDataPaths(service, event.surfaceId); if (!surfaceCompleter.isCompleted) { surfaceCompleter.complete(event.surfaceId); } } }); unawaited(service.sendUserMessage(userMessage)); final surfaceId = await surfaceCompleter.future .timeout(const Duration(seconds: 45)); ``` The `SurfaceController` emits `ConversationSurfaceAdded` once Gemini sends the first `createSurface` chunk. Handing that surface ID to a `Surface` widget is all you need to render: ```dart Surface(controller: service.controller, surfaceId: surfaceId) ``` ### Client-owned data paths Reactive state is where GenUI earns its keep. `DataModel` lets your app and the model share a path tree. Gemini's widgets read and write paths like `/today/captures_count` or `/drafts/note`, and your app binds Flutter streams to those same paths. Here's how the demo exposes a live counter of today's captured notes to any widget Gemini renders: ```dart void _rebindDataPaths(GeminiGenUiService service, String surfaceId) { _unbindDataPaths(); final dataModel = service.controller.store.getDataModel(surfaceId); _unbindCaptures = dataModel.bindExternalState( path: DataPath(DataPaths.todayCapturesCount), source: _liveData.todayCapturesCount, ); } ``` Any `Text` component Gemini renders with `BoundString` or `BoundNumber` on `/today/captures_count` now reflects the app's live value. Rewrite the counter on disk, call `_liveData.refresh()`, and every widget bound to that path updates on the next frame. The seed matters too. Before the first surface is composed, the app tells Gemini what paths exist and what values they currently hold: ```dart JsonMap _buildClientDataModelSeed() { return { 'today': { 'captures_count': _liveData.todayCapturesCount.value, }, 'drafts': { 'note': '', }, }; } ``` Gemini sees the path tree in the system prompt and can render widgets bound to any of it from turn one. ## Step 4: Handle interactions Buttons Gemini renders dispatch events back to your app. The demo listens through `onInteraction`: ```dart GeminiGenUiService( apiKey: key, systemPrompt: systemPrompt, catalog: catalog, onInteraction: _handleInteraction, ) ``` `_handleInteraction` routes well-known event names to persistence code. Capturing a note into the event log. Recording an answered question. Marking a daily puzzle done. The constraint lives in the catalog's system prompt fragment: ```text INTERACTION EVENT NAMES you can dispatch from Buttons: - note_captured — TextField + submit Button capture pattern. - answer_submitted — Slider / ChoicePicker + submit Button pattern. - puzzle_played — "Mark done" button on a daily-puzzle card. Any other event name you dispatch is passed through unchanged and has NO local side effects. ``` Gemini now knows exactly which interaction names your app handles and what context payload each expects. This is the same principle as designing a REST API. A short, well-documented vocabulary beats a wide one. Two more pieces finish the loop. First, the allowed operations policy. This demo lets Gemini create a surface once and then update it in place: ```dart final prompt = PromptBuilder.custom( catalog: catalog, allowedOperations: SurfaceOperations.createAndUpdate(dataModel: false), systemPromptFragments: [...], clientDataModel: _buildClientDataModelSeed(), ); ``` `dataModel: false` keeps writes client-owned. Gemini can read paths but cannot rewrite them, which preserves a single source of truth for anything sensitive. Second, the event subscription keeps listening after the first surface arrives. Follow-up turns from Gemini usually come in as `ConversationComponentsUpdated` events. The `Surface` widget handles those internally, so the cubit does not re-emit state. The UI just updates. ## Inside the app: four inspectors When we built `genui_demo`, we wanted a first-class way to see what was happening at each layer. The drawer labeled "Inside the app" isn't a debug mode hidden behind a build flag. It's a feature, always on, because one of the app's goals is to teach how GenUI works. Four pages, one per concept, each tied to something earlier in this post. ![The "Inside the app" drawer in the demo, showing four learning pages — GenUI catalog, Conversation log, Prompt inspector, and Data model — each with a short description of what it surfaces](/assets/images/blog/flutter-genui-home-screen-tutorial/inside-drawer.avif) ### GenUI catalog The first drawer entry lists every catalog item the model is allowed to compose, rendered with its own example payload. It uses `DebugCatalogView` from the `genui` package, so the page updates automatically the moment you register a new `CatalogItem` in `AppCatalog.build()`. When you're wondering "does the model know about this widget yet?", this page is the answer. It's also where the `exampleData` you wrote in Step 1 earns its keep — the card for each item shows exactly what Gemini sees when it's deciding whether to use your component. ### Conversation log Every event the live `Conversation` emits lands here: `surfaceAdded`, `componentsUpdated`, `surfaceRemoved`, streamed text chunks, waiting states, and errors. Entries are newest-first and tap to expand. This is the page that makes the streaming behavior from Step 2 visible. You can watch `Streamed text` entries arrive in real time as Gemini composes, then see `Surface added` fire when the first `createSurface` chunk lands. On follow-up turns it's usually `Components updated`, which confirms the `createAndUpdate` policy from Step 4 is doing what we asked. ### Prompt inspector The exact system prompt the app sent on the most recent refresh. The page shows: - Capture time and character count - The user message that kicked off the turn - Each labeled fragment (`PERSONA`, `USER_PROFILE`, `HEALTH_CONTEXT`, `DATA_PATHS`, `SURFACE_UPDATES`, and the others) with its own Copy button - The full joined prompt, monospace, at the bottom This is the single most useful page for tuning behavior. When the model makes a surprising choice, the prompt inspector tells you exactly what it saw when it made it. Tweak a fragment in the cubit, refresh, and compare the new capture side-by-side with the old one. ### Data model A live view of the active surface's `DataModel`, which is the shared path tree from Step 3. Every known path gets a row with its current value, description, and source. Below that, the raw JSON tree prints every key the surface currently contains, updated in real time. When a note is captured and `LiveDataStore.refresh()` runs, `/today/captures_count` ticks up on this page without any reload. That's `bindExternalState` working in public. Any widget the model renders with `BoundString` or `BoundNumber` on the same path is reacting to the same values you're watching here. The four pages line up with the four steps in this post. Catalog to Step 1. Conversation log to Step 2. Prompt inspector to the system prompt you build before handing it to the service. Data model to the paths you bind in Step 3. If you fork the repo and add one of your own catalog items, one data path, or one interaction handler, the drawer is where you'll watch your change light up. ## Make it your own Fork the [repo](https://github.com/JohnWeidner/genui_demo), drop your Gemini API key into Settings, and run it. Then try this progression. Add a catalog item. Pick something small: a badge, a streak card, a single-stat tile. Write the schema, write the builder, add a rule fragment that tells Gemini when to reach for it. Watch Gemini pick it up on the next refresh. Add a data path. Expose a value from your app with `bindExternalState`. Ask Gemini to render it in a Text widget. Watch it stay in sync. Add an interaction. Give a Button a new event name, route it in `onInteraction`, and persist something. Teach Gemini about it in a prompt fragment. Each step is independently useful, and each one strengthens the mental model. The catalog is your vocabulary. The surface is your canvas. The data model is your shared state. Interactions are your write path. Get those four right and GenUI stops feeling like magic and starts feeling like a platform you build on. ### Flutter Socket.IO Chat: Real-Time Production Messaging Source: https://verygood.ventures/blog/flutter-socket-io-realtime-chat/ A tax client and a tax preparer working on the same return need a place to talk. Email is too slow, phone tag is worse, and a separate app means another login. We worked through a production Flutter codebase that puts that conversation inside the client portal itself, on iOS, Android, and web. The chat layer runs on [Socket.IO](https://socket.io/), sits behind a clean-architecture seam, and ships from a single Dart and Flutter codebase. ## The product context The app is a client-facing fintech portal for a tax and accounting service in the United States. Clients use it to upload documents, review tax returns, manage payments, and check the status of their filing. Each client is assigned a tax preparer on the back office side, and the two need to coordinate continuously during filing season. Most of that coordination is short messages. A question about a missing W-2. A confirmation that a document was received. A receipt that the return was filed. This is an enterprise Flutter client portal built once for mobile and web from a single Dart and Flutter codebase, and the chat module is one of the more interesting surfaces because it has to satisfy three constraints at once. It must feel instant to the client. It must keep state consistent across sessions and devices. And it must work in a browser tab on Safari, where the rules for long-lived sockets are different from native. ## Why Socket.IO, not a raw WebSocket A real-time chat in Flutter can be built directly on [`dart:io`'s WebSocket](https://api.flutter.dev/flutter/dart-io/WebSocket-class.html), on [`package:web_socket_channel`](https://pub.dev/packages/web_socket_channel), or on a higher-level protocol like Socket.IO. The codebase uses Socket.IO, and the benefits appear throughout the implementation. For a broader look at how we approach Flutter real-time work, see our case study on [building real-time apps with Flutter](https://verygood.ventures/blog/very-good-location-sharing-chat-app/). Socket.IO buys three things this product needs: A room model. Each client gets a room keyed by their `clientId`. The preparer joins the same room from the back office. Routing messages becomes a server-side concern, and the client only listens to events it cares about. Named events with payloads. Instead of a single message stream that has to be parsed and dispatched, the implementation works with semantic events like `onMessage`, `onSetAsRead`, `onRemoveMessage`, and `onFindAllMessages`. Each event has its own handler, and the contract between Flutter and the Node backend is explicit. Transport fallback. Socket.IO can run over WebSocket where it is supported, and fall back to HTTP long-polling where it is not. For a web build that needs to work across browsers, that fallback is doing real work, as we will see. The dependency lives in `pubspec.yaml` as a path dependency on a vendored copy of `socket_io_client` 1.0.2: ```yaml socket_io_client: path: modules/socket_io_client-1.0.2 ``` Pinning to a local copy keeps the team in control of the transport code. If the upstream package ships a regression, the build does not break overnight. The trade-off is that security patches have to be backported by hand. ## One client, one room, one preparer The event protocol is small and easy to reason about. It lives in `lib/domain/utils/chat_utils.dart`: ```dart enum MessageEvent { onMessage(NetConstants.onMessage), findAllMessages(NetConstants.onFindAllMessages), removeMessage(NetConstants.onRemoveMessage), joinRoom(NetConstants.onJoinRoom), error(NetConstants.onError), setAsRead(NetConstants.onSetAsRead); const MessageEvent(this.value); final String value; } enum MessageSenderType { employee(NetConstants.employee), client(NetConstants.client); const MessageSenderType(this.value); final String value; } ``` Two enums carry the entire wire contract. `MessageEvent` enumerates the events the client emits and listens for. `MessageSenderType` distinguishes a message sent by the client from one sent by the assigned preparer. Every payload that crosses the socket uses one of these constants, which means the compiler, not the developer, enforces the protocol. On connection, the client joins its room and asks for history in two emits: ```dart _socket?.emit(MessageEvent.joinRoom.value, { NetConstants.roomId: "$clientId", }); _socket?.emit(MessageEvent.findAllMessages.value, { NetConstants.clientChatId: clientId, NetConstants.employeeId: taxId, NetConstants.roomId: "$clientId", "page": 1, "take": NetConstants.pageTake, }); ``` The room id is the client id, stringified. That choice matters for the back office. A preparer who handles dozens of clients in a day joins each client's room as needed, and the server fans messages out by room. The Flutter app does not need to know anything about that. It only knows its own room. ## Two transports, one codebase The transport fork lives in `_initSocket`. The transport list branches at build time based on whether the app is running in a browser: ```dart io.Socket _initSocket(String? accessToken) { return getIt().isWeb ? io.io( "${FlavorConfig.instance.values.baseUrl}:3001/", io.OptionBuilder() .setTransports(["polling"]) .disableAutoConnect() .setExtraHeaders({ NetConstants.authorizationHeader: "${NetConstants.bearerAuthorization} $accessToken", }) .build(), ) : io.io( "${FlavorConfig.instance.values.baseUrl}:3001/", io.OptionBuilder() .setTransports(["websocket", "polling"]) .disableAutoConnect() .setExtraHeaders({ NetConstants.authorizationHeader: "${NetConstants.bearerAuthorization} $accessToken", NetConstants.userAgentHeader: NetConstants.userAgentValue, "Connection": "Upgrade", "Upgrade": "websocket", }) .build(), ); } ``` On iOS and Android, the socket prefers WebSocket and falls back to polling. The handshake includes the standard `Connection: Upgrade` and `Upgrade: websocket` headers and sets a desktop user agent so the upstream proxy handles the request the same way on every client. On web, the transport list is reduced to `polling` only. WebSocket is removed from the negotiation. For background on Flutter and WebSocket in general, the [Flutter WebSockets cookbook](https://docs.flutter.dev/cookbook/networking/web-sockets) is a good baseline. Polling-only on web is technically a step back from WebSocket. The reason is browser reality. The Authorization header cannot be set on a raw WebSocket handshake from a browser, which means token auth has to ride on the polling transport that is reachable via XHR. The [browser WebSocket API does not allow custom headers](https://github.com/dart-lang/web_socket_channel/issues/379), and this limitation is exactly what forces the polling fork. Forcing polling keeps the auth model identical across platforms at the cost of slightly more chatter on web. The chat page warns Safari users that messaging behaves differently, which shows the codebase encountered this limitation in production and chose transparency over a workaround. `disableAutoConnect()` is on for both transports. The socket is created lazily and connected only when the chat screen is mounted, which keeps idle clients off the real-time backend. Pair that with `clean()` and `disconnect()`, which release the socket and reset the message cubit, and the resource lifecycle stays tight. ## A clean-architecture spine The chat layer mirrors the app's three-layer structure: presenter, domain, and data. Each layer has one responsibility. This is the same shape described in our writeup on [VGV layered architecture](https://verygood.ventures/blog/very-good-flutter-architecture/). The presenter layer is `ChatPage`. It owns the text controller, the reply title, and the scroll, and it delegates everything else to the manager. It also listens to a Cubit so the UI rebuilds when messages change. ```dart @override void initState() { super.initState(); taxId = _profile?.taxPreparer?.id; clientId = _profile?.id; _chatManager.initChatManager(taxId, clientId, _accessToken); ... } @override void dispose() { _chatManager.disconnect(); super.dispose(); } ``` The domain layer is `ChatManager`. It is the only place that knows Socket.IO exists. Every emit and every listener lives here, and the rest of the app talks to chat through an abstract interface: ```dart abstract class ChatManager { Cubit>> get chatMessages; void initChatManager(String? taxId, String? clientId, String? accessToken); void sendMessage(MessageSubject subject, String message); void replyMessage(String replyTitle, String repliedMessageId, String message); void deleteMessage(ChatMessage message); void disconnect(); void clean(); void onUploadDocument( String year, String? taxId, String? clientId, String? accessToken, ); void onAccountCreated(); void onAdditionalServicesAdded(List addOns); } ``` The last three methods are not chat operations. They are product events. When a client uploads a document, finishes signup, or adds an extra service, the manager emits a chat message describing what happened so the preparer sees it in their thread. The chat doubles as an activity log between client and preparer, which is a small idea with a large payoff. It collapses three notification channels into one. The data layer is `ChatRepository`. It maps raw payloads from the socket and from the legacy HTTP endpoint into `ChatMessage` domain objects, derives flags like `isMe` from the sender type, and pulls a list of unread message ids out of a `findAllMessages` payload so they can be marked as read in one round trip. Wiring is done in `lib/di/di_init.dart` using [`get_it`](https://pub.dev/packages/get_it): ```dart getIt.registerLazySingleton( () => ChatRepositoryImpl(getIt(), getIt())); getIt.registerLazySingleton( () => ChatManagerImpl(getIt())); ``` A lazy singleton for the manager is the right shape here. The chat needs a single source of truth for the socket and the message list across the lifetime of the session, and the lazy registration means the socket is not constructed until something asks for it. For more on why we lean on this state-management stack, see [why we use flutter_bloc for state management](https://verygood.ventures/blog/why-we-use-flutter-bloc/). ## State, read receipts, and idempotent updates The message list lives in a private cubit inside `chat_manager_impl.dart`: ```dart class _MessagesStateCubit extends Cubit>> { _MessagesStateCubit() : super(Data( state: DataState.isLoading, data: List.empty(), )); void addMessage(ChatMessage newMessage) { ... } void addMessages(List newMessage) => _notifyData(newMessage); void removeMessage(String uid) { ... } void setMessageAsRead(String id) { ... } void setMessageAsReplied(String id) { ... } } ``` [`flutter_bloc`](https://pub.dev/packages/flutter_bloc) does the heavy lifting. The cubit emits a `Data>`, the `ChatPage` rebuilds with `BlocBuilder`, and the rest of the UI stays declarative. The cubit is private to the manager, which keeps state mutation funneled through a small number of methods and prevents the rest of the app from poking at the message list directly. The choice of Cubit over Riverpod or another state manager kept the chat consistent with the rest of the app's state layer. That alignment pays back in onboarding time. For deeper coverage of this pattern with live data, see [using Bloc with streams](https://verygood.ventures/blog/how-to-use-bloc-with-streams-and-concurrency/). Three details in the message handler stand out. First, deduping. When a new message arrives, the manager checks the existing list by id before appending: ```dart var alreadyAdded = _chatMessages.state.data ?.any((element) => element.id == newMessage.id) ?? false; if (!alreadyAdded) { _chatMessages.addMessage(newMessage); ... } ``` Real-time systems duplicate events. A reconnect after a brief disconnect can replay a message the client already has. Filtering by id at the application layer is cheap insurance against double-rendered bubbles. Second, read receipts. As soon as a message from the preparer is added to the list, the manager emits a `setAsRead` event with that message id: ```dart if (!newMessage.isMe) { _socket?.emit(MessageEvent.setAsRead.value, { NetConstants.roomId: "$clientId", NetConstants.messageIds: [newMessage.id], NetConstants.clientChatId: clientId, NetConstants.employeeId: taxId, }); } ``` The same logic runs in bulk when history loads. `_setMessagesAsRead` pulls every unread employee message out of the initial `findAllMessages` payload and acknowledges them in one emit. The preparer's UI flips them all in a single update, which is the right behavior when a client opens the app for the first time in a day. Third, replies and deletes are explicit. A reply sends an extra field, `repliedMessageId`, on the same `onMessage` event so the server can thread it. A delete is allowed only if the message is from the current user and has not yet been read. That guard lives in the domain manager, not in the UI: ```dart void deleteMessage(ChatMessage message) async { if (!message.isRead && message.isMe) { _chatMessages.removeMessage(message.id); _socket?.emit(MessageEvent.removeMessage.value, { ... }); } } ``` Putting the rule in `ChatManager` means it cannot be bypassed by a different button on a different screen. The abstract interface also makes the manager straightforward to test with a mock, which is a quiet benefit of pushing transport details behind a domain seam. ![Flutter chat screen showing a long-press context menu with Reply and Delete options on a sent message bubble](/assets/images/blog/flutter-socket-io-realtime-chat/body-0.avif) ## Reconnection and the disconnect handler Network drops are the default state of the world on mobile. The implementation handles them with a reconnect loop inside the connect handler: ```dart _socket?.onDisconnect((_) { _socket?.connect(); }); ``` When the socket disconnects, it reconnects. The code comment explains this handles server failover. The app does not use exponential backoff at the application layer, and that is intentional. [Socket.IO's own engine handles backoff and jitter](https://socket.io/docs/v4/client-options/#reconnectiondelay) for the underlying transports, so the application layer focuses on the user-visible state machine. If a message arrives during reconnection, the dedupe-by-id check we saw earlier prevents a duplicate bubble. The `onUploadDocument`, `onAccountCreated`, and `onAdditionalServicesAdded` flows take the same approach. Each calls a private `_checkSocketStatus` that reconnects the socket if it is null or disconnected before emitting the message. Those code paths fire when the user has not opened the chat screen yet, so the socket might not exist. Reconnecting on demand is safer than assuming. ## What this codebase teaches Pick the protocol that matches the workload, then own the dependency. Socket.IO was the right call for a chat with rooms, named events, and mixed transports. Vendoring the client as a path dependency keeps the team in control of the transport code on a long-lived product. We have seen this pay off on consumer apps where a single regression in a popular real-time library can ship a P0 bug into production overnight. Treat the real-time client as a domain manager, not a global singleton with side effects. The interface for `ChatManager` lists the methods the app needs, and nothing else. Socket.IO never leaks past it. When the codebase needs to swap to a different protocol or add a second transport for push delivery, the surface area to change stays small. Make read receipts and dedupe boring. Both are implemented as a handful of lines, and both prevent a class of bugs that look easy on day one and ugly in production. Filter incoming messages by id. Acknowledge unread messages in bulk on history load. Acknowledge them one by one as they arrive. There is no clever trick here, and that is the point. Split transports by platform on purpose. The web build runs on polling because auth headers ride there reliably. The native build runs on WebSocket because the handshake supports them and the latency is lower. The fork is two branches of an `OptionBuilder` and a comment that explains why. A future engineer can read it without archaeology. For teams scaling Flutter for enterprise, these are the kinds of seams that keep a long-lived codebase legible. ## Where to take it next The `findAllMessages` response is paginated with a `take` of fifty, but the client does not yet request additional pages. Long threads need infinite scroll, and wiring a scroll controller to request the next page when the user nears the top of the list is a small change with a real payoff. The reconnect loop could surface a connection state to the UI so users see a "Reconnecting" affordance instead of a silent retry. Token rotation through the socket would let the chat survive a refresh without a full reconnect, and sketching that flow inside `_checkSocketStatus` is a natural extension of the existing pattern. To start a new Flutter project on a foundation that already encodes these seams, the [Very Good Core starter template](https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/) ships with the layered architecture and DI wiring described in this post. The current build runs in production, ships from one codebase to iOS, Android, and web, and keeps a clean seam between the real-time layer and everything else. For a team building a similar surface in Flutter, the patterns here transfer directly. Choose the protocol early, isolate it behind a domain interface, and handle the transport fork at init time. The conversation between a client and their preparer is a small surface area. Getting the real-time layer right keeps it that way. ### Flutter 3.44 at Google I/O 2026: What We Saw at the Conference Source: https://verygood.ventures/blog/google-io-2026-flutter/ Flutter is now the #2 most popular SDK on both major app stores. Chrome shipped APIs that could quietly fix Flutter Web's biggest limitations. And someone printed latte art on my cappuccino using Gemini. Here's what I came back with from Google I/O 2026. ![Audience at the Google I/O 2026 keynote at Shoreline Amphitheatre.](/assets/images/blog/google-io-2026/keynote-crowd-shoreline-amphitheatre.avif) ![Sundar Pichai on stage during the Google I/O 2026 keynote at Shoreline Amphitheatre.](/assets/images/blog/google-io-2026/sundar-pichai-keynote-stage-opening.avif) ## Flutter at I/O 2026 The headline stat from "What's New in Flutter" landed right at the top of the session. Flutter is the **#2 most popular app development SDK** on both the App Store and Google Play, and it's used by 1.5 million developers worldwide. For teams making framework decisions, a number like that is a useful long-term signal about ecosystem risk. ![The "What's New in Flutter" session at Google I/O 2026.](/assets/images/blog/google-io-2026/whats-new-in-flutter-session.avif) ![Slide showing Flutter ranked #2 most popular mobile app development SDK on the App Store and Google Play.](/assets/images/blog/google-io-2026/flutter-number-2-most-popular-sdk-slide.avif) [Flutter 3.44](https://blog.flutter.dev/whats-new-in-flutter-3-44-b0cc1ad3c527) shipped alongside the conference. A few things caught my eye. **Agentic Hot Reload** closes a feedback loop that has been annoying since AI-assisted coding became part of the daily workflow. The [Dart and Flutter MCP server](https://docs.flutter.dev/ai/mcp-server) finds your running app automatically and triggers hot reloads after an agent makes changes. The manual copy-paste loop between agent output and your terminal disappears. The feedback cycle shrinks from minutes to seconds. That kind of improvement compounds over a full workday. We've been writing about [MCP servers for Dart and Flutter developers](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/) for a while now, and [Very Good CLI's built-in MCP server](https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/) sits in the same lineage of work. **[Swift Package Manager is now the default](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers) for iOS and macOS.** CocoaPods has been a steady source of pain for years. The CLI handles migration automatically. The split of **Material and Cupertino** out of the core framework is progressing toward enforcement. Imports will soon move to `package:material/material.dart` instead of `package:flutter/material.dart`. [We've covered this shift before](https://verygood.ventures/blog/a-new-era-for-app-design-and-what-it-means-for-flutter/), and it's a necessary step toward cleaner API boundaries. Toyota premiered a [video on the work to bring Flutter to the 2026 RAV4 infotainment system](https://verygood.ventures/blog/top-companies-using-flutter/), with more vehicles to follow. Seeing the platform land in production automotive hardware in front of a developer audience reinforces why teams are picking Flutter for surfaces well beyond phones. This is a very special project for VGV as well, as it demonstrates the results of multiple years of hard work teaming up with the Toyota team. {% video url="https://www.youtube.com/watch?v=X4376aLFHFA" title="Flutter at Google I/O 2026 — Toyota RAV4" %} [GenUI](https://verygood.ventures/resources/genui/) was again a popular topic from [Cloud Next](https://verygood.ventures/blog/vgv-at-google-cloud-next-2026/) through I/O. The practitioner conversations I had were more specific than the headline concept. People were working through three recurring questions: how to keep token usage efficient when the model is generating UI on every interaction, whether on-device models are capable enough to handle it without a cloud round-trip, and how to identify which parts of an app actually benefit from generative UI versus which parts should stay deterministic. That third question is the hardest one. The appetite is clearly there, but the teams doing it well are the ones who are being deliberate about where to apply it. The Antigravity Coffee Co. demo was the standout floor experience. Flutter ran across multiple screens at once: phone, tablet, laptop, and TV. The app asked a few questions, generated a personalized prompt, and Nano Banana (Google's image generation model) produced custom latte art based on the answers. A printer then applied the image directly to the foam of a cappuccino. I asked it to make _"Coding with my fellow unicorns."_ The result was delightful, and the underlying point landed. A multi-screen Flutter app coordinating with image generation and physical hardware is exactly the kind of experience the framework is built for now. ![The order confirmation for a cappuccino with custom AI-generated latte art, powered by Gemini, Firebase, and Flutter.](/assets/images/blog/google-io-2026/antigravity-coffee-order-confirmation.avif) ![Marcus with the Antigravity Coffee Co. display showing the prompt "My happy place is: Coding with my fellow unicorns."](/assets/images/blog/google-io-2026/marcus-antigravity-coffee-display.avif) ![A cappuccino with AI-generated latte art printed directly on the foam.](/assets/images/blog/google-io-2026/ai-generated-latte-art-cappuccino.avif) ## HTML-in-Canvas and WebMCP: What They Mean for Flutter Web Google is bringing several new web APIs to Chrome with real implications for Flutter Web. [**HTML-in-Canvas**](https://developer.chrome.com/blog/html-in-canvas-origin-trial) could let Flutter Web's Canvas-based renderer embed native HTML elements with much lower performance cost than today. That opens a path to real SEO and native text tools (including browser translation) for Flutter Web apps, which has been one of the framework's longer-standing limitations on the platform. One of the Chrome engineers at the booth said it directly. "One of the first use cases we were interested in was Flutter Web." Worth tracking as the API moves through the origin trial and toward standardization across browsers. ![Chrome booth demoing HTML-in-Canvas and Built-in AI APIs at Google I/O 2026.](/assets/images/blog/google-io-2026/chrome-booth-html-in-canvas-built-in-ai.avif) [**WebMCP**](https://developer.chrome.com/blog/webmcp-epp) is a proposed standard for exposing structured tools so browser-based AI agents can interact with web apps reliably. For Flutter specifically, this is significant. Flutter Web doesn't have a DOM, so traditional in-browser agents have nothing to grab onto. WebMCP could let a Flutter web app explicitly declare its available actions so an agent can do something useful with it, like changing inputs or submitting orders. ![WebMCP demo booth at Google I/O 2026.](/assets/images/blog/google-io-2026/webmcp-demo-booth.avif) [Chrome 149 also ships APIs](https://developer.chrome.com/blog/chrome-at-io26) for calling AI models built directly into the browser, no server round-trip required. Together with improvements to running Gemma 4 on device for mobile apps, the direction is clear. On-device inference has real advantages for latency and privacy, and it removes token costs for calls that don't need cloud-scale reasoning. The open question for teams is where the capability ceiling lands for production use cases, and how that ceiling interacts with GenUI patterns that have so far assumed a cloud round-trip. ## Gen Media APIs [Project Genie](https://blog.google/products/google-labs/project-genie/) was the standout floor experience for generative media. You described a world in natural language, and Gemini generated it as a fully interactive 3D environment. The crystal globes around the booth were how you made your prompt — you picked a character globe and an environment globe, put them on special stands, and that combination seeded your generation. ![Wall of Project Genie crystal globes at Google I/O 2026 with the prompt "Pick your environment".](/assets/images/blog/google-io-2026/project-genie-crystal-globes-wall.avif) ![Project Genie crystal globes up close on the Google I/O 2026 floor.](/assets/images/blog/google-io-2026/project-genie-crystal-globes-closeup.avif)
There was an AI sticker generator where you picked two categories and Nano Banana combined them into a custom I/O sticker. I ended up with "Snowflake Motherboard." ![The AI sticker generation experience running on a Pixel tablet at Google I/O 2026.](/assets/images/blog/google-io-2026/ai-sticker-generator-pixel-tablet.avif) The AI portrait experience generated you as a pixel art fighter, a toy-like action figure, and a Renaissance oil painting, all powered by Nano Banana. ![Pixel art portrait generated by Nano Banana at Google I/O 2026.](/assets/images/blog/google-io-2026/nano-banana-portrait-pixel-art.avif) ![Toy-line action figure portrait generated by Nano Banana at Google I/O 2026.](/assets/images/blog/google-io-2026/nano-banana-portrait-action-figure.avif) ![Renaissance oil painting portrait generated by Nano Banana at Google I/O 2026.](/assets/images/blog/google-io-2026/nano-banana-portrait-renaissance.avif) ## Coming Back The last time I went to I/O was 2019, and it was the first time I felt like a real developer. I was surrounded by people who cared about the same problems I did, watching announcements that spoke directly to my work. I remember talking to the Flutter folks about project hummingbird, a demo of Flutter running in a web browser, that eventually became Flutter Web. I later learned [VGV had built the original demo](https://verygood.ventures/blog/flutter-agency-8-year-leadership-journey/), which cemented in my mind I wanted to work for them. Coming back in 2026 as a VGV Engineering Lead and [GDE in Flutter](https://verygood.ventures/blog/my-journey-from-barista-to-google-developer-expert/) felt like a homecoming of sorts. It also reinforced the thing I still believe about the Flutter community: it's the best in the world! I chatted with dozens of devs that were generous with knowledge and invested in making everyone's work better, the same way they always have been. Big thanks to the **GDE program** for the invitation and to the **Flutter team** for the hospitality and for building something I've been proud to work with for years. And to **Very Good Ventures** for sending me. I'm lucky to work somewhere that invests in the community. --- _Resources:_ - [What's new in Flutter 3.44](https://blog.flutter.dev/whats-new-in-flutter-3-44-b0cc1ad3c527) - [Flutter 3.44 release notes](https://docs.flutter.dev/release/release-notes/release-notes-3.44.0) - [Google I/O 2026: What's new in Flutter](https://io.google/2026/explore/pa-keynote-12) - [I/O 2026 developer highlights](https://blog.google/innovation-and-ai/technology/developers-tools/google-io-2026-developer-highlights/) - [100 things announced at Google I/O 2026](https://blog.google/innovation-and-ai/technology/ai/google-io-2026-all-our-announcements/) ### Rust as a Flutter Data Layer: Build Safe Data Layers Source: https://verygood.ventures/blog/rust-as-flutter-data-layer/ When you're building a Flutter application that needs to talk to hardware, there's a question that surfaces quickly: where does the systems-level code actually live? It's an architecture question—and the answer determines whether your team spends cycles chasing memory bugs in production or building features. At VGV, we saw this challenge play out firsthand while teaming up with Toyota on their in-vehicle infotainment system (IVI). In that project, Rust handles communication with the vehicle's computer: real-time data feeds, sensor inputs, and low-level control signals. Flutter owns state management and UI rendering. The result is a clean architectural boundary that lets each layer do what it does best. Let's look at why that division of responsibility makes sense, and how you can apply the same pattern in your own projects. ## The Problem with Reaching Too Far Flutter is an exceptional framework for building expressive, performant user interfaces across platforms. But it was designed to render UI—not to manage shared memory, handle unsafe pointer arithmetic, or guarantee the absence of data races in a concurrent systems environment. When embedded or native system code is involved, the typical approach is to reach for platform channels: write the systems-level logic in C, C++, then call it from Dart through FFI or a plugin interface. That works, but it comes with a cost. C and C++ are powerful precisely because they give you direct control over memory, but that power comes with responsibility—they'll let you misuse that control in ways that produce subtle, hard-to-reproduce bugs that only surface in production, often under load. Consider referencing [Flutter plugin generation with Very Good CLI](https://verygood.ventures/blog/generate-flutter-plugins-with-very-good-cli/) for an alternative approach to native integration. For a consumer infotainment system inside a vehicle, "subtle bug that surfaces under load" is not an acceptable failure mode. ![Conceptual comparison of memory safety — C/C++ with tangled pointer structures versus Rust with organized, protected memory management using VGV Blue shields and guardrails](/assets/images/blog/rust-as-flutter-data-layer/body-0.avif) ## How Rust Fills This Gap Rust was built to solve the exact problem that makes systems programming risky: it gives you low-level control without sacrificing memory safety. Its ownership and borrowing model enforces at compile time the discipline that C and C++ demand you maintain manually. Here's what that means in practice for a Flutter data layer: **Memory safety without a garbage collector.** Rust's ownership model eliminates entire classes of bugs (null pointer dereferences, use-after-free, data races) at compile time. You get native performance without GC unpredictability or the memory-safety risks of C/C++. For a Flutter app, this means the data layer is fast and correct by construction, not by careful discipline. For a deeper understanding, see the [Rust's ownership model](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) in the official Rust Book. **Fearless concurrency.** Vehicle computers push data constantly (speed, throttle position, battery state, sensor readings) across multiple threads. Rust's type system makes it impossible to share mutable state across threads without explicit synchronization. You can't accidentally introduce a data race. This means the development team can reason about concurrent behavior with certainty—no threads silently corrupting shared state in production. That certainty compounds as the codebase grows. For more details, explore [Rust's fearless concurrency model](https://doc.rust-lang.org/book/ch16-00-concurrency.html). **A strong FFI story.** Rust can expose a C-compatible interface, which means it integrates naturally with [Dart's FFI documentation](https://docs.flutter.dev/platform-integration/bind-native-code). You get the expressiveness and safety of Rust at the systems level, and the ergonomics of Dart and Flutter at the UI level, without an awkward glue layer in between. **Cross-platform compilation.** A Rust library compiles to native code on every major platform Flutter targets: iOS, Android, macOS, Linux, and Windows. Write the data layer once; deploy it everywhere. For teams shipping to multiple platforms, this reduces both code surface area and maintenance burden. Learn more about [Flutter's cross-platform capabilities](https://verygood.ventures/blog/top-companies-using-flutter/). **Explicit error handling.** [Rust's `Result` type](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html) forces you to handle errors at the call site. There are no unchecked exceptions that silently propagate and manifest as crashes at a layer far removed from the actual failure. When you're interfacing with hardware that can fail in a dozen ways, explicit error paths aren't a burden—they're a feature. ![Infographic showing five key advantages of Rust for Flutter data layers — Memory Safety, Fearless Concurrency, Strong FFI, Cross-Platform Compilation, and Explicit Error Handling — each in VGV Blue circular nodes](/assets/images/blog/rust-as-flutter-data-layer/body-1.avif) ## The Architecture in Practice The Toyota IVI project illustrates this pattern clearly. The system has three conceptual layers: 1. **The vehicle computer**: proprietary hardware pushing real-time signals. 2. **The Rust data layer**: responsible for consuming those signals, transforming them into structured data, and exposing a clean API to the layer above. 3. **The Flutter layer**: responsible for state management (consuming the Rust API) and rendering the UI. Flutter never knows the vehicle computer exists. It only knows about the structured data types and stream interfaces the Rust layer exposes. This separation has two important consequences. First, the Flutter team and the vehicle systems team can work independently. The interface contract between Rust and Flutter is defined in code; as long as both sides honor it, they don't need to be in the same room—or even on the same continent. This architectural separation enables [the Very Good Layered Architecture approach](https://verygood.ventures/blog/very-good-flutter-architecture/), where data, domain logic, and presentation layers are cleanly separated. Second, the Rust layer is independently testable. Business logic that lives in Rust can be unit tested in Rust, without a Flutter harness or an emulated device. This tightens the feedback loop for the development team and reduces the risk of integration-time regressions. See [independently testable systems](https://verygood.ventures/blog/flutter-tests-very-good-cli/) for VGV's testing philosophy. ![Three-layer architecture diagram — Vehicle Computer hardware at bottom feeding real-time data to a Rust Data Layer in VGV Blue, which exposes a clean API to the Flutter UI layer at top](/assets/images/blog/rust-as-flutter-data-layer/body-2.avif) ## The Packages That Make This Work Two libraries connect Rust and Flutter effectively. They take different approaches, and the right choice depends on your use case. ### membrane Toyota utilizes the [membrane](https://github.com/jerel/membrane) package in their IVI project. It generates Dart bindings from annotated Rust code, with first-class support for async streams—which is exactly what you need when consuming continuous data from a hardware source. The ergonomics are tight: you annotate your Rust functions and types, run the generator, and get idiomatic Dart on the other side. The generated code feels native to Flutter, not like a foreign interface bolted on. membrane is purpose-built for this pattern, which means it's opinionated in useful ways. Membrane was made specifically for IVI's architecture pattern: continuous data flowing from Rust into Flutter. Starting with the tool that matches your use case saves integration headaches later. If your use case involves real-time data flowing from a Rust backend into a Flutter UI, it's worth starting here. ### flutter_rust_bridge [flutter_rust_bridge](https://github.com/fzyzcjy/flutter_rust_bridge) is the broader, community-standard solution for adding Rust to Flutter projects. It supports a wide range of types, handles async operations, and integrates well with the Flutter build system across all major platforms. The documentation is thorough, the community is active, and the project is well-maintained. If you're starting a new project and want a battle-tested, general-purpose integration layer, `flutter_rust_bridge` is the right starting point. It lacks membrane's stream-first ergonomics but covers a wider range of use cases and provides more examples. ## When to Reach for This Pattern Rust as a Flutter data layer isn't the right answer for every application. If your data layer is a REST API and a local SQLite database, this is overkill. Rust as a Flutter data layer makes sense primarily when correctness is non-negotiable (crashes or data corruption have real-world consequences). The benefits compound if any of these are also true: - You're interfacing with hardware, sensors, or proprietary protocols. - You're targeting multiple platforms and want a single, unified systems implementation. - Your team already has Rust expertise, or the systems team is separate from the Flutter team. - You need predictable performance characteristics without GC interference. The Toyota IVI project sits at the intersection of all five. Your project probably won't. But if it hits even two or three of them, the architecture earns its complexity. Learn more about [VGV's scalable architecture best practices](https://verygood.ventures/blog/scalable-best-practices/). ## Where to Go From Here Architectural clarity compounds over time. Define the boundary between your systems and UI layers explicitly, rather than letting it blur as the codebase grows. The key insight is that this decision framework applies broadly—whether you're using Rust, C++, or another systems language, the architectural boundaries matter more than the specific tool. If you're building something that lives close to hardware, we'd encourage you to prototype the pattern before committing. Stand up a minimal Rust library, expose a few functions via `flutter_rust_bridge` or `membrane`, and integrate them into a Flutter app. The build tooling has matured considerably, and the path from zero to working is now short. At VGV, this is the kind of architecture problem we find genuinely interesting—the intersection of systems constraints and product experience, where the right technical decision has a direct effect on what a user sees and feels. If you're working on something in that space and want to think it through, we'd love to hear about it. Explore [VGV's work on enterprise Flutter projects](https://verygood.ventures/success-stories/) to see how we've applied architectural clarity in production systems. ### From IDEs to Agents: Claude Code Desktop Hands-On Review Source: https://verygood.ventures/blog/claude-code-desktop-hands-on-review/ ![Developer workspace evolving from a single IDE to a multi-agent orchestration layout — Claude Code Desktop review](/assets/images/blog/claude-code-desktop-hands-on-review/main.jpeg) Not long ago, [AI-assisted development](/blog/flutter-ai-development-wingspan-workflow/) meant autocomplete. An AI suggesting the next line, filling in function bodies, offering inline completions. The editor was the center of everything. You wrote code, and the AI helped you write it faster. That model is fading. In 2026, the question developers ask their tools has changed. It went from "help me write this function" to "build this feature while I review another PR." The tools that matter now are not the ones that complete your code. They are the ones that take over the typing entirely and let you focus on architecture, review, and direction. This shift built up gradually. Before we get into Claude Code Desktop specifically, it helps to understand the landscape that led here. ## The Rise of Agentic Development ### From Autocomplete to Orchestration The evolution happened in three waves. **Wave one: inline completions.** AI lives inside your editor as an extension. It watches what you type and suggests what comes next. You stay in full control. The AI is a faster autocomplete. This is where most developers first experienced AI-assisted coding. **Wave two: AI-native editors.** Tools forked VS Code and built AI deeper into the editor. Chat panels, multi-file edits, codebase-aware context. The AI could do more, but the editor remained the center of the experience. You were still driving. **Wave three: agentic environments.** The editor stops being the center of anything. The agent reads your codebase, reasons about architecture, writes code, runs tests, creates branches, and opens PRs. You direct and review. This is where Claude Code started as a terminal-first agent, and where Claude Code Desktop now picks up the story. A majority of developers now use AI coding tools, and major players across the industry are pivoting from editors to agent orchestration platforms. The question is no longer _whether_ development becomes agent-first. It is _what the interface for managing agents should look like_. That is the question Claude Code Desktop tries to answer. ### The Key Distinction Two paradigms have emerged. **Agentic IDEs.** AI bolted onto an editor. Developer-directed, step by step. Single agent, sequential execution. The human is involved at every step. **Agentic Development Environments (ADEs).** Built ground-up for agents. Goal-based, multi-agent, parallel execution in isolated environments. The human sets direction and reviews results. Claude Code Desktop falls in this category. Once a task touches 10+ files, spans multiple packages, or requires 3+ parallel agents, the ADE model fits better than an agentic IDE. In practice, most developers use both. IDE extensions handle the quick, in-context moments. Agent tools handle the bigger tasks you want to hand off entirely. ### Why Worktrees Matter Parallel agents need git worktrees. The reason is straightforward: an AI agent that edits files needs isolation. Two agents running in parallel cannot both write to the same working directory. They will step on each other's changes and create merge conflicts mid-task. Git worktrees solve this by checking out multiple branches of the same repository simultaneously, each in its own directory. Every agent gets its own workspace. They read, write, run tests, and commit independently, all without touching your main working tree. Claude Code has supported worktrees from the start with its built-in `/worktree` command. At VGV, we've seen teams go from single-agent workflows to running four or five agents in parallel, each working on a different feature branch. Our [Wingspan agentic engineering workflow](/blog/vgv-wingspan-agentic-engineering-workflow/) leans heavily on this pattern. The experience is closer to managing a small distributed team than pair-programming with a chatbot. Without worktrees, you are limited to one agent, one task, one branch at a time. With them, you can kick off three agents on three separate PRs and keep working on your own thing. Claude Code Desktop makes this pattern more visible than the terminal ever could. ### The Developer's Role Is Changing, Not Disappearing These tools do not remove the need for engineering judgment. They make that judgment the primary thing you contribute. Agents handle the typing, the boilerplate, the repetitive patterns. Code design, architecture decisions, and enforcing project conventions remain your responsibility. The developers getting the most out of agentic workflows are the ones who treat the agent as a capable executor while they focus on direction, review, and the decisions that require context no model has yet. That framing brings us to the tool we have been testing. --- ## Claude Code Desktop: Hands-On ### What It Is Claude Code Desktop is Anthropic's standalone app for [Claude Code](https://claude.com/blog/claude-code-desktop-redesign). The same AI-powered coding agent that previously lived in the terminal, now with a native desktop experience. Instead of managing agent sessions through tmux panes and terminal tabs, you get a purpose-built UI for orchestrating multiple coding agents at once. The feature set covers a lot of ground. A sidebar manages all active and recent sessions with filtering by status, project, or environment. A built-in terminal lets you run tests and builds alongside your agent sessions. An in-app file editor handles quick changes. A redesigned diff viewer is optimized for large changesets. A preview pane supports HTML, PDFs, and local dev servers. Drag-and-drop layout customization lets you arrange your workspace. A side chat (`⌘ + ;`) lets you branch conversations without derailing the main thread. ### First Impressions If you have been using Claude Code in the terminal, Claude Code Desktop does not give you new capabilities. The underlying agent, the MCP integrations, the worktrees, the skills: it is all the same engine. What changes is the surface. If you have ever tried to build an editor-like experience around your Claude Code terminal by splitting panes, using tmux to monitor sessions, and switching between diffs and logs, Claude Code Desktop is Anthropic packaging that up into a proper app. Slash commands become buttons. Diffs render inline with a proper viewer. Session management moves from your head to a sidebar with real status indicators. For developers already comfortable with the terminal, the value is convenience, not capability. For developers who found the CLI intimidating or wanted a more visual way to work with coding agents, it lowers the barrier to entry. ### What Works Well **Session status visibility.** Each session shows a clear indicator of its current state. Is the agent running? Does it need your attention for a decision? Has it finished and created a PR? When you run multiple agents in parallel, this matters. You glance at the sidebar, see that two sessions are progressing, one needs a quick approval, and one already has a PR ready for review. You never have to click into each session to check. ![Claude Code Desktop session sidebar showing multiple sessions with status indicators](/assets/images/blog/claude-code-desktop-hands-on-review/session-sidebar.png) **Parallel agent monitoring.** The experience works well when running multiple sub-agents within a single session. In the screenshot below, four review agents (VGV standards, code simplicity, test quality, and architecture review) run simultaneously. Each one shows its current operation, elapsed time, token usage, and tool call count. ![Four parallel review agents running in Claude Code Desktop showing status, token usage, and tool calls](/assets/images/blog/claude-code-desktop-hands-on-review/parallel-agents-running.png) **Context window and usage visibility.** The usage panel breaks down what consumes your context window: MCP tools, messages, system tools, skills, system prompt, memory files, autocompact buffer, and free space. Each category shows token counts and percentages. You can spot an MCP server bloating your context, see how close you are to your 5-hour rate limit, and decide when to compact or start a fresh session. ![Claude Code Desktop context window usage panel showing token breakdown by category and plan usage limits](/assets/images/blog/claude-code-desktop-hands-on-review/context-usage-panel.png) **Plan and task windows.** Plans and background agent tasks open as separate windows. Instead of scrolling through terminal output, you get a dedicated view into the agent's plan and the tasks it executes. This is useful when reviewing what an agent is doing before approving a larger change. **Built-in terminal.** Having a terminal inside the app removes one of the main reasons to switch away. You can run tests, check build output, or inspect logs without leaving the context of your agent session. It sounds minor, but when you are reviewing an agent's changes and want to verify something quickly, not having to switch windows keeps you focused. **Side chat.** The side chat (`⌘ + ;`) lets you branch off a quick question without interrupting the main session thread. If the agent is mid-task and you want to ask something unrelated, you open a side chat instead of derailing the conversation. It keeps the main session focused. ![Claude Code Desktop side chat for branching conversations without interrupting the main agent session](/assets/images/blog/claude-code-desktop-hands-on-review/side-chat.png) **PR controls.** Once a session creates a PR, controls appear at the top of the session that let you address review comments or check whether CI is passing. You stay inside Claude Code Desktop instead of switching to GitHub. For teams that move fast between agent output and PR review, this keeps the loop tight. ![Claude Code Desktop PR controls showing CI status and review comment actions at the top of a session](/assets/images/blog/claude-code-desktop-hands-on-review/pr-controls.png) ### Claude Code Best Practices We've Kept Moving from the CLI to the desktop app does not change the habits that make Claude Code effective. The same practices apply. Keep sessions narrowly scoped to a single task. Use worktrees to isolate parallel work. Review plans before approving execution. Watch the context window and compact early rather than late. Let the agent write tests alongside the code it generates. The desktop app just makes each of these easier to see and act on. If anything, the visibility nudges the team toward better defaults — the usage panel makes context bloat obvious in a way the terminal never did. ### What Could Be Better **Diff viewer gaps.** The built-in diff viewer handles code well, but it does not support everything. Images do not render in the diff view. If an agent adds or modifies an image file, you see the path changed but cannot preview what it looks like. There is also no way to preview files directly from the diff. When you are reviewing a blog post or a UI change that includes assets, you still need to open a separate tool to see the full picture. For a desktop app that aims to keep you in one place, these gaps send you back to your file browser or IDE more often than expected. **Limited git controls.** Most git operations are left to the agent. That works for simple commits and branches, but not every workflow wants the agent driving git. Sometimes you want to stage specific hunks, amend a commit, or rebase manually without writing a prompt for it. Surfacing common git actions as buttons or shortcuts would give teams more control over the parts of the workflow they do not want to delegate. **Customizable action buttons.** The desktop app ships with built-in buttons like "create PR" that trigger Anthropic's own skills. There is no way to swap those for your own. At VGV we have a custom create-PR skill with our own commit conventions, PR template, and review checklist. Being able to wire that into the same button instead of running it as a separate command would make the app feel like ours, not just Anthropic's. **Side panel organization.** The separate plan and task windows work in isolation, but the layout breaks down at scale. Every panel opens to the side. After two or three are open, the screen gets cluttered. You spend time managing windows instead of reviewing agent output. These panels are also scoped per session. When running several sessions, you end up with overlapping panels and no clear way to navigate between them. A tabbed or docked layout would improve this. ## Final Thoughts Claude Code Desktop is a solid first version of what feels like an inevitable direction. The industry is moving from editor-first to agent-first, and every tool needs an answer to one question: how do I manage multiple agents working on my codebase at the same time? Anthropic's answer is a dedicated app with session management, status indicators, built-in diffs, and a layout system. It does not add new capabilities over the CLI. It adds accessibility. For a tool built around multiplying developer output, making it easier for more developers to use is a meaningful step. Session creation needs fewer clicks. The side panels need better organization. Power users of the CLI will not find new capabilities here. But the app makes parallel agent workflows more visible and more manageable. At VGV, we have been using Claude Code as part of our daily engineering workflow for months. The desktop app does not change what we can do. It changes how approachable those workflows are for the rest of the team. That matters. ### Branching Out: Being a PM Who Gets GitHub Source: https://verygood.ventures/blog/branching-out-being-a-pm-who-gets-github/ Program management in software and product development lives or dies by the quality of conversations you have with your engineering team. The best ones happen while the work is in flight — not in standup the morning after something shipped, and not in a retrospective two weeks later. They happen in the pull request, while decisions are still being made, while scope is still being shaped, while trade-offs are still on the table. It turns out that joining those conversations doesn't require writing code, or even opening a terminal. What it takes is a mental model for how engineers collaborate in GitHub, a handful of vocabulary words, and the confidence to click around a repository on the web UI. With this post I hope to break down some barriers to illustrate how the development process can be more collaborative across disciplines, written for the next PM who wants in on the work in flight. By the end, you'll be able to track what's happening in real time, read a pull request without skimming a single line of code, and engage with engineers as a peer — no need to "herd cats" to get answers, because you'll be in the discussion. And make no mistake: you belong in that discussion. As the PM, no one on the team understands the client's goals more deeply than you do. That perspective is exactly what the pull request is waiting for — and bringing it to the conversation, while the work is still being shaped, is how you make sure the team is building the right thing, the right way. ## The Mental Model ### Git is not GitHub The single most useful thing I learned early on: Git and GitHub are not the same thing. People use the words interchangeably, and that's where most of the confusion starts. Git is the tool. GitHub is the workplace where teams use that tool together. As a PM, you'll spend almost all your time in GitHub — the website — and almost none of it in Git itself. | Git | GitHub | | :---- | :---- | | A version control system (the technology) | A platform (the website and company) | | Runs on an engineer's computer | Lives in the cloud | | Tracks every change to every file | Hosts that history online so teams can collaborate | | Works fine by itself | Adds pull requests, reviews, issues, and project boards | ![Diagram comparing Git the version control tool running locally against GitHub the cloud collaboration platform, explained for product managers](/assets/images/blog/branching-out-being-a-pm-who-gets-github/nano-banana-2.avif) Once understanding this distinction, two concepts did most of the heavy lifting for me: A **repository** (or "repo") is a project's entire filing cabinet. Every file, every version, and the full history of changes live inside it. It is the single source of truth for the project's code. A **branch** is a safe parallel copy of the project where changes happen before they go live. The closest analogy is Google Docs Suggesting mode, but more powerful — multiple people can work simultaneously without collisions. Branches let the team review and approve changes before they affect anyone else. Almost everything else in Git and GitHub sits on top of those two ideas. ## Why It's Worth the Investment Three things changed for me once I started being comfortable in GitHub, and all of them saved time — my most precious commodity. **I could track progress.** Once I learned to navigate GitHub on my own, I could see what engineers were working on and when features actually landed — without asking in standup. Commit history turned out to be a more honest source of truth than our Jira board. **I could join the conversation.** [Pull requests](https://verygood.ventures/blog/building-better-software-the-importance-of-rigorous-code-reviews/) are where code review, scope debates, and implementation decisions happen. Reading them — and occasionally commenting — gave me context I wasn't getting anywhere else. **I could unblock myself.** [Documentation kept alongside the code](https://engineering.verygood.ventures/development/documentation/documentation/), configuration, feature flags, and project details usually lived in the repository. Once I could find them directly, I stopped waiting on engineers for questions I could answer myself. ## The Vocabulary, and What It Actually Signals The surface definitions are a Google search away. What took me longer to learn was how to read **each term as a signal** — what it tells you about a team's pace, posture, and health. ![Abstract project health dashboard showing branch, commit, push, merge, and tag indicators as GitHub signals for program managers](/assets/images/blog/branching-out-being-a-pm-who-gets-github/nano-banana-3.avif) The **README** is a repository's front door: a document that introduces the project and explains its purpose, setup, and usage so anyone can understand the codebase and contribute to it. **It's also a health signal.** A thin or outdated README on an active project tells you the team is shipping faster than they're documenting — a risk worth flagging, not a nitpick. A strong README (setup, architecture, onboarding, key decisions) correlates with a team thinking beyond the current sprint. Commits are save points for developers, **but the structure and size are the signals to follow.** Small, well-scoped commits reflect deliberate tasks. A wall of `wip`, `fix`, and `more fix` usually signals the work is exploratory or in flux—expect scope to shift. **Push** and **pull** are sync operations, but **looking at the staleness is a signal to catch**. A branch that hasn't been pushed in three days may be blocked or abandoned. A burst of rapid pushes late at night before a demo signals a rushed finish—worth a gentle check-in about test coverage. A **merge conflict** is a **coordination signal.** Occasional conflicts are noise. Recurring conflicts on the same files mean multiple people are working in the same zone without alignment — a [technical problem engineers resolve in the moment](https://www.atlassian.com/git/tutorials/using-branches/merge-conflicts), but also a planning conversation worth surfacing. A **tag** or **release** is the team's public declaration and **the signal that something shipped.** Release cadence is one of the most honest measures of delivery health, so follow the clues to understand when things are being pushed. The result? I started speaking the language. "Is the PR merged yet?" replaced waiting for a status summary. **My standups got shorter. My Slack threads got sharper.** ## How to Read a Pull Request Without Reading the Code Once you've got the lingo, the next place to focus is where the action actually happens: the pull request. The PR is [a proposal to merge changes from one branch into another](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). What matters for a PM is how to read one for information that isn't in the code. I start with metadata: - **Draft vs. ready for review.** A draft PR is the earliest signal of a feature in progress; subscribe to these. - **Reviewer assignment.** Who is tagged shows who the author considers the change authority. Unexpected senior engineer involvement signals non-routine scope, architecture, or risk. - **Labels and milestones.** Labels like `needs-product-input` or milestone attachment are direct requests for PM attention. - **Linked issues and projects.** A PR lacking a linked ticket is either trivial, tech debt, or a tracking system gap, which warrants a comment. The **conversation tab** is where I spend most of my time. Things I watch for - A reviewer's question hanging unanswered for a day ("should this also handle guest checkout?") — that's a scope call, not an engineering one. **Weigh in.** - A PR **open more than a few days with no movement** — usually stuck on a busy reviewer, an unescalated scope question, or a dependency. Worth surfacing. - A **missing or wrong linked ticket** — easy to fix, but it quietly breaks reporting if left. ![GitHub pull request conversation tab showing reviewer comments, scope questions, and PM responses, demonstrating how product managers read pull requests without reviewing code](/assets/images/blog/branching-out-being-a-pm-who-gets-github/nano-banana-4.avif) When I do comment, I stay in my lane: not on implementation, but on scope ("is this covering the edge case we discussed?"), linked tickets ("this isn't tied to `ATLAS-412` — should it be?"), and timing ("is this still in the Thursday release?"). One well-placed PM comment usually unblocks a conversation faster than a Slack thread. ### The PR Status Cheat Sheet PRs have four states worth knowing: **Approved** (reviewers have signed off, ready to merge), **Changes requested** (a reviewer found issues, the author needs to update), **Merged** (changes are live, feature has landed), and **Closed** (abandoned or superseded by another PR). Those statuses tell me, at a glance, where every piece of work stands. ## GitHub is the PMs Safe Zone The GitHub web interface is your window into any project — and the good news is that almost everything useful to a PM is one click away. **Viewing and commenting can never break anything.** Pull requests exist precisely as a safety net. A few key items: The **README** is displayed on the repo homepage. Project overview, setup instructions, and key links all live there. Always start here. The **file browser** lets you click through folders to find documentation, configuration, or assets. [Our own documentation standards](https://engineering.verygood.ventures/development/documentation/documentation/) cover what belongs in a project's docs. The search bar jumps you to files by name, which is usually faster than clicking. The **commit history** is a timeline of every change. You can see who worked on what and when features were actually developed. That's my source of truth for status updates and retrospectives. [**Issues and Projects**](https://docs.github.com/en/issues/planning-and-tracking-with-projects) track bugs, features, and tasks. Project boards give you a Kanban-style view of what's in progress. Some teams use this instead of a dedicated PM tool. The pattern: anything *read-only* or *additive* (a comment, an issue, a docs edit) is yours to use. Anything that changes the shared state — what's merged, what's deleted, what enforces the team's rules — most likely belongs to engineering (for now\!) ### Why Living in the Repo Beats Waiting for Status Once I was comfortable navigating a repo, I stopped relying on status meetings and async pings to understand where development stood. The repo itself is the most accurate, real-time source of truth you'll find on any project — and it's available 24/7 without interrupting anyone. ![Circular workflow diagram showing a product manager's daily GitHub routine of monitoring pull requests, unblocking engineers, and communicating progress](/assets/images/blog/branching-out-being-a-pm-who-gets-github/nano-banana-5.avif) Here's what five to ten minutes a day in GitHub gave me: - **I protect the team's focus.** By monitoring the PR queue and answering stakeholder questions based on code progress, I absorb the context-switching load, letting engineers maintain deep work and velocity. - **I clear the path for delivery.** When a PR is stalled due to a confusing CI/CD failure, a complex review dependency, or a lingering decision, I step in to unblock the engineer and facilitate the conversation or escalate the challenge. - **I champion the team's quality standards.** Reviewing PR comments and status checks ensures our definition of "done"—including testing, documentation, and code quality—is being consistently met, helping engineers maintain a high bar. - **I build shared understanding and trust.** Using the PR as the single source of truth, I proactively communicate progress to stakeholders, manage expectations, and shield the team from unnecessary check-in requests. - **I invest in our process health.** Patterns like frequently failing tests, long-running review cycles, or common miscommunications surface in PR insights. I can propose and facilitate workflow improvements based on what I see. The habit is small. The payoff: I'm never more than a day out of date on any project, and my team spends less time feeding me information I could find myself. ## You've Got This This week, try three things: - Browse a repository and read the README. - Leave a comment on an active PR — even if it's just a question. - Edit a Markdown document directly in the GitHub UI. You don't need to be an engineer to use GitHub effectively. You just need to start. That's what I did — and the more I looked around, the more natural it got. A month in, I was the PM who knew where features actually were, who read PRs without waiting for a summary, and who spoke the same language as the people building the product. That's a different kind of program manager. And it started with opening a tab and clicking around. ### Design Systems at AI Speed: A Figma-to-Flutter Skill with Golden Tests and Widgetbook Validation Source: https://verygood.ventures/blog/figma-to-flutter-claude-code-skill-golden-tests/ We joined a native-to-Flutter migration with a mature design system. Dozens of components in Figma, each with specific spacing tokens, color mappings, typography scales, and interaction states. Translating those into production-ready Dart widgets is repetitive work. Spacing values, color tokens, file scaffolding, golden tests, Widgetbook use cases, barrel updates. Multiply that across forty components and you have weeks of low-judgment, high-detail work. That's the exact profile of work an AI agent should handle. The engineer reviews the output instead of writing boilerplate. So I built a Claude Code skill for it. This post walks through the Figma to Flutter workflow we developed, why generic AI prompting falls short for design system automation, and how the skill closes the gap between an AI-generated widget and a mergeable PR. ## Why Generic AI Prompting Falls Short The naive approach: paste a Figma screenshot into an AI chat, ask for a Flutter widget. The output looks close but isn't production-ready. Hardcoded color values instead of `ColorScheme`. Invented spacing constants instead of the project's `AppSpacing`. No awareness of the existing component library. No tests. This is also where plugin-based Figma to Flutter tools (Visual Copilot, FlutterFlow, and similar) hit their ceiling. They produce literal code without theme awareness, test coverage, or documentation. A structured skill operates differently. It treats Flutter design system automation as a workflow, not a one-shot generation step. The gap between AI-generated widget and mergeable PR is where the real work lives. Design token integration, theme compliance, reuse, visual validation, documentation. A structured skill can close it. ## The Core Idea: Deterministic Steps, AI Judgment, Human Gates ![Three-pillar diagram of deterministic steps, AI judgment, and human gates in the Figma to Flutter Claude Code skill](/assets/images/blog/figma-to-flutter-claude-code-skill-golden-tests/three-pillars.png) The skill separates what should be deterministic from what requires judgment. This is the architectural pattern behind the workflow, and it's a useful lens for Claude Code best practices generally. **Deterministic:** fetching the Figma design context via the MCP server, extracting node IDs from URLs, scaffolding file structures, generating golden test boilerplate, updating barrel files. These have a "right answer" defined by project conventions. **AI judgment:** deciding which Flutter built-in widget to use (`FilledButton` vs. a custom component), composing the widget tree from Figma's layout properties, mapping design tokens to the project's theme system, choosing when to reuse versus build new. **Human gates:** the engineer reviews every generated widget before it merges. The skill produces the implementation, golden tests, and Widgetbook use cases. The engineer then decides whether the output meets the bar. This isn't autonomous code generation. It's structured AI assistance with explicit review points. ## The Figma to Flutter Workflow The skill executes six phases. Here's how. ### Phase 1: Fetch the Design Context The engineer provides a Figma URL. The skill extracts the file key and node ID, then calls the [Figma MCP server](https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server) twice. First for `get_design_context`, which returns structured layout data. Spacing, typography, colors, component hierarchy. Second for `get_screenshot`, the visual reference that becomes the benchmark for the rest of the process. For complex components where the context response is too large, the skill falls back to `get_metadata` for a high-level node map, then fetches individual child nodes separately. For broader context on MCP Flutter integration, see our guide to the [7 MCP servers every Dart and Flutter developer should know](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/). ### Phase 2: Map Design Tokens to the Project's Theme This is the highest-leverage step. Generic AI prompts skip it entirely. Before generating any widgets, the skill maps Figma's design tokens to the project's existing theme system. Colors map to `ColorScheme` properties. Custom colors that fall outside the standard palette go into `ThemeExtension`. Typography maps to `TextTheme`. Spacing maps to `AppSpacing` constants. The practical effect is that every generated widget supports light mode, dark mode, and future theme changes out of the box. For deeper context on this approach, see our post on [mastering scalable theming for custom widgets](https://verygood.ventures/blog/mastering-scalable-theming-for-custom-widgets/) and the [VGV Engineering theming reference](https://engineering.verygood.ventures/development/ui/theming/). Instead of: ```dart // Generic AI output. Hardcoded values. Container( padding: EdgeInsets.all(16), color: Color(0xFF1B5E20), child: Text('Hello', style: TextStyle(fontSize: 14)), ) ``` The skill produces: ```dart // Skill output. Theme-integrated. Padding( padding: const EdgeInsets.all(AppSpacing.lg), child: ColoredBox( color: Theme.of(context).colorScheme.primary, child: Text('Hello', style: Theme.of(context).textTheme.bodyLarge), ), ) ``` Every value traces back to the theme. No orphaned magic numbers. For teams handling Figma styles export at scale, the [Very Good Flutter Styles plugin](https://verygood.ventures/blog/very-good-plugins-very-good-flutter-styles/) takes a complementary approach to the same problem. ### Phase 3: Translate to Idiomatic Flutter Widgets The skill translates Figma's layout model into Flutter's widget tree. Auto Layout becomes `Row` or `Column`, Fill Container becomes `Expanded`, Hug Contents becomes `MainAxisSize.min`. Three rules keep the output production-quality. **Prefer Flutter's built-in widgets.** If the Figma design shows a button, the skill checks whether a styled `FilledButton` or `OutlinedButton` can match the design through `ThemeData` before creating a custom widget. Same for `Divider`, `Chip`, `Card`, `ListTile`. Recreating built-in widgets from `Container` and `InkWell` is a common AI mistake the skill explicitly avoids. **Reuse existing components.** Before creating anything new, the skill scans the project's UI package for matching components. If an `AppButton` already exists, it extends it with new variants rather than duplicating. This is critical on a migration project where widgets accumulate over time. **Implement only what Figma defines.** If a component has one size and one state in Figma, the skill implements one size and one state. No invented variants, no speculative API surface. This keeps the output focused and reviewable. The skill extracts parts of the widget tree into private `StatelessWidget` classes rather than `_buildX()` helper methods. This is a VGV convention that enables const constructors, independent rebuilds, and better testability. ### Phase 4: The Visual Comparison Loop ![Iterative AI visual comparison loop between a Figma reference and a rendered Flutter widget on a smartphone](/assets/images/blog/figma-to-flutter-claude-code-skill-golden-tests/visual-comparison-loop.png) Instead of generating golden tests and hoping for the best, the skill runs an iterative visual comparison loop. It renders the widget, reads its own output with vision capabilities, compares it against the Figma screenshot, and adjusts until the two match. This closed-loop validation is the most distinctive part of the workflow. The mechanics. The skill generates a throwaway Alchemist test (Alchemist is VGV's golden-testing library) with `autoUpdateGoldenFiles = true`, runs it to produce a PNG, then reads that PNG and evaluates it against the Figma reference from Phase 1. It checks layout, typography, colors, corner radii, shadows, and asset rendering. Any discrepancies trigger iteration. The loop continues until parity is achieved. Once visual parity is confirmed, the throwaway test file and its golden artifacts are deleted. They served their purpose: giving the AI eyes to validate its own work. For a complementary take that pairs the Figma MCP with Maestro for runtime verification, see our post on building [pixel-perfect Flutter UIs with Figma, Maestro, and Dart MCPs](https://verygood.ventures/blog/pixel-perfect-flutter-designs-with-figma-and-maestro/). ### Phase 5: Golden Tests and Widgetbook Use Cases The visual comparison loop handles immediate validation. For long-term visual regression protection, the skill offers two options. Neither is mandatory. This reflects Flutter testing best practices we've refined across migration projects. **Golden tests are optional.** The skill asks the engineer before generating them. For core buttons and typography scales, permanent Alchemist golden tests catch regressions in CI automatically. For components still in active iteration, they create noise. The engineer decides. ```dart goldenTest( 'renders correctly', fileName: 'app_button', tags: ['golden'], builder: () => GoldenTestGroup( children: [ GoldenTestScenario( name: 'default', child: Theme( data: AppTheme.light, child: const AppButton( label: 'Click me', onPressed: _noop, // _noop is an empty callback defined in the test file. ), ), ), ], ), ); ``` **Widgetbook use cases** are generated whenever the project uses Widgetbook. Each use case links back to the original Figma design URL and includes interactive knobs, so designers and developers can explore the component without navigating away. ```dart @UseCase( designLink: 'https://figma.com/design/...', name: 'Playground', type: AppButton, ) Widget appButtonPlayground(BuildContext context) { final label = context.knobs.string( label: 'Label', initialValue: 'Click me', ); final isEnabled = context.knobs.boolean( label: 'Enabled', initialValue: true, ); return Theme( data: AppTheme.light, child: AppButton( label: label, onPressed: isEnabled ? () {} : null, ), ); } ``` The visual comparison loop confirms parity at implementation time, golden tests guard against regressions in CI, and Widgetbook gives designers a live environment to confirm the implementation matches their intent. ### Phase 6: Self-Validate and Report The skill runs `flutter test` and `flutter analyze`, then compiles a report. What was implemented, what Figma states were covered, any unresolved items. Unresolved items like missing assets, unmatched fonts, or slight shadow mismatches get `// TODO:` comments in the code and a summary in the report. The skill surfaces problems explicitly so the reviewing engineer knows what needs attention. ## What We Learned Building and iterating on this skill across a real migration project surfaced several practical lessons. **AI vision closes the loop, mostly.** The skill reads its own rendered output and compares it against the Figma screenshot using vision capabilities. This eliminates manual eyeballing. Vision-based comparison has limits. It catches layout mismatches and color discrepancies reliably, but subtle issues like 1px borders or slight shadow spread variations can slip through. The next frontier: pairing AI vision with programmatic pixel-diff scoring for true quantitative validation. That's the single biggest remaining gap in the workflow. **Reuse compounds over time.** Early in the migration, most components are new. As the UI kit grows, the skill spends less time generating widgets and more time composing existing ones. A card component that would be built from scratch in week one gets assembled from `AppAvatar`, `AppBadge`, and `AppButton` in week six. The skill's instruction to check existing components before creating new ones is what makes this compounding work. We saw a similar dynamic in [how Hespress aligned Figma designs directly with Flutter code](https://verygood.ventures/success-stories/hespress-news-app/) on their team. **Theme integration up-front pays off later.** Mapping every value to `ColorScheme`, `TextTheme`, and `AppSpacing` in Phase 2 means light mode, dark mode, and future rebrands work without rework. Skipping this step is how AI-generated widgets become orphaned code. ## Where This Fits in VGV's Flutter AI Engineering Stack This Figma to Flutter skill is one piece of a stack we've been building across VGV projects. Each piece solves a different problem at a different scale. The **VGV AI Flutter Plugin** (open source) encodes our production conventions directly at the code level. It's the foundation: consistent architecture, consistent patterns, consistent quality, enforced by the tooling itself. **VGV Wingspan** (open source) structures the workflow around those conventions. Where the plugin handles the *what*, Wingspan handles the *how*. Orchestrating multi-step engineering tasks with the right sequence, the right context, and the right validation at each stage. **[VGV Arcana](https://verygood.ventures/resources/flutter-ai-engineering/)** scales the conventions and workflow across entire organizations. Native-to-Flutter migrations, comprehensive codebase assessments, Figma to code at scale. The kind of challenges where you need conventions, workflow, and the ability to apply them across dozens of projects and teams simultaneously. The Figma to Flutter skill in this post is part of Arcana. The pattern generalizes. Any repeatable engineering workflow with clear structure and well-defined quality criteria works as a skill. The key design choice: where to draw the line between what the AI handles and where the human reviews. For design system implementation, the line is clear. The AI maps tokens, generates widgets, writes tests, and scaffolds use cases. The engineer validates visual fidelity, confirms the component fits the broader system, and decides when it merges. The engineer's time shifts from transcribing spacing values to reviewing and refining output. That's where engineering judgment actually matters. I plan to cover the other Arcana skills in upcoming posts. The same pattern of deterministic steps, AI judgment, and explicit human review applies across the rest of the migration workflow. ### How to Use Browser DevTools with Flutter WebViews Source: https://verygood.ventures/blog/how-to-use-browser-dev-tools-with-flutter-webviews/ If your Flutter app uses WebViews, you've probably hit a wall where something goes wrong inside the web content and you have no idea why. A network request silently fails, JavaScript throws an error you can't see, or the layout looks broken and you're left guessing. The good news: you can use the same browser developer tools you already know — Chrome DevTools on Android and Safari Web Inspector on iOS — to debug WebView content inside your Flutter app. Full console, network inspection, DOM editing, breakpoints, and all. This post walks through the complete setup, the common gotchas that trip people up (especially on iOS), and a demo app you can use to try every debugging technique hands-on. ## The Demo App We built a Flutter app with five interactive scenarios, each designed to exercise a specific DevTools tab: | Scenario | DevTools Tab | What It Demonstrates | | --- | --- | --- | | Console Logging | Console | `console.log`, `warn`, `error`, `table`, `group`, `trace` | | Network Requests | Network | `fetch`, `XMLHttpRequest`, headers, timing, error states | | DOM Inspection | Elements | Element selection, live CSS editing, CSS variables, animations | | JS Debugging | Sources | Breakpoints, step-through, call stack, closures, `debugger;` | | Error Debugging | Console | Error types, stack traces, caught vs uncaught, promise rejections | The full source code is available in [this repository](https://github.com/taaha161/webview_debug_demo). For the rest of this post, we'll reference specific code from the app as we walk through each setup step and debugging technique. ## Prerequisites - Flutter SDK 3.18+ - A physical Android device or emulator (for Chrome DevTools) - A physical iOS device or simulator + a Mac with Safari (for Safari Web Inspector) - The `webview_flutter` package (official, maintained by the Flutter team) ## Step 1: Project Setup ### Add the dependencies ```yaml # pubspec.yaml dependencies: flutter: sdk: flutter webview_flutter: ^4.10.0 webview_flutter_android: ^4.3.0 webview_flutter_wkwebview: ^3.16.0 ``` Run `flutter pub get` to install. The main `webview_flutter` package provides the cross-platform API. The two platform packages (`webview_flutter_android` and `webview_flutter_wkwebview`) are needed to access platform-specific features like enabling debugging and setting WebViews as inspectable. The platform packages are already transitive dependencies of `webview_flutter`, but adding them as direct dependencies gives you access to their platform-specific classes. ## Step 2: Platform Configuration ### Android: Internet permission WebViews that make network requests need the `INTERNET` permission. Add it to `android/app/src/main/AndroidManifest.xml`: ```xml ` tag. ### iOS: App Transport Security By default, iOS blocks HTTP (non-HTTPS) requests from WebViews. If your web content needs to reach HTTP endpoints, add this to `ios/Runner/Info.plist`: ```xml NSAppTransportSecurity NSAllowsArbitraryLoads NSAllowsArbitraryLoadsInWebContent ``` > **Note:** For production apps, you should use more specific ATS exceptions rather than allowing all arbitrary loads. This blanket setting is appropriate for development and demo purposes. ## Step 3: Enable WebView Debugging in Code This is the critical step that most tutorials gloss over. There are **two separate mechanisms**, one for each platform, and both are accessed through the platform-specific packages. ### Android: `AndroidWebViewController.enableDebugging` On Android, WebView debugging is a **global, static setting**. You call it once, typically in `main()`, and it applies to every WebView in your app: ```dart import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); if (WebViewPlatform.instance is AndroidWebViewPlatform) { AndroidWebViewController.enableDebugging(true); } runApp(const MainApp()); } ``` Key points: - This maps to Android's native `WebView.setWebContentsDebuggingEnabled(true)` - The platform check (`is AndroidWebViewPlatform`) ensures it only runs on Android - You must call this **before** any WebView is created - It affects **all** WebViews in the app, not just one ### iOS: `setInspectable` per WebView iOS works differently. Starting with **iOS 16.4**, Apple changed the default behavior: WebViews are **no longer inspectable** unless you explicitly opt in. This is a per-WebView setting, not a global one. After creating your `WebViewController`, cast the platform controller and call `setInspectable`: ```dart import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; // Create the controller with platform-specific params late final PlatformWebViewControllerCreationParams params; if (WebViewPlatform.instance is WebKitWebViewPlatform) { params = WebKitWebViewControllerCreationParams( allowsInlineMediaPlayback: true, ); } else { params = const PlatformWebViewControllerCreationParams(); } final controller = WebViewController.fromPlatformCreationParams(params); // Enable inspection on iOS 16.4+ if (controller.platform is WebKitWebViewController) { (controller.platform as WebKitWebViewController).setInspectable(true); } ``` This is the single most common reason people report "Safari Web Inspector can't see my WebView." Before iOS 16.4, inspection was always enabled in debug builds. After iOS 16.4, you must call `setInspectable(true)` on **each individual WebView instance**. > **On iOS simulators**, WebViews are always inspectable regardless of this flag. So you might think everything is working in the simulator, then get confused when it breaks on a real device. Always test inspection on a physical device. ## Step 4: Connect DevTools ### Android: Chrome DevTools 1. **Enable USB debugging** on your Android device: - Go to **Settings > About phone** - Tap **Build number** 7 times to enable Developer Options - Go to **Settings > Developer options** - Enable **USB debugging** 2. **Connect your device** via USB to your development machine. 3. When prompted on the device, tap **Allow** to authorize USB debugging from your computer. 4. **Run your Flutter app**: ```bash flutter run ``` 5. **Open Chrome** on your development machine and navigate to: ```text chrome://inspect/#devices ``` 6. Ensure **"Discover USB devices"** is checked. 7. Your device should appear in the list, with each WebView shown under it. Click **"inspect"** next to the WebView you want to debug. A full Chrome DevTools window opens, connected to your WebView. You now have access to Console, Network, Elements, Sources, and everything else. > **Emulator note:** `chrome://inspect` works with emulators too, but the connection can be flaky. For reliable demos, use a physical device. ### iOS: Safari Web Inspector Safari Web Inspector requires a **Mac** — there is no Windows or Linux equivalent. 1. **Enable Web Inspector on your iOS device**: - Go to **Settings > Safari > Advanced** - Toggle **Web Inspector** ON > This setting is easy to miss. It's not under the main Safari settings — you need to scroll to the bottom and tap **Advanced**. 2. **Enable the Develop menu in Safari on your Mac**: - Open **Safari > Settings** (or press `Cmd + ,`) - Go to the **Advanced** tab - Check **"Show features for web developers"** This adds a **Develop** menu to Safari's menu bar. 3. **Connect your iOS device** via USB to your Mac (or use the same Wi-Fi network for wireless debugging). 4. **Run your Flutter app** on the iOS device: ```bash flutter run ``` 5. In Safari on your Mac, go to **Develop > [Your Device Name]**. You should see your app and its WebView listed. Click on it to open Web Inspector. > **If your device doesn't appear in the Develop menu:** > - Make sure the device is unlocked > - Try disconnecting and reconnecting USB > - Restart Safari > - Confirm `setInspectable(true)` was called on your `WebViewController` (iOS 16.4+) > - Confirm Web Inspector is enabled in device Settings ## Step 5: Debugging in Practice Now that DevTools is connected, let's walk through what you can actually do. Each section corresponds to a scenario in our demo app. ### Console logging **DevTools tab: Console** The Console tab captures all JavaScript `console.*` calls from your WebView. This is your first stop for understanding what's happening inside the web content. Our demo provides buttons that exercise every console method: ```javascript // Basic log levels -- each gets a different icon/color in DevTools console.log('Standard message'); console.warn('This will show a yellow warning icon'); console.error('This shows red with a stack trace'); console.info('Informational message'); // Structured data console.table([ { name: 'Alice', role: 'Engineer', level: 'Senior' }, { name: 'Bob', role: 'Designer', level: 'Mid' }, ]); // Grouped output for readability console.group('API Request Lifecycle'); console.log('1. Request initiated'); console.log('2. Headers set'); console.groupEnd(); // Performance measurement console.time('Heavy Operation'); // ... do work ... console.timeEnd('Heavy Operation'); // Stack traces console.trace('How did we get here?'); ``` **What to try in DevTools:** - Filter by log level (click the icons next to "Default levels") - Click on logged objects to expand and inspect them - Use `console.table` output — it renders as an actual sortable table - Right-click a log entry and select "Store as global variable" to interact with it in the Console ### Network requests **DevTools tab: Network** The Network tab shows every HTTP request the WebView makes, with full request/response details. Our demo fires various types of requests: ```javascript // Simple GET -- inspect response body and headers await fetch('https://jsonplaceholder.typicode.com/posts/1'); // POST with payload -- inspect the request body await fetch('https://jsonplaceholder.typicode.com/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: 'Debug Demo Post', body: 'Created from WebView debug demo.', userId: 1 }) }); // Slow request -- watch the timing bar in the waterfall await fetch('https://httpbin.org/delay/3'); // Parallel requests -- see the waterfall visualization await Promise.all([ fetch('https://jsonplaceholder.typicode.com/posts/1'), fetch('https://jsonplaceholder.typicode.com/posts/2'), fetch('https://jsonplaceholder.typicode.com/users/1'), fetch('https://jsonplaceholder.typicode.com/comments/1'), fetch('https://jsonplaceholder.typicode.com/todos/1'), ]); ``` **What to try in DevTools:** - Click any request to see its **Headers**, **Payload**, **Response**, and **Timing** tabs - The **Waterfall** column shows how long each request took and whether they overlapped - Filter requests by type (Fetch, XHR, JS, CSS, etc.) - Right-click a request and select **"Copy as cURL"** to replay it in your terminal - Throttle the connection (Network tab > throttle dropdown) to simulate slow networks ### DOM inspection **DevTools tab: Elements** The Elements tab lets you inspect and live-edit the DOM and CSS of your WebView content. This is especially useful when debugging layout issues in hybrid apps. Our demo includes a rich layout with multiple CSS techniques: ```html
JD

Jane Developer

Senior Engineer
``` **What to try in DevTools:** - Click the **inspect cursor** (top-left icon) and tap any element on the WebView to jump to it in the DOM tree - Edit CSS properties in the **Styles** panel — changes apply instantly - Change CSS variables on `:root` to restyle the entire page (try changing `--primary` to `#E91E63`) - Check the **Box Model** diagram to understand padding, margin, and border - Toggle the CSS animation on `.pulse-dot` by unchecking the `animation` property - Double-click text content in the Elements panel to edit it inline - Check **Computed** tab to see the final resolved values for any property ### JavaScript debugging **DevTools tab: Sources** The Sources tab is your full JavaScript debugger — breakpoints, step-through execution, variable inspection, and call stack analysis. Our demo includes a data processing pipeline with clearly-marked breakpoint targets: ```javascript function fetchData() { // BREAKPOINT HERE: inspect the raw data const rawData = [ { id: 1, name: 'Widget A', price: 29.99, category: 'electronics', inStock: true }, // ... ]; return rawData; } function transformData(data) { // BREAKPOINT HERE: watch the transformation const transformed = data.map(item => ({ ...item, displayName: item.name.toUpperCase(), priceFormatted: '$' + item.price.toFixed(2), })); return transformed; } function filterData(data) { // BREAKPOINT HERE: see what gets filtered const filtered = data.filter(item => { const isInStock = item.inStock === true; const isAffordable = item.price < 50; return isInStock && isAffordable; }); return filtered; } ``` **What to try in DevTools:** 1. In the **Sources** tab, find the page's script (it may appear under `demo.local`). 2. Click a line number to set a breakpoint (a blue marker appears). 3. Press **"Run Data Pipeline"** in the WebView. 4. Execution pauses at your breakpoint. Now you can: - **Step Over** (F10) to execute the current line and move to the next - **Step Into** (F11) to enter a function call - **Step Out** (Shift+F11) to return from the current function - Hover over variables to see their current values - Check the **Scope** panel on the right to see all local and closure variables - Check the **Call Stack** panel to see the function call chain The demo also includes a `debugger;` statement behind a button: ```javascript function triggerDebugger() { const secret = 'You found the hidden value!'; const counter = 42; const data = { message: secret, count: counter }; debugger; // Execution pauses here when DevTools is open } ``` When you click the button with DevTools open, execution pauses immediately and you can inspect all variables in scope. **Bonus — Fibonacci for call stack depth:** Set a breakpoint inside the `fibonacci()` function and run it. The recursive calls create a deep call stack visible in the Call Stack panel, which is great for understanding how recursive functions execute. ### Error debugging **DevTools tab: Console (with Sources "Pause on exceptions")** The Console tab shows JavaScript errors with their full stack traces. Combined with the "Pause on exceptions" feature in Sources, you can freeze execution at the exact moment an error occurs. Our demo triggers various error types: ```javascript // Each of these creates a different error in DevTools function throwReferenceError() { console.log(undefinedVariable); // ReferenceError } function throwTypeError() { const obj = null; obj.toString(); // TypeError } function throwSyntaxError() { eval('function {'); // SyntaxError } // Async errors function throwPromiseRejection() { Promise.reject(new Error('Async operation failed: timeout after 30s')); } // Caught vs uncaught -- compare behavior in DevTools function caughtError() { try { const data = JSON.parse('{ invalid json }'); } catch (error) { console.error('Caught error:', error); } } ``` **What to try in DevTools:** - Enable **"Pause on exceptions"** (the pause icon in the Sources tab) — execution will freeze at the exact line where an error is thrown - Toggle **"Pause on caught exceptions"** as well to catch errors inside try/catch blocks - Click on stack trace entries to jump to the source location - Compare how caught and uncaught errors appear differently in the Console - Watch for **unhandled promise rejections** — these show up with a distinct icon ## The WebView Screen: Bringing It Together Here's how the demo app's WebView screen is structured. Each scenario uses the same screen with different HTML content: ```dart import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; class DemoWebViewScreen extends StatefulWidget { final DemoScenario scenario; const DemoWebViewScreen({super.key, required this.scenario}); @override State createState() => _DemoWebViewScreenState(); } class _DemoWebViewScreenState extends State { late final WebViewController _controller; @override void initState() { super.initState(); // Use platform-specific params for iOS late final PlatformWebViewControllerCreationParams params; if (WebViewPlatform.instance is WebKitWebViewPlatform) { params = WebKitWebViewControllerCreationParams( allowsInlineMediaPlayback: true, ); } else { params = const PlatformWebViewControllerCreationParams(); } _controller = WebViewController.fromPlatformCreationParams(params) ..setJavaScriptMode(JavaScriptMode.unrestricted) ..loadHtmlString( widget.scenario.htmlContent, baseUrl: 'https://demo.local/', ); // Enable inspection on iOS 16.4+ if (_controller.platform is WebKitWebViewController) { (_controller.platform as WebKitWebViewController).setInspectable(true); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.scenario.title)), body: Column( children: [ // Instruction panel for presenters ExpansionTile( initiallyExpanded: true, title: const Text('What to look for'), children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 12), child: Text(widget.scenario.description), ), ], ), const Divider(height: 1), // The WebView Expanded( child: WebViewWidget(controller: _controller), ), ], ), ); } } ``` A few things worth noting: - **`WebViewController.fromPlatformCreationParams`** — On iOS, we pass `WebKitWebViewControllerCreationParams` to configure WebKit-specific behavior. On Android, we use the default params. This pattern lets you write platform-adaptive code cleanly. - **`setInspectable(true)`** — This is what makes the WebView visible to Safari Web Inspector on iOS 16.4+. Without it, the WebView simply won't appear in the Develop menu. You access this through the platform-specific `WebKitWebViewController`. - **`setJavaScriptMode(JavaScriptMode.unrestricted)`** — Enables JavaScript execution in the WebView. Without this, none of the demo interactions would work. - **`loadHtmlString` with `baseUrl`** — When loading HTML inline, the page's origin is `about:blank` by default. Setting a `baseUrl` gives the page a proper origin so network requests from JavaScript don't hit CORS issues. - **`WebViewWidget(controller: _controller)`** — The widget is a thin wrapper that renders whatever the controller has loaded. The controller holds all the configuration and state. ## Common Gotchas and Troubleshooting ### "My WebView doesn't appear in chrome://inspect" 1. Verify `AndroidWebViewController.enableDebugging(true)` is called in `main()` **before** the WebView loads. 2. Check USB debugging is enabled on the device. 3. Try a different USB cable — charging-only cables don't support debugging. 4. Change USB mode from "Charging" to "File Transfer" on the device. 5. Refresh `chrome://inspect` and make sure "Discover USB devices" is checked. ### "Safari Develop menu shows my device but no WebView" This is almost always the `setInspectable` issue on iOS 16.4+: 1. Make sure you're calling `setInspectable(true)` on the `WebKitWebViewController`: ```dart if (_controller.platform is WebKitWebViewController) { (_controller.platform as WebKitWebViewController).setInspectable(true); } ``` 2. Make sure Web Inspector is enabled on the device: **Settings > Safari > Advanced > Web Inspector**. 3. Rebuild and re-run the app — hot reload doesn't apply setting changes. 4. Remember: simulators always work without this flag, so test on a **real device**. ### "Network requests from my WebView fail with CORS errors" When HTML is loaded via `loadHtmlString`, the page origin is `about:blank`, which can trigger CORS restrictions. Pass a `baseUrl`: ```dart controller.loadHtmlString( htmlContent, baseUrl: 'https://demo.local/', ); ``` ### "The `debugger;` statement doesn't pause execution" `debugger;` only pauses if DevTools is open and attached **before** the statement is hit. If DevTools isn't connected, the statement is silently ignored. Connect DevTools first, then trigger the action. ### "I can't see my WebView's source in the Sources tab" The page loaded via `loadHtmlString` may appear under the `baseUrl` domain you set (e.g., `demo.local`). Look in the page tree under that domain. If you don't set a `baseUrl`, it appears under `about:blank`, which can be harder to find. ## Quick Reference: Setup Checklist ### Android - [ ] `INTERNET` permission in `AndroidManifest.xml` - [ ] `AndroidWebViewController.enableDebugging(true)` called in `main()` - [ ] USB debugging enabled on device - [ ] Device connected via USB (data cable, not charging-only) - [ ] `chrome://inspect/#devices` open in Chrome with "Discover USB devices" checked ### iOS - [ ] `NSAppTransportSecurity` configured in `Info.plist` (if hitting HTTP endpoints) - [ ] `setInspectable(true)` called on `WebKitWebViewController` (required for iOS 16.4+) - [ ] **Web Inspector** enabled on device: Settings > Safari > Advanced > Web Inspector - [ ] **"Show features for web developers"** enabled in Safari > Settings > Advanced on your Mac - [ ] Device connected via USB to Mac - [ ] Safari > Develop > [Device Name] > select the WebView ## Wrapping Up Debugging WebView content in Flutter doesn't require special tools or complex setups — it's the same Chrome DevTools and Safari Web Inspector you use for regular web development. The key steps are: 1. Use `webview_flutter` with the platform-specific packages for debugging control. 2. Enable debugging in code: `AndroidWebViewController.enableDebugging(true)` on Android, `setInspectable(true)` on iOS. 3. Configure the device settings: USB debugging on Android, Web Inspector on iOS. 4. Connect via `chrome://inspect` or Safari's Develop menu. The [demo app](https://github.com/taaha161/webview_debug_demo) for this post gives you a hands-on playground for every major DevTools feature. Clone it, run it on a device, connect DevTools, and start inspecting. If you've been treating WebViews as black boxes in your Flutter apps, this is your way in. ### Genkit for Flutter: Moving AI Logic to a Dart Backend Source: https://verygood.ventures/blog/genkit-for-flutter-moving-ai-logic-to-a-dart-backend/ In [Part 1](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/) we built a conversational shopping assistant powered by Flutter, GenUI, and Firebase AI. In [Part 2](https://verygood.ventures/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/) we swapped Firebase AI for Genkit and added flows, middleware, custom tools, structured output, and context — all running client-side. Client-side Genkit is fine for prototyping. But when all your AI logic runs in the app, the API key is embedded in the client — exposed in a web build, readable from DevTools. The natural fix is to move logic that doesn't need the UI to a backend server, where the API key never leaves. In this post we move the AI logic that has no UI dependency to a Dart backend server using `genkit_shelf`. The Gemini API key moves with it, off the client for good. The best part: since both the server and the Flutter app are Dart, the code barely changes. The full source is available on the [`genkit-refactor-part2` branch](https://github.com/VGVentures/genui_shopping_assistant/tree/genkit-refactor-part2). ## What We're Building The architecture shift looks like this: ![Diagram comparing Part 2 client-side architecture (ContentGenerator holds all flows and API key, exposed in app) with Part 3 server-side architecture (UI-dependent flows stay in Flutter, data flows and API key move to a Dart server)](/assets/images/blog/genkit-for-flutter-moving-ai-logic-to-a-dart-backend/body-0.avif) The UI layer stays completely untouched. The `ContentGenerator` interface is the same. We're only changing where the data work happens. ## 1. Understanding the Split Before writing any server code, it helps to be precise about what belongs on the server and what doesn't. The GenUI tools — `surfaceUpdate`, `beginRendering`, `deleteSurface` — emit events to streams that drive the Flutter widget tree. They need direct access to the UI. They stay in the Flutter app. The `shoppingAssistantFlow` orchestrates those UI tools, so it stays in Flutter too. But two flows have zero UI dependency: - **`searchProductsFlow`** — takes a query, searches an inventory, returns JSON - **`quickRecommendationFlow`** — takes a user query, calls Gemini with structured output, returns typed recommendations Both are pure data operations. Both belong on the server. | What | Lives on | Why | |---|---|---| | `surfaceUpdate`, `beginRendering`, `deleteSurface` | Flutter app | Needs UI access | | `shoppingAssistantFlow` | Flutter app | Orchestrates UI tools | | `searchProductsFlow` | **Dart server** | Pure data, no UI dependency | | `quickRecommendationFlow` | **Dart server** | Pure data, no UI dependency | | Gemini API key | **Dart server** | Never exposed to client | | `ShoppingContext` | Flutter app | Source of truth for cart state | The Flutter app remains the source of truth for cart state — it sends that state to the server with each request. The server uses it, derives what it needs, and discards it. This keeps the server stateless, which matters: a shared mutable context on the server means user A's cart bleeds into user B's request. Each request gets its own fresh `ShoppingContext` instance derived purely from the payload. ## 2. Setting Up the Dart Server Create a `backend/` folder at the root of the project with its own `pubspec.yaml`: ```yaml name: shopping_assistant_backend description: Dart backend server for the GenUI Shopping Assistant. publish_to: none version: 0.0.1 environment: sdk: '^3.10.0' dependencies: genkit: ^0.12.0 genkit_google_genai: ^0.2.3 genkit_shelf: ^0.1.0 schemantic: ^0.4.0 dev_dependencies: lints: ^4.0.0 ``` Add an `analysis_options.yaml` referencing `package:lints/recommended.yaml` so linting rules are enforced in the backend package just as they are in the Flutter app. Then run `dart pub get` inside `backend/` and you're ready. > **Note:** `product_data.dart` and `shopping_context.dart` are copied into `backend/lib/` for simplicity in this demo. In a production app, extract shared models into a separate Dart package and reference it via path dependency from both the Flutter app and the backend. This avoids the risk of the two copies drifting out of sync. ## 3. Exposing Flows as HTTP Endpoints Create `backend/bin/server.dart`. Flows are defined exactly as they were in the Flutter app — `defineFlow` works the same on the server. The only difference is that instead of calling them directly, you pass them to `startFlowServer`, which exposes each flow as a `POST` endpoint at `/`. ```dart void main() async { final apiKey = Platform.environment['GOOGLE_API_KEY'] ?? ''; if (apiKey.isEmpty) { print('ERROR: GOOGLE_API_KEY is not set.'); exit(1); } final ai = Genkit(plugins: [googleAI(apiKey: apiKey), RetryPlugin()]); final searchProductsFlow = ai.defineFlow, String, void, void>( name: 'searchProductsFlow', fn: (input, _) async { /* ... */ }, ); final quickRecommendationFlow = ai.defineFlow>, void, void>( name: 'quickRecommendationFlow', fn: (userQuery, _) async { /* ... */ }, ); await startFlowServer( flows: [searchProductsFlow, quickRecommendationFlow], port: 3400, cors: {'origin': Platform.environment['ALLOWED_ORIGIN'] ?? '*'}, ); } ``` The API key comes from an environment variable — never hardcoded. `ALLOWED_ORIGIN` defaults to `*` for local development and should be set to your app's domain in production. One production detail worth calling out: each request to `searchProductsFlow` gets a fresh `ShoppingContext` derived from the payload. The Flutter app sends its current cart state with every request, and the server uses it to annotate results. ```dart fn: (input, _) async { // Fresh instance per request — a shared instance would bleed // cart state between users on the server. final shoppingContext = ShoppingContext(); final cartItems = (input['cartItems'] as List?) ?.map((e) => e as String).toList() ?? []; for (final item in cartItems) { shoppingContext.addToCart(item, 0); } // ... search and return results }, ``` The flow definitions themselves — tools, `outputSchema`, retry middleware — are identical to Part 2. They moved files, not concepts. The full implementation is in [`backend/bin/server.dart`](https://github.com/VGVentures/genui_shopping_assistant/blob/genkit-refactor-part3/backend/bin/server.dart). Start the server: ```bash GOOGLE_API_KEY=your_key dart run bin/server.dart ``` Before touching the Flutter app, verify both endpoints work independently: ```bash # Test searchProducts curl -X POST http://localhost:3400/searchProductsFlow \ -H "Content-Type: application/json" \ -d '{"data": {"query": "running shoes"}}' # Test quickRecommendation curl -X POST http://localhost:3400/quickRecommendationFlow \ -H "Content-Type: application/json" \ -d '{"data": "recommend me something for running"}' ``` Both should return JSON responses. The server is working. ## 4. Updating the Flutter App Add `http: ^1.2.0` to the Flutter app's `pubspec.yaml`. Then open `genkit_content_generator.dart` and update the `fn` inside `_searchProductsTool`. This is the only meaningful change — instead of calling `searchProducts()` locally, it calls the server: ```dart // Before (Part 2): local call fn: (input, _) async { final results = searchProducts(query: input['query'] as String, ...); return jsonEncode(results.map((p) => p.toJson()).toList()); }, // After (Part 3): server call fn: (input, _) async { final backendUrl = const String.fromEnvironment( 'BACKEND_URL', defaultValue: 'http://localhost:3400', ); final response = await http.post( Uri.parse('$backendUrl/searchProductsFlow'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'data': { 'query': input['query'], if (input['category'] != null) 'category': input['category'], 'cartItems': _shoppingContext.cartItems.map((i) => i.productName).toList(), } }), ); if (response.statusCode != 200) { throw Exception('Backend error: ${response.statusCode}'); } return (jsonDecode(response.body) as Map)['result'] as String; }, ``` The `BACKEND_URL` comes from `--dart-define` so it works in both development and production without a code change. The same pattern applies to `_handleRecommendationQuery`, which now calls `quickRecommendationFlow` on the server instead of running locally. Also remove the `_quickRecommendationFlow` field and `product_data.dart` import from the Flutter app — neither is needed anymore. ## 5. Running the Full Stack Two terminals — one for the server, one for Flutter. **Terminal 1:** ```bash cd backend GOOGLE_API_KEY=your_key dart run bin/server.dart ``` **Terminal 2:** ```bash flutter run -d chrome \ --dart-define=GOOGLE_API_KEY=your_key \ --dart-define=BACKEND_URL=http://localhost:3400 ``` In production, set `BACKEND_URL` to your deployed server URL and `ALLOWED_ORIGIN` to your app's domain. Neither value is hardcoded anywhere in the source. ## What We Gained The change across the whole project is small — two methods updated in the Flutter app, a new `backend/` folder, one new dependency. What it delivers: **Security.** The Gemini API key lives only on the server. It never touches the Flutter app. **Stateless server flows.** Each request to `searchProductsFlow` gets a fresh `ShoppingContext` derived from the payload. No state bleeds between users. **Configurable URLs.** `BACKEND_URL` and `ALLOWED_ORIGIN` come from environment variables. The same binary works in development, staging, and production without a rebuild. **Clean separation.** Flows with UI dependencies stay in Flutter. Flows with data dependencies move to the server. The split follows a clear rule rather than being arbitrary — and it's a pattern that scales to any Flutter AI integration, not just this app. The UI didn't change. The user experience didn't change. The `ContentGenerator` abstraction means the Flutter app never needed to know where the work happened — and now it doesn't need to know the server exists either. ## What's Next The remaining Genkit logic — `shoppingAssistantFlow` itself — still runs in the Flutter app because it orchestrates the GenUI UI tools, which need direct access to the widget tree. Moving that flow to the server requires a different approach — specifically an A2UI agent backend, where the server drives UI events over a streaming protocol. The `genui_a2a` package is designed exactly for this. That's the natural next step for teams building production GenUI apps, and we'll cover it in a future post. *The full source code for this post is on the [`genkit-refactor-part2` branch](https://github.com/VGVentures/genui_shopping_assistant/tree/genkit-refactor-part2). Parts [1](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/) and [2](https://verygood.ventures/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/) cover the GenUI architecture and the client-side Genkit setup.* ### How We Built VGV Wingspan: Our Claude Code Plugin Source: https://verygood.ventures/blog/how-we-built-wingspan-claude-code-plugin/ At [Very Good Ventures](https://verygood.ventures/success-stories/), we kept running into the same gap with AI coding assistants: the tools were great at generating code, but they didn't know *how we work*. Our teams follow a consistent set of engineering practices — clean architecture, comprehensive testing, YAGNI, and clear layer separation. These practices scale. But no AI tool understood them. So we built VGV Wingspan — a [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) plugin that encodes our engineering workflow as AI-driven skills and specialized agents. Not as a replacement for developers, but as a collaborator that understands the full lifecycle: from exploring an idea to shipping a pull request. ## The Problem: AI Is Fast, But Fast Isn't Enough AI coding assistants can generate code quickly. But speed without structure creates problems: - **No discovery phase.** Jumping straight to code skips the "what are we actually building?" conversation. - **No plan.** Generated code often addresses the wrong problem — or the right one in a way that breaks existing architecture. - **No quality gate.** Without review, AI-generated code can introduce subtle violations — wrong state management patterns, missing tests, broken layer boundaries. We wanted a tool that made AI follow the same workflow our engineers follow: explore the problem, plan the approach, build with discipline, and review before shipping. ## Tech-Agnostic by Design Early versions of Wingspan were Flutter-specific. We had [Flutter](https://flutter.dev) linting rules, Dart formatting hooks, and [BLoC](https://bloclibrary.dev)-specific review logic baked in. It worked. But only for Flutter projects. We made a deliberate decision to separate concerns: Wingspan handles the *workflow* (brainstorm, plan, build, review), and companion plugins handle *technology-specific* conventions (linting, formatting, scaffolding, and framework patterns). This means Wingspan works with any stack. The engineering principles it enforces — clean architecture, comprehensive testing, YAGNI, and clear layer separation — are universal. The Flutter-specific rules moved into the [Very Good AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin), which Wingspan automatically recommends when it detects a Flutter project. Wingspan also integrates MCP servers that extend its capabilities across the workflow. Figma connects design assets directly to the development process, so brainstorm and plan phases can reference real designs instead of verbal descriptions. Context7 fetches up-to-date documentation for libraries and frameworks, ensuring that plans and implementations use current APIs rather than relying on stale training data. ### The Recommendation System This design decision is worth pausing on. Detection isn't limited to scanning existing project files — Wingspan also picks up on the technology specified during brainstorm or plan phases. Start a brainstorm about a new Flutter app on an empty directory, and Wingspan recommends the Flutter companion plugin before any code exists. It recommends silently, just once per session. ![Wingspan plugin ecosystem architecture — tech-agnostic core with companion plugins including Very Good AI Flutter Plugin, extensible via hook-based recommendations](/assets/images/blog/how-we-built-wingspan-claude-code-plugin/plugin-architecture.avif) The system is entirely extensible without code changes — adding support for a new ecosystem is just a configuration file and a hook script. No redeployment required. ## Markdown-First Architecture for Skills and Agents One of the earliest decisions — and one we never regretted — was making everything markdown. Skills are markdown files with [YAML frontmatter](https://jekyllrb.com/docs/front-matter/). Agents are markdown files with structured prompts. Templates are markdown. Plans, brainstorms, reviews, and debriefs — all markdown. Why markdown over YAML, JSON, or TOML as the primary format? Markdown is the one format that is simultaneously human-readable, version-controllable, CI-parseable, and self-documenting. Every other option optimizes for machines at the expense of contributors. This has practical consequences: - **Version control works.** Every change to a skill or agent is a diff you can review in a PR. - **No special tooling.** Anyone can read, edit, and understand a skill without learning a DSL or framework. - **CI validates them.** Our pipeline runs markdown lint, spell check, and structural validation on every PR. - **They're self-documenting.** The skill file *is* the documentation. ## Managing Context Windows Between AI Phases Large language models [degrade as context fills up](https://arxiv.org/abs/2307.03172). Early reasoning gets pushed out, instructions blur, and the model starts making mistakes. We saw this firsthand — a brainstorm that ran long would produce a worse plan. A plan review that loaded too much code would miss architectural issues. ![Wingspan context clearing pattern — cluttered AI context window versus a clean fresh slate, with artifacts carrying decisions forward between phases](/assets/images/blog/how-we-built-wingspan-claude-code-plugin/context-clearing.avif) Our solution: **clear context between phases.** Every transition point offers a "Clear context and [next step]" option. Selecting it invokes the next skill with a fresh context window. The previous phase's output is already saved to `docs/`, so nothing is lost — but the model starts with a clean slate. Context management also matters *within* phases. When agents report back during the review phase, full verbose output can consume thousands of tokens and crowd out the reasoning space the orchestrator needs. We learned to have agents return concise summaries rather than exhaustive reports — the details are written to files, but the context window only gets what's needed to make decisions. This pattern was counterintuitive at first. Why throw away context? Because the artifacts carry the decisions forward, and fresh reasoning produces better results than exhausted reasoning. ## 14 Skills, 10 Agents, 80 Commits ![VGV Wingspan development stats — 14 skills, 10 agents, and 80 commits built over 8 weeks from February to April 2026](/assets/images/blog/how-we-built-wingspan-claude-code-plugin/development-stats.avif) Wingspan took shape over eight weeks, from February to April 2026. The core workflow loop — brainstorm, plan, build, and review — landed in the first four weeks. The remaining time went to the tech-agnostic transformation, the plugin recommendation system, and the skills we kept reaching for: `/hotfix` for urgent production fixes (skips brainstorm and plan, goes straight to build-and-review), `/review` for on-demand quality checks against any branch, `/debrief` for post-incident analysis, and utility skills like `/commit` and `/create-branch` for the git operations that bookend every task. The final count: 14 skills, 10 agents, a hook-based plugin recommendation system, and a CI pipeline that validates every change. Eight weeks from first commit to marketplace release. ## What We Learned ### 1. Structure beats intelligence A mediocre plan executed with discipline beats a brilliant improvisation. By forcing the AI through brainstorm-then-plan-then-build-then-review, we get consistently better results than letting it freestyle. ### 2. Multi-agent code review catches what single-pass review misses Five agents reviewing from different angles — standards, simplicity, testing, architecture, and readiness — catch more issues than a single comprehensive review. The disagreements between them often surface the most interesting problems. ### 3. YAGNI applies to AI tools too Early versions of Wingspan had features we never used: complex state machines for skill transitions, elaborate retry logic, and configurable review thresholds. We removed all of it. The simplest version that works is the right version. ### 4. Context is precious — spend it wisely Every token of context that goes to stale information is a token not available for fresh reasoning. The context-clearing pattern was our single biggest quality improvement. ### 5. Tech-agnostic is harder but worth it It was tempting to keep Flutter-specific logic in Wingspan — it worked and our team uses Flutter daily. But separating concerns made Wingspan useful for *all* our projects and opened the door for companion plugins from the community. ## The Four-Phase Workflow Wingspan is structured around four phases, each producing artifacts that inform the next: ![Wingspan four-phase workflow diagram — Brainstorm, Plan, Build, and Review phases feeding into a pull request](/assets/images/blog/how-we-built-wingspan-claude-code-plugin/four-phase-workflow.avif) ### 1. Brainstorm (`/brainstorm`) This is the discovery phase. You describe a problem or idea, and Wingspan opens a collaborative dialogue to explore requirements, constraints, and approaches. Instead of rushing to code, it asks questions, surfaces trade-offs, and clarifies the actual problem. The boundary exists because the best implementation starts with a shared understanding of what you're building and why — something AI tools routinely skip. The output is a brainstorm document saved to `docs/brainstorm/`. This document captures the "why" and the "what" before anyone writes a line of code. ### 2. Plan (`/plan`) Once the brainstorm is solid, Wingspan transforms it into an actionable implementation plan. For existing projects, this phase reviews the codebase, identifies relevant patterns and conventions, and produces a step-by-step plan with clear acceptance criteria. For greenfield projects, it researches the target stack and establishes the conventions the implementation will follow. Either way, the plan phase exists as a separate step because we've seen AI tools produce perfectly reasonable code that solves the wrong problem — or solves the right problem in a way that ignores the project's existing conventions. Plans come in three sizes — minimal, standard, and extensive — matched to the complexity of the task. A simple bug fix gets a minimal plan. A new feature spanning multiple layers gets an extensive one with phases, risks, and dependencies. The plan is saved to `docs/plan/`, ready for the build phase. ### 3. Build (`/build`) This is where code gets written. Wingspan executes the plan and writes tests alongside the implementation, producing a working branch ready for review. This is also where companion plugins become active — if the plan specifies Flutter, the Flutter plugin's scaffolding, linting, and framework-specific conventions guide the implementation. ### 4. Review Before opening a pull request, Wingspan runs a multi-agent quality review. Five specialized agents review the work in parallel: - **VGV Review Agent** — Checks adherence to Very Good Ventures engineering standards - **Code Simplicity Agent** — Flags YAGNI violations and premature abstractions - **Test Quality Agent** — Verifies every testable unit has meaningful tests - **Architecture Agent** — Validates layer separation and dependency direction - **PR Readiness Agent** — Final pre-merge checks (CI compatibility, merge conflicts) ![Wingspan multi-agent review — five parallel quality agents reviewing a build before a pull request](/assets/images/blog/how-we-built-wingspan-claude-code-plugin/review-agents.avif) Issues are categorized as critical (must fix), important (should fix), or suggestions (noted in the PR). The review phase doesn't just check for bugs — it enforces the engineering practices that let teams scale. ## Getting Started Wingspan is our attempt to answer a question every engineering team using AI tools eventually faces: how do you get the AI to work the way your team works? The answer, for us, was to encode the workflow — brainstorm, plan, build, review — and let the AI execute it. If that pattern is useful for your team, Wingspan is [open source](https://github.com/VeryGoodOpenSource/vgv-wingspan) and available on the [Claude Code marketplace](https://docs.anthropic.com/en/docs/claude-code/plugins). ```bash /plugin marketplace add VeryGoodOpenSource/very_good_claude_code_marketplace /plugin install vgv-wingspan@very_good_claude_code_marketplace ``` Start with `/brainstorm` and see where it takes you. ### From Dart to Flutter: A Journey Through Software Development Source: https://verygood.ventures/blog/brian-egan-very-good-ventures-the-evolution-of-dart-and-flutter/ {% video url="https://www.youtube.com/watch?v=stkQineQreY" title="Brian Egan, Very Good Ventures — The Evolution of Dart and Flutter" %} In the ever-evolving world of software development, languages come and go, but some leave a lasting impact. One such language is Dart, which has seen a remarkable journey since its inception in 2011. ## The Birth of Dart Dart was created by a team at Google and was designed to be a modern alternative to JavaScript, aiming to cater to the needs of increasingly complex web applications. Brian Egan recalls his experience with Dart's early days while working at Zappos, installing an IDE plugin and beginning to explore its capabilities. ## The Syntax and Learning Curve One of the appealing aspects of Dart is its C-like syntax, which made it accessible for developers already familiar with languages like Java and JavaScript. The introduction of type safety was a welcome change, especially when working in teams. ## Community Reception and Initial Skepticism When Dart was first introduced, the community was skeptical about its potential to become a web standard. Developers were concerned about its ability to coexist with JavaScript, given the latter's dominance in web development. ## The Turning Point: From Dart to Flutter A significant turning point occurred when Flutter emerged, aiming to simplify mobile app development while leveraging Dart's strengths. Brian's journey into Flutter began as he sought faster iteration cycles and improved performance compared to traditional Android development. The promise of features like hot reload and hot restart captured his attention. ## Key Features and Improvements Over Time Over the years, Dart has undergone substantial improvements. Notable changes include the removal of the 'new' keyword (improving code readability), the introduction of true sound type safety, and Flutter's expanding multi-platform capabilities beyond mobile. ## Key Takeaways The evolution of Dart from a bold experiment to a powerful language integrated with Flutter showcases the importance of adaptability in technology. Key lessons include the significance of type safety, the value of community feedback, and the importance of continuous improvement in programming languages. ### How VGV Used Claude Code to Rebuild Betterment's GraphQL Code Generator for Dart 3 Source: https://verygood.ventures/blog/how-vgv-used-claude-code-to-rebuild-betterment-graphql-code-generator-for-dart3/ Betterment's Flutter codebase relied on [Ferry, a stream-based GraphQL client for Dart](https://github.com/gql-dart/ferry), backed by [the `gql` Dart library](https://pub.dev/packages/gql) for code generation. But Ferry’s generated code predated Dart 3 and couldn’t support sealed classes, exhaustive pattern matching, or switch expressions. Fragments, the backbone of any GraphQL schema, could fail silently at runtime, uncharacteristic behavior for Dart. Cache deserialization was tricky and could easily break. Polymorphic types couldn’t be handled idiomatically. Betterment’s team had built compensating patterns to work around these limitations, but the workarounds carried a real cost: added maintenance burden, increased cognitive load for developers, and a GraphQL experience on Flutter that was significantly more constrained than what the team was accustomed to on other platforms. Rather than continue absorbing that overhead, the team decided it was time to fix the problem at the source. Rather than migrate away from Ferry, [VGV's team, building on a long-running Flutter partnership with Betterment](https://verygood.ventures/success-stories/betterment/), wrote a new code generation layer from the ground up, using [Claude Code](https://code.claude.com/docs/en/overview) as a core part of the development workflow. The result: a Dart 3-native GraphQL code generator with sealed classes for polymorphic type safety, custom serialization, and a multi-pass pipeline architecture that eliminated the root causes of v1's limitations. For Betterment, the impact went beyond the generated code itself. The new generator removed the compensating patterns that had accumulated around Ferry’s shortcomings, reducing maintenance burden in the Flutter codebase and cognitive load across the team’s broader GraphQL practice. With polymorphism and fragments working idiomatically, product teams could move faster, writing GraphQL code on Flutter the same way they did on other platforms, without workarounds slowing them down. This is the story of how we built it, what we learned about agentic coding in practice, and what other engineering teams can take from the experience. ## Results at a Glance * **10x ticket velocity:** Tasks that previously took 2-4 days completed in 2-4 hours * **2x project output:** 6 epics delivered in 3 months (rather than an estimated 6 months without Claude) * **38% faster builds:** v2 code generator build times vs. v1 * **4 critical Ferry code generation issues resolved:** Deserialization, polymorphism, and fragment handling issues eliminated * **Generated file size increase:** Only 0.7%, with simpler output and no `built_value` dependencies ## The Problem: Why Ferry’s Code Generator Had To Change ### What Was The Change Needed? Ferry relied on `gql`, a library that predates Dart 3 and doesn't support modern features like [sealed classes](https://dart.dev/language/class-modifiers#sealed) for exhaustive pattern matching and [switch expressions](https://dart.dev/language/patterns) for type discrimination. This created four critical issues: * **Type matching failures for interface fragments**, making it impossible to use [fragments on interface types](https://graphql.org/learn/queries/#fragments). * **Inability to deserialize polymorphic fragments from the cache.** * **Heavy dependency on the [built_value serialization library](https://pub.dev/packages/built_value)**, which failed to serialize JSON fields correctly. The generated code was also overly verbose, making testing difficult. * **No fragment reuse on polymorphic types**, leading to widespread code duplication. These weren’t problems unique to Betterment. Every team relying on gql for code generation faced the same limitations: Ferry’s code generator simply hadn’t kept pace with Dart 3. A partnership between Betterment, VGV, and Claude Code made it possible to move fast enough to fix the problem at the source, improving Betterment’s GraphQL practice while contributing improvements that benefit the broader Flutter community. ### A Concrete Example: The Silent Casting Failure The code generation work was pure Dart, not Flutter, but the issue manifests clearly in fragment deserialization. In GraphQL, you have interfaces and fragments. A fragment can target an interface, like this: ```graphql interface MyInterface { // common fields } type FirstImplementation implements MyInterface { // type specific fields } type SecondImplementation implements MyInterface { // type specific fields } ``` And then a fragment on that interface: ```graphql fragment MyFragment on MyInterface { // fragment fields selection } ``` The v1 code generator produced this: ```dart switch (G__typename) { // Server sends `FirstImplementation` or `SecondImplementation`, // never `MyInterface` case 'MyInterface': return MyInterface(this); case 'FirstImplementation': return FirstImplementation(this); // Always hits this for interface fragments default: return orElse(); } ``` This created a silent failure: the fragment never matched the interface type, forcing developers to handle all interface types manually. This wasn’t a bug in the traditional sense, it was a design limitation of the v1 code generation. To compensate, Betterment’s engineers duplicated code, avoided idiomatic GraphQL polymorphism entirely, and added extra test coverage to protect against the gaps the generated code couldn’t catch. ![Visual explanation of the bug that the Ferry code generator introduced](/assets/images/blog/how-vgv-used-claude-code-to-rebuild-betterment-graphql-code-generator-for-dart3/bug.avif) ## Why A Custom Generator Was The Right Call GraphQL generates models directly from schemas and operations, eliminating the need for manual model definitions. Generated models become the domain model directly, with no additional mapping layer. This makes the code generator critical to the entire application architecture. Switching to a different GraphQL client would have required rewriting the integration across the entire codebase. Instead, the team built a custom code generator that produces sealed classes for polymorphic type safety, includes custom serializers, and removes heavy dependencies. Forking and improving `gql` rather than replacing Ferry preserved the existing client layer while unlocking Dart 3-native code generation — cleaner output, faster builds, and a clear migration path for Betterment's production codebase. ## The Architecture: A Multi-Pass Code Generation Pipeline ### How The Builders Work `SchemaBuilderV2`, `DataBuilderV2`, `ReqBuilderV2`, and `VarBuilderV2` function similarly to their v1 counterparts — each running in isolation via [build_runner](https://dart.dev/tools/build_runner), with distinct responsibilities for visiting document nodes and generating request, variable, or data classes. The critical change lives in DataBuilderV2, which introduces multi-pass code generation. ### Why Single-Pass Failed The v1 generator used a single pass: it started at the document root, found an operation, and immediately resolved types and nullability. This single-pass approach created context blindness — a fragment used in Operation A was regenerated when it appeared in Operation B, causing duplication and unnecessary complexity. ### The v2 Multi-Pass Pipeline The v2 architecture introduces a multi-pass pipeline, splitting code generation into three distinct stages with SchemaVisitor, FragmentVisitor, and OperationVisitor: * **Step 1 (SchemaVisitor):** Identifies all schema types - enums, inputs, scalars, objects, interfaces, and unions - then registers them in a SchemaRegistry available to subsequent steps. * **Step 2 (FragmentVisitor):** Analyzes fragments in isolation, resolving their exact type before they are used in operations. Results are stored in a FragmentRegistry. * **Step 3 (OperationVisitor):** Assembles everything using the registries from previous steps, identifies operations, and calls class builders to generate Dart classes. Despite traversing nodes multiple times, the multi-pass approach has no measurable impact on build time. The real value is correctness: because the FragmentVisitor resolves each fragment's type in isolation before any operation uses it, shared fragments produce a single, consistent type across the entire codebase. In v1's single-pass approach, the same fragment could produce subtly different generated types depending on which operation context it was resolved in, making true fragment reuse impossible. ![Visualization of the v2 multi-pass pipeline](/assets/images/blog/how-vgv-used-claude-code-to-rebuild-betterment-graphql-code-generator-for-dart3/v2-multi-pass.avif) ### Removing `built_value`: Why Thin Serialization Won While `built_value` was once the standard for Dart serialization, it had several drawbacks: * Heavy boilerplate * Slow build times * Failed deserialization of `Map`, which breaks all GraphQL operations using the JSON type * Handling generics and polymorphism requires manual serializer registration for each type; omitting even one causes runtime crashes during deserialization The team replaced this with custom serialization, adding `fromJson` and `toJson` methods to each generated class. This approach reduces complexity, works well with static methods and factory constructors, and avoids plugins and builders entirely. It eliminates `built_value` dependencies and improves both runtime and build performance. ### Sealed Classes: Exhaustive Polymorphism For GraphQL The previous version relied on `built_value` and manual type checking to handle polymorphism. This approach risked runtime errors: forgetting to handle any type variant could cause crashes or silent failures. Without exhaustiveness checking, the compiler couldn't verify that all type variants were handled. Betterment's end-to-end testing provided coverage against these runtime failures, but the feedback loop was slow — a missed type variant wouldn't surface until tests ran, rather than at compile time. The team wanted errors caught in the editor, not in CI. Dart 3's sealed classes enable switch expressions that are exhaustive: the compiler ensures all subtypes are handled, preventing missed cases while allowing access to shared base fields. Each sealed hierarchy includes an unknown subclass that acts as a catch-all, so when new types are added to the schema, existing clients handle them gracefully instead of breaking. The result is cleaner, safer code with built-in forward compatibility that enables idiomatic polymorphic GraphQL patterns to benefit from Dart’s type safety. ### Design Decisions Along The Way The team debated whether individual unit tests or comprehensive integration tests would better cover functionality and edge cases. We also discussed how to separate responsibilities between visitors and class spec builders, and how to maximize code reuse across them. These weren't major concerns because the architecture was well-defined; we encountered only minor issues during implementation. ## How Claude Code Accelerated The Team ### Ramping Up On An Unfamiliar Domain The VGV team had prior, but not vast experience with code generation and [build_runner](https://dart.dev/tools/build_runner), which made the project's early phases a steep learning curve. We asked Claude foundational questions before starting v2: "How does `build_runner` work? What are its entry points? How is the code structured? How do we invoke builders?" Within a few clarifications, we understood the key concepts: `build_runner` runs independently; factories in `build.yaml` specify input and output files; builders are isolated and communicate only through generated assets. Claude was essential to understanding how `gql` works. This helped us own tickets independently: We could first analyze what was needed and where changes should go. Once we understood v1, we used Claude to walk through the v2 architecture documentation — the multi-pass pipeline, each visitor's scope, and how to wire them together — which clarified the entire new design. ### How Much Time Did Claude Code Save On Ramp-Up? Understanding v1's architecture and `build_runner` basics could have taken weeks; learning v2's design might have taken another week or more. Based on similar projects, we would have spent the first month primarily learning rather than developing. Claude compressed this ramp-up to just a few days, allowing the entire VGV team to start productive work immediately. ## Documentation As Infrastructure: [AGENTS.md](http://AGENTS.md), [CLAUDE.md](http://CLAUDE.md), And Scoped Context ### The Story Behind The Betterment team, with its deep expertise in Ferry and GraphQL, had already faced and resolved four critical production issues. They had meticulously diagnosed the root causes and explored every angle before starting any v2 development. The GraphQL specification served as their definitive guide. This led to the v2 architecture—a multi-pass pipeline, sealed classes, and thin serialization—which was explicitly designed against the spec with a clear end goal. Their issue write-ups were more than simple bug reports; they included root cause analyses, reproduction steps, and proposed solutions linked to the architectural documentation. Furthermore, the documentation was not a static, initial draft. It was continuously refined and validated; they repeatedly used Claude Code against the documentation itself to ensure it was functional context for AI-assisted development. The documentation was considered complete and ready—embodying the "documentation as infrastructure" thesis—only when Claude could independently take a ticket, read the architecture docs, and generate correct, unambiguous code. This illustrates not just the need for good documentation, but what "good enough to drive an AI-assisted engagement" truly means and how to achieve it. This entire process—from identifying problems and designing the architecture to rigorously battle-testing the documentation—is what enabled VGV's high velocity. ### The Documentation That Was Already In Place The Betterment team had architecture and implementation documents in place when we joined, with major issues already catalogued. We updated these docs as we encountered problems and obstacles. The project was comprised of six epics; the first three of which had complete ticket breakdowns when we joined. A structured ticket template helped Claude separate concepts and objectives clearly throughout the engagement. The final epic included more diverse tickets focused on refactoring and cleanup. ### How [AGENTS.md](http://AGENTS.md) and [CLAUDE.md](http://CLAUDE.md) guided Claude’s Behavior The [AGENTS.md file structure](http://AGENTS.md) Betterment created was excellent. The root-level file provided concise guidance for Claude's repository work, including specification references, guidance, and review checklists. The documentation was organized by architecture, design principles, implementation details, v1 issues, epics, and tickets. Package-level AGENTS files and tickets referenced specific documentation indices tied to task scope. This let Claude work with focused scope and task-specific context, avoiding overwhelming global documentation. The documentation's level of detail, description style, and topic-based structure all contributed to scoped, manageable context. ### Tickets In The Repo: What Worked And What Didn’t Including tickets in the repo let Claude reference requirements when working on a task. Internally, Betterment already uses the [Atlassian MCP server](https://www.atlassian.com/blog/announcements/remote-mcp-server) to connect Claude directly to Jira — humans and agents pull context from the same source of truth. But at the time of this engagement, we weren't logistically equipped to share that MCP setup across both organizations, which meant copying Jira tickets into the repository as a practical workaround. Jira was the official source of truth, but the repository sometimes fell out of sync. **Lesson learned:** The ideal setup would have been connecting a [Jira/Confluence MCP](https://www.atlassian.com/blog/announcements/remote-mcp-server) to Claude instances. This would let Claude fetch tickets and docs on demand, eliminating local documentation copies entirely — and MCP connections to external systems are exactly the kind of infrastructure investment that pays dividends across projects. ## AI-Assisted Engineering: Our Workflow We conducted team-wide pair programming sessions for collaborative learning. The ticket owner would explain the task to the team and identify key areas needing work: new files, model changes, and so on. With a plan in place, we'd start with a simple prompt: "Claude, let's work on ticket XYZ." While basic, this prompt worked because Claude had full access to documentation and tickets. Claude would review the documentation and tickets, summarize them, and propose a detailed plan. We'd review the plan and suggest refinements like reusing existing files or refactoring repeated methods. After several iterations (usually to fix missed imports), Claude would summarize what was done, which helped the team review the code and changes. We'd ask Claude to iterate if anything needed refinement. We committed the code and wrote the PR description manually, using Claude's summary to ensure we captured all changes. Then we'd move to the next ticket with a different team member leading. Claude also assisted with PR reviews, which complemented — but never replaced — human review. Because Claude had the same context as during development, its review comments helped reviewers orient quickly, but every PR was reviewed and approved by a human engineer before merging. ![Phases of the AI-assisted engineering workflow used by VGV](/assets/images/blog/how-vgv-used-claude-code-to-rebuild-betterment-graphql-code-generator-for-dart3/ai-workflow.avif) ### Ticket Velocity: Hours, Not Days The early tickets in the first epic were the most complex as they were our introduction to the project. We weren't yet confident in the architecture, and the learning curve was steep. Without Claude, those tickets would have taken 2-4 days; with Claude and our workflow, we finished them in 2-4 hours. We rarely spent more than a day on a ticket unless the build failed or the Betterment team requested changes during review. Test tasks were another example. The nature of the project required bootstrapping new golden tests for generated code throughout the process, as each epic expanded the generator's capabilities, the team needed golden tests to protect against regressions going forward. These tests involved many edge cases and repetitive GraphQL mocking, which Claude handled significantly faster than manual implementation. Claude also helped the team more quickly validate that the golden tests themselves were sound; that they were actually capturing the right generated output and would catch meaningful regressions, not just recording whatever the generator happened to produce. ### Claude Code Pull-Request Reviews: What We Learned About AI As A Reviewer Claude Code's PR review capability was particularly effective. Since Claude had the same context as during development, it could review with deep, scoped knowledge. Claude's reviews were fast, and combined with pair programming, reduced review bottlenecks significantly. Interestingly, Claude sometimes suggested different improvements during review than during development, even with identical context. We'd think, "Why didn't Claude catch this during implementation?" But AI responses aren't deterministic — the same input can produce different outputs. Think of Claude as two different team members: one helping with development, another with review. Even if Claude suggests different things at different times, you'll reach the same outcome if your context is clear. Understanding this removes the temptation to "fix" the inconsistency. Even humans reviewing their own code weeks later notice things they missed before. The biggest win: Claude Code reviews eliminated time-zone bottlenecks that could have slowed down our distributed team. Because Claude's review pass caught structural issues, style inconsistencies, and missed edge cases before human reviewers ever opened the PR, engineers could focus their review time on architectural judgment and domain-specific correctness rather than mechanical concerns. Once we trusted the process, we let Claude approve and merge PRs in selected repositories (while keeping human in the loop review where required by our risk management policies), enabling true asynchronous continuous integration. If something broke, we'd file a follow-up ticket instead of blocking developers in different time zones waiting for feedback. ### Minimizing Hallucinations Through Documentation Hallucinations increase with ambiguity: unclear documentation, vague prompts, and Claude identifying patterns it assumes apply more broadly than they do. Eliminating ambiguity — through deterministic documentation and precise prompts — significantly reduces hallucinations. By documentation, we mean architecture guides, ticket descriptions, prompts, and any contextual information. Because tickets and architecture were well-documented, Claude rarely produced confidently incorrect code, though small, subtle bugs and tricky edge cases did slip through Claude’s initial implementations at times. Once developers identified such bugs, we'd ask Claude about the specific edge case with an example, and Claude could usually spot the issue and propose a fix. ### Onboarding: “Any Doubt, Ask Claude” If a new engineer joined the project today, their first day would look similar to my own first week: asking Claude everything. Too much documentation can overwhelm new engineers, leaving them unsure where to begin. Claude helps by letting you ask specific questions — "Where's the code entry point?" "What should I read first?" "Where are data classes generated?" — rather than navigating documentation yourself. This helps new engineers learn the codebase and adapt to AI-assisted development. ## The Results ![Productivity gains thanks to AI-assisted workflows](/assets/images/blog/how-vgv-used-claude-code-to-rebuild-betterment-graphql-code-generator-for-dart3/results.avif) ### V2 Code Generator Capabilities The v2 generator didn't add new capabilities but significantly improved polymorphism handling. It handles the same operations as v1 — queries, mutations, and subscriptions — plus regular fragments, interface fragments, inline fragments, nested fragments, scalar types (String, Int, Float, Boolean, ID), custom scalars, and enums. ### Benchmarks Benchmarks show v2 build times decreased by 38%, generated file size increased only 0.7%, and the output is simpler without `built_value` dependencies. All four critical issues — deserialization, polymorphism, and fragment handling — were completely resolved. ### Overall Productivity Rough estimates: **tasks that would have taken 2-4 days completed in 2-4 hours with Claude**; each epic took about 2 weeks. We completed 6 epics in 3 months, with demos at each sprint's end. Without Claude, we estimate we'd have delivered roughly half the work. These aren't exact figures, but they're indicative. ### Building the Future of Web Development: The Journey of Jaspr Source: https://verygood.ventures/blog/kilian-schulte-jaspr-building-the-future-of-web-development/ {% video url="https://www.youtube.com/watch?v=MUjtLvsFt-U" title="Kilian Schulte, Jaspr — Building the Future of Web Development" %} In the ever-evolving world of web development, the need for innovative frameworks continues to grow. One such groundbreaking framework is Jaspr, created by Flutter expert Kilian Schulte. This blog post delves into the fascinating journey of Jaspr, its unique features, and the significant impact it has on developers transitioning from Flutter to web development. ## What is Jaspr? Jaspr is a web framework designed to build websites using Dart. Unlike traditional HTML and CSS frameworks, Jaspr offers a unique approach by allowing developers to create components similar to Flutter widgets. This concept makes Jaspr particularly appealing to Flutter developers looking to expand their skill set into web development. As Kilian Schulte describes, "it looks a lot like Flutter, but it works slightly different," providing a familiar yet distinct environment for developers. ## The Inspiration Behind Jaspr Kilian Schulte's journey with Jaspr began nearly four years ago when he noticed a significant gap in the market for a Dart-based web framework. "There isn't really anything framework-wise there for Dart to build websites," Kilian explained. Motivated by his own experiences and a desire to avoid writing JavaScript, he decided to create a prototype over a weekend, leading to the birth of Jaspr. This initial experiment laid the groundwork for what would become a widely adopted framework. ## Community Engagement and Feedback The success of Jaspr is not solely attributed to its technical features but also to the vibrant Dart community. After launching Jaspr on Reddit, Kilian received overwhelming positive feedback, which fueled his motivation to further develop the framework. He noted, "The initial Reddit post was very successful, and a lot of people liked it and gave good feedback." This supportive community environment has been instrumental in shaping Jaspr, allowing it to evolve based on real user needs. ## The Big Moment — Official Adoption by Flutter and Dart Teams A pivotal moment in Jaspr's journey occurred when the Flutter and Dart teams approached Kilian to rebuild their official websites using Jaspr. This collaboration not only validated Jaspr's capabilities but also showcased its potential for larger-scale applications. Kilian shared, "They were actually considering it at that point, or at least looking at it and trying to evaluate it," highlighting the trust placed in his framework by industry leaders. ## Features Tailored for Developers To cater to the needs of Flutter developers transitioning to web development, Kilian designed Jaspr with specific features in mind. This includes robust support for Markdown content generation, server-side rendering, and a focus on creating a seamless developer experience. Kilian emphasized, "I always said from the beginning that I don't want to abstract away the concepts of the web," ensuring that developers not only use Jaspr effectively but also understand the underlying web technologies. ## Conclusion: Key Takeaways Jaspr represents a significant advancement in the web development landscape, bridging the gap between Flutter and traditional web frameworks. Its user-friendly design, coupled with strong community support, empowers developers to create dynamic web applications without the steep learning curve associated with other frameworks. As Kilian Schulte continues to enhance Jaspr, it is clear that this framework is poised to shape the future of web development. ### Pixel-Perfect Flutter UIs with Figma, Maestro, and Dart MCPs Source: https://verygood.ventures/blog/pixel-perfect-flutter-designs-with-figma-and-maestro/ Building a Flutter screen from a Figma design is a tedious squint-and-compare cycle. So are tracking down UI bugs or refactoring screens that have drifted from their designs. Read the design, write or adjust the widget, run it, eyeball the result, tweak, and repeat. Now, AI agents can now run that whole loop themselves. They read Figma's structured design data, change the code, hot-reload, and visually verify the rendered result against the design with precision. The Figma, Maestro, and Dart [MCPs](https://modelcontextprotocol.io/specification/2025-11-25) are what make it work. If you're new to how MCP servers fit into the Flutter developer toolkit, see [7 MCP servers every Dart and Flutter developer should know](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/). ## How the loop works Together, these MCPs enable a five-step loop: read, write, reload, capture, and compare. ![The five-step agentic loop: Read, Write, Reload, Capture, Compare](/assets/images/blog/pixel-perfect-designs-with-figma-and-maestro/loop.avif) ### Figma The Figma MCP enables an agent to read directly from Figma files - extracting components, variables, layout data, and other design details. See the [official Figma MCP guide](https://help.figma.com/hc/en-us/articles/39216419318551-Get-started-with-the-Figma-MCP-server). ### Maestro With the Maestro MCP, agents can interact with a running device - scrolling, tapping, entering text, and even taking screenshots. See the [official docs](https://docs.maestro.dev/get-started/maestro-mcp).

Heads up: The Maestro MCP currently has some rough edges on Flutter web — several core interactions don't behave reliably with a browser window. For now, run your app on an iOS or Android emulator, a physical device, or a desktop target. Track progress in the Maestro issue tracker.

### Dart Finally, the Dart MCP allows agents to interact with the Dart VM and the running Dart process. This allows agents to hot-reload after making changes, enabling a lightning-fast validation path. ## Setting up the MCPs

Using the Very Good AI Flutter Plugin?

You already have all three! Skip ahead to the next section.

### Figma #### Install Install the Figma Plugin for Claude Code: ```bash claude plugin install figma@claude-plugins-official ``` #### Configure Authenticate with your Claude.ai account: 1. Launch a `claude` session and open the `/mcp` menu 2. Find the `figma` MCP server 3. If necessary, authenticate and connect to enable the server See the official [remote server installation guide](https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/). ### Maestro #### Install Install the Maestro CLI: ```bash curl -Ls "https://get.maestro.mobile.dev" | bash ``` The MCP server is bundled with the CLI - no separate install is needed. #### Configure Add the Maestro MCP to Claude Code: ```bash claude mcp add maestro -- maestro mcp ``` For full details, see the [official Maestro installation guide](https://docs.maestro.dev). ### Dart #### Install The Dart MCP server ships with the Dart SDK (Dart 3.9+) - no separate installation needed. #### Configure Add the Dart MCP to Claude Code: ```bash claude mcp add dart -- dart mcp-server ``` #### Connecting The Dart MCP connects via the Dart Tooling Daemon (DTD). Run your Flutter app with the `--print-dtd` flag so the DTD URI is printed to the console: ```bash flutter run --print-dtd ``` Look for a URI like: `ws://127.0.0.1:/`

Tip: You can use the bang (!) prefix to run the command directly in a Claude conversation; the output, including the DTD URI, will land in the conversation automatically:

! flutter run --print-dtd

## Running the loop For new UI, prompt your agent. Something like: > Implement the component(s) from this Figma design: [link]. Use the Figma MCP to read the design. Once you've written the widget(s), hot-reload the app with the Dart MCP and take a screenshot with Maestro, then verify it matches the design. Iterate until they are pixel-perfect. For an existing screen that has drifted from its design: > The [screen/component name] no longer matches this Figma design: [link]. Use the Figma MCP to read the design and the Maestro MCP to screenshot the current state. Identify the discrepancies, fix the widget(s), hot-reload, and verify. Iterate until pixel-perfect. From here, Claude Code runs the loop autonomously: 1. **Read** - The Figma MCP pulls the component tree, spacing, color values, and typography directly from the Figma file. No copying and pasting design specs. 2. **Write** - Claude generates and/or modifies your Flutter code. 3. **Reload** - The Dart MCP triggers a hot reload, applying the changes to the running app immediately and without losing app state. 4. **Capture** - The Maestro MCP interacts with the app to locate the elements, then captures a screenshot. 5. **Compare** - Claude compares the screenshot against the Figma design, identifies discrepancies, and goes back to step 2. In practice, the loop converges in two or three iterations for straightforward screens. The agent catches obvious mismatches - wrong padding, an off-brand color, a font weight that didn't apply - whether it's building a screen from scratch or fixing one that has slipped out of sync. ## Wrapping up The Figma, Maestro, and Dart MCPs turn what was a tedious manual process into a tight agentic loop. Figma eliminates the guesswork on the designs. Dart keeps iteration fast. And Maestro closes the loop with visual verification, letting your agent confirm its own output. The result is less time shuttling between tools and more time on the work that actually requires your judgment. ## Further reading Check out our article on [Maestro MCP test automation](https://verygood.ventures/blog/maestro-mcp-claude-mobile-ui-test-automation/). ### Building Dashy Enterprise with GenUI + ADK Source: https://verygood.ventures/blog/dashy-app-adk/ ![Dashy Enterprise App chat bot returning genUI components from chat message](/assets/images/blog/building-dashy-with-adk/body_1.gif) From the above gif, you see a chat bot return a descriptive paragraph along with a rendered graph and other stats from tapping an "AI Insight" card with a statement regarding your company orders. Cool! Based on the user’s request, the AI agent was able to draw and populate these widgets with real data regarding their business. You may already know this is possible with GenUI. A few of us at VGV partnered with Google to build an example app for Google Cloud Next and create a demo of what GenUI looks like in a real business setting. We created a professional dashboard with an AI agent that generates charts, graphs, and other data-driven visuals on demand. The twist: in most prior GenUI examples, something _deterministic_ drove the schema — a backend service, a rules engine, a handwritten response. Here, the driver is a Google Agent Development Kit (ADK) reasoning over the live business data. Before jumping into the ADK aspect of this project, here are things we already know about using GenUI: - Component catalog: a reusable widget library for the agent to utilize and populate with information. - JSON schema: define the guidelines of the use of each catalog item for the agent with no embedded business logic. - A2UI (Agent-to-UI): JSON message protocol that carries a widget tree from server to client as a pair of messages. `createSurface` (sets up container to render upon) and `updateComponents` (populate that surface). The agent and the UI need this protocol to decide what to show and how to draw it. ### How using ADK differs from plain GenUI In a GenUI-powered feature a client sends a request, a backend handler runs business logic, and code then deterministically constructs the A2UI JSON. With the ADK, the agent is authoring the schema in real time. In this project, Gemini 2.5 on Vertex AI decides both what to say and which component to render, with an **action-object matrix** in the system prompt acting as its style guide. The matrix is a literal markdown table embedded in the system prompt. Each row maps an intent (an action paired with an object) to a prioritized chain of catalog components: | Action | Object | Primary → Secondary → Optional | | ------- | ----------- | ---------------------------------------------------- | | view | zone/region | BarChart → RecommendedActions | | view | expense | ExpensesCategories → ApprovedExpenses | | compare | employee | ApprovedExpenses → RecommendedActions | | track | order/AOV | AovAnalysis | | filter | expense | RadioOptions → ApprovedExpenses → RecommendedActions | When the agent classifies a question as _view · zone_, it knows to lead with a `BarChart` and follow it with `RecommendedActions`. No retrieval step, no embeddings — just a lookup table the LLM reads at inference time. It's actually pretty amazing how much logic the agent takes on its own. As developers, we can spend more time refining our catalog and design systems instead of spending an arduous amount of time dictating render rules. ### Function tools We defined a number of ADK `FunctionTool`s that wrap Postgres queries. The agent picks the tools, composes results, and emits the corresponding GenUI block in the same turn. Each tool is a plain Python function that reads session state (via `ToolContext`), enforces access rules, and returns a JSON-serializable dict: ```python async def _get_revenue_summary(tool_context: ToolContext) -> dict: """Fetch order revenue summary (total, count, average, change %). Admin only.""" mode = tool_context.state.get("profile_mode") if mode != ProfileMode.ADMIN.value: raise PermissionError("Revenue summary is only available in admin mode.") return await AppDependencies.instance().orders_use_cases.get_orders_revenue_info() ``` `tool_context.state` carries the session's `profile_mode`, `user_id`, `_insight_topic`, etc. so the tool has everything it needs to enforce permissions without the LLM having to pass them in. The docstring is part of the contract. ADK surfaces it to the model as the tool description, so it also works as prompt context. Tools are wrapped as `FunctionTool`s and collected in a single list: ```python from google.adk.tools import FunctionTool DATA_TOOLS = [ FunctionTool(func=_get_revenue_summary), FunctionTool(func=_get_order_volume_data), FunctionTool(func=_get_user_expenses), FunctionTool(func=_get_team_expenses), FunctionTool(func=_get_standout_expenses), FunctionTool(func=_get_user_expenses_aggregate), FunctionTool(func=_get_recent_team_expense_summary), FunctionTool(func=_get_employee_profile), FunctionTool(func=_get_topic_data), # ... ] ``` This tool list is handed to the ADK `LlmAgent` at construction time, along with the system prompt. Gemini sees each function's name and docstring as available tools and decides which to call. ### Our agent architecture For the Dashy Enterprise app, we have three main ADK agents — **chat, insights, and suggestions**. We didn’t want the frontend to know about these individual agents. From the backend we exposed a single A2A endpoint called an **orchestrator**. The Flutter client never sees the individual agents. This orchestrator passes information to the `metadata.agent` and delegates to `ChatRunner`, `InsightsRunner` or `SuggestionsRunner`. ![OrchestratorRunner architecture: a single A2A endpoint with one auth gate routes Flutter client requests to the Chat, Insights, or Suggestions ADK agents via metadata.agent, with ChatRunner invoking the Insights agent as an AgentTool](/assets/images/blog/building-dashy-with-adk/body_2.png) Here is what the `OrchestratorRunner` looks like. Also note that we only need to check for Auth once, instead of three times across the three sub-runners! ```python class OrchestratorRunner: """Unified runner that delegates to chat, insights, or suggestions runners.""" VALID_AGENTS = frozenset({"chat", "insights", "suggestions"}) def __init__(self, models: AgentModels, app_name: str): self._runners: dict[str, AgentRunner] = { "chat": ChatRunner(models, app_name), "insights": InsightsRunner(models, app_name), "suggestions": SuggestionsRunner(models, app_name), } async def execute(self, context: RequestContext, event_queue: EventQueue) -> None: _get_user_id(context) # centralised auth gate agent_type = _get_required(context, "agent") runner = self._runners.get(agent_type) if runner is None: raise ValueError(f"Unknown agent type '{agent_type}'.") await runner.execute(context, event_queue) ``` ### How it works: From chat input to backend A user asks "How are zones performing today?" In a single call to the model through the chat agent: 1. Calls `_get_revenue_summary(tool_context)` → gets back a dict with `totalAmount`, per-zone breakdown 2. Decides, per the action-object matrix, that _view · zone_ → `BarChart` → `RecommendedExpenses` 3. Emits: ```python $385,200 total revenue across all zones today. Top performer: Zone 03 - Uptown at $140K. {"BarChart": {"title": "Revenue by Zone", "data": [ {"day": "Zone 01", "value": 120000}, {"day": "Zone 02", "value": 95000}, {"day": "Zone 03", "value": 140000}, {"day": "Zone 04", "value": 30200} ]}} ``` **What the ADK is doing: the agent is picking, composing, and emitting in one request.** ### How it works: From agent to widget Once the chat agent emits a block like the "recommended expenses" block above, it doesn't ship straight to Flutter. The backend extracts the JSON by running `parse_genui_blocks`, then wraps each component in two A2UI messages (`createSurface` + `updateComponents`) and attaches them to the JSON-RPC response as `DataPart`s alongside the natural-language `TextPart`. On the Flutter side, the `ChatAgentClient` sends the response parts in order, dispatching text to the chat bubble and `DataPart`s to the genui package, materializing them into widgets from the catalog. **The frontend is completely passive in what is being rendered.** The agents in the backend did the hard part of determining how to shape the data. ```python # Backend: from text to A2UI parts cleaned_text, component_dicts = parse_genui_blocks(final_response) parts: list[Part] = [] if cleaned_text: parts.append(Part(root=TextPart(text=cleaned_text))) for component in component_dicts: for a2ui_msg in surface_messages_for_component(component): parts.append(Part(root=DataPart(data=a2ui_msg))) ``` ```dart // Frontend: from response parts to chat thread for (final part in result.parts) { switch (part) { case RpcTextPart(:final text): onTextMessage(TextMessage(text.trim())); case RpcDataPart(:final data): onA2uiMessage(A2uiMessage.fromJson(data)); } } ``` Here is a diagram to sum up the roundtrip flow: ![Diagram of flow end-to-end from user to agent and how the LLM returns genui components](/assets/images/blog/building-dashy-with-adk/body_3.png) Dashy Enterprise is an early experiment in schema-driven UI authored by an ADK agent. The patterns we landed on — a single orchestrator, a component catalog, a prompt that doubles as a router — will certainly evolve. Surely down the line we will determine what the most efficient practice is to manage multiple agents as the technology continues to grow. The ADK ecosystem is still finding its shape. But we have learned that pushing UI authorship into the model reduces a lot of traditional product work, and that's a trade off worth experimenting with even at this stage. ### Beyond the Chat Box: How We Built a Generative UI Wizard for GCN Source: https://verygood.ventures/blog/generative-ui-flutter-wizard-gcn/ Most AI-powered apps follow the same pattern: a text input at the bottom, a scrolling list of messages above, and a send button. It works. But it doesn't guide. For a fintech financial planning demo like GCN, where a confused user staring at a blinking cursor is the worst possible outcome, we needed something different. We needed a wizard. Here's the architectural breakdown of how we built GCN's AI-driven financial planning simulator with [GenUI SDK for Flutter](https://verygood.ventures/resources/genui/), where the LLM doesn't respond to the user — it builds the screen for them. ![Diagram of the AI wizard flow — LLM generates full Flutter UI screens instead of chat responses](/assets/images/blog/generative-ui-flutter-wizard-gcn/beyond-the-chat-box-gcn-hero.avif) ## The Core Idea: The LLM Is a UI Generator, Not a Chatbox The mental model shift that unlocked everything: stop asking the AI what to say, and start asking it what screen to show. In GCN, every AI response is a complete UI surface — a JSON-defined screen with sliders, selection cards, charts, and buttons. The LLM never returns raw text. It returns structured widget trees that Flutter renders as full-screen experiences. This is generative UI in practice: an AI-generated user interface, rendered natively at runtime. The user never sees a chat thread. They see a sequence of polished screens. They swipe forward. They answer. The AI generates the next screen. It's a wizard, powered by a language model. ## Step 1: Gate the AI Behind a Human Onboarding Flow Before the user ever touches AI, we collect two critical data points through a hand-crafted wizard. The flow is: IntroPage → PickProfilePage → WantToFocusPage → SimulatorPage. **PickProfilePage** asks one question: Beginner or Optimizer? It uses `PickProfileCubit` and renders `ProfileCard` widgets, with the `Next` button disabled until a selection is made. No skipping. **WantToFocusPage** presents focus areas (Retirement, Mortgage, Healthcare, and Spending) as tappable widgets with multi-selection. There's even a free-text custom option. This matters architecturally because by the time we call the LLM, we already know who the user is and what they care about. That data shapes the first user prompt, so the user doesn't have to type an initial message to start the conversation. The AI doesn't have to ask cold-open questions — it already has context. ## Step 2: Design a Closed Widget Catalog for Generative UI ![Closed Widget Catalog](/assets/images/blog/generative-ui-flutter-wizard-gcn/beyond-the-chat-box-gcn-widget-catalog.avif) The biggest mistake teams make with generative UI is letting the AI do anything. Unconstrained generation creates inconsistent, unpredictable UIs. The answer is a closed catalog — a server-driven UI (SDUI) constraint applied to LLM output. For a hands-on parallel example, see our [Flutter GenUI tutorial: build an AI shopping assistant](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/). We defined exactly 28 widget types the LLM is allowed to use, grouped by role: * **Container Components** (root layout) * QuestionContainer: for focused questions * SummaryContainer: for the final summary * SectionCard: groups related content * **Interactive Inputs** (write to a data model, don't dispatch events) * GCNSlider * RadioCard * EmojiCard * FilterBar * **Action Widgets** (read-only, can bind to data model paths) * SectionHeader, MetricCard, and InsightCard * Charts: LineChart, BarChart, PieChart, SparklineCard, and ProgressBar * Tables: RankedTable, ComparisonTable, and TransactionList * NextStepsBar (sticky footer on the summary screen) The catalog is registered with a unique ID, and the LLM must reference it. Unknown widget types are rejected. For readers curious about hardening this generative UI framework further, our post on [Genkit for Flutter: flows, middleware, and tools in a GenUI shopping assistant](https://verygood.ventures/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/) covers layering middleware and structured output on top of a GenUI catalog. ## Step 3: Build the Prompt as a Rulebook, Not Instructions The system prompt in `lib/simulator/prompt/prompt.dart` isn't a description of the app — it's a strict contract the LLM must follow. This rulebook framing reflects the same "Structured Creativity" approach we described in [Generative UI for adaptive Flutter experiences](https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/). ``` - Each step MUST create a NEW surface with a unique surfaceId - Root component MUST be QuestionContainer or SummaryContainer - ALL text MUST be inside surface JSON — no markdown, no prose outside blocks - Output format: ONLY JSON blocks, nothing between them ``` The contract depends on the Catalog, DataModel, and SurfaceController abstractions described in the [GenUI SDK components and concepts](https://docs.flutter.dev/ai/genui/components) documentation. If you want to reproduce the setup, the [GenUI SDK for Flutter (get started)](https://docs.flutter.dev/ai/genui/get-started) guide is the fastest path. The first user message is constructed programmatically from the onboarding selections: ``` "Hi! I'm new to financial planning. I want to focus on: - everyday spending - saving for retirement Guide me step by step..." ``` The LLM receives this and generates Surface 1 — a welcome screen with goal confirmation. From there, every "Next" button tap sends the user's data back to the LLM, which generates Surface N+1. ## Step 4: Architect the State Machine with flutter_bloc The simulator's state lives in `SimulatorBloc`, and it's a proper state machine, not a simple `isLoading` toggle. We lean on the [`flutter_bloc` package](https://pub.dev/packages/flutter_bloc) for the same reasons we describe in [why we use flutter_bloc for state management](https://verygood.ventures/blog/why-we-use-flutter-bloc/): sealed-class states and a clean event/state contract. Here's the core state shape: ```dart class SimulatorState { List> pages; // Each page = one full-screen step int currentPageIndex; // Currently visible int? pendingPageIndex; // Deferred navigation (LLM still generating) bool isLoading; bool showLoadingOverlay; SimulatorStatus status; SurfaceHost? host; } ``` Each page contains a list of `DisplayMessage` items, which are a sealed class: ```dart sealed class DisplayMessage UserDisplayMessage(text) // What the user submitted AiTextDisplayMessage(text) // Rare transitional text AiSurfaceDisplayMessage(surfaceId) // The actual rendered surface ``` In practice, nearly every page is a single `AiSurfaceDisplayMessage`. The list structure supports edge cases (text before a surface generates, multi-surface pages). The bloc responds to 8 event types: `Started`, `MessageSent`, `SurfaceReceived`, `ContentReceived`, `Loading`, `LoadingOverlayRequested`, `ErrorOccurred`, and `Retried`. ## Step 5: Handle LLM Latency with Deferred Navigation This is the subtlest and most important pattern in the whole app. When the user taps "Next", we send their data to the LLM. The LLM takes 1–3 seconds to generate the next surface. What should the user see? The naive answer is to show a loading spinner and blank the current screen. That breaks the wizard feel. Instead, we show the current screen exactly as-is, with a "thinking" animation overlaid. When the surface arrives, we animate to the new page. ```dart // In SimulatorBloc._onSurfaceReceived() if (state.isLoading) { // LLM still streaming — defer navigation emit(state.copyWith(pendingPageIndex: newPageIndex)); } else { // LLM done — navigate now emit(state.copyWith(currentPageIndex: newPageIndex)); } ``` The view watches for `pendingPageIndex` becoming non-null (loading complete) and triggers the page transition. The user always has something to look at. ## Step 6: Animate Between Pages Like a Wizard, Not a Router Flutter's standard `Navigation.push` uses route transitions designed for hierarchical navigation, not lateral wizard steps. We replaced it entirely with a [PageView](https://api.flutter.dev/flutter/widgets/PageView-class.html) and a custom scroll physics that makes transitions feel like turning pages. The `_FadingPageView` widget applies a compound animation during scroll: ```dart // As page scrolls away: opacity: lerpDouble(1.0, 0.0, progress * 2) // Fades out at 50% scroll transform: translateY(lerpDouble(0, 40, curve)) // Arcs downward ``` The result: pages don't slide in from the side like a browser navigation. They *dissolve upward* as the new page rises to meet them. It feels like a native wizard flow, not a web navigation. ## Step 7: Make Widgets Reactive to User Input One useful pattern: display widgets can bind to the data model that input widgets write to. The Flutter team's post on [rich and dynamic user interfaces with Flutter and generative UI](https://blog.flutter.dev/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui-178405af2455) introduces the DataModel concept that makes this possible. For a broader conceptual grounding, our explainer [what is GenUI and why it matters in 2026](https://verygood.ventures/blog/what-is-genui-how-generative-user-interfaces-will-redefine-hospitality-in-2026/) walks through reactive binding in plain language. A `SectionHeader` on the summary screen can show the user's selected value: ```dart { "type": "SectionHeader", "title": "Your Monthly Budget", "subtitle": { "path": "/income_slider/formattedValue" } } ``` When the user moves the `GCNSlider`, it writes to `/income_slider/formattedValue`. The `SectionHeader` reactively updates. The LLM doesn't need to round-trip for this — it's all client-side data binding within the surface. Each widget carries its own `DataContext`. When the user selects "Continue," all data in the `DataContext` is gathered and integrated into the chat history as part of the user's message (managed by GenUI). ## Step 8: Handle the Final Step with Drama The final screen before the summary deserves special treatment. The `AppButton` in that surface includes a tag: ```dart { "type": "AppButton", "label": "See My Plan", "showLoadingOverlay": true } ``` When tapped, it triggers a full-screen loading overlay with a Rive animation. The LLM generates the summary surface behind the scenes. When it's ready, the overlay dissolves and the summary dashboard appears. This is the moment of reveal. The loading isn't a bug — it's a beat. ![The GCN widget catalog rendered at runtime — interactive sliders, radio cards, and charts generated by the LLM](/assets/images/blog/generative-ui-flutter-wizard-gcn/beyond-the-chat-box-gcn-widget-catalog-in-action.avif) ## Step 9: Build an Error Recovery Path LLM calls fail. Network requests fail. We handle both without losing the user's progress. When an error occurs, `SimulatorBloc` emits a state that displays `_ErrorView` with a retry button. The retry event: * Finds the last page with rendered content * Resets `currentPageIndex` to that page * Sends the full conversation history back to the LLM with a "please continue" prompt The conversation history is preserved throughout, so the retry picks up exactly where it failed. The user doesn't restart from zero. ## What This Architecture Gets You ![Standard chat UI vs. GCN generative UI wizard — Flutter mobile side-by-side of AI chat and structured wizard](/assets/images/blog/generative-ui-flutter-wizard-gcn/beyond-the-chat-box-gcn-chat-vs-wizard.avif) In practice, these nine steps combine to create a radically different user experience. Compared to a standard chat UI: | Standard Chat | GCN Wizard | | --- | --- | | User reads messages | User takes actions | | Open-ended input | Constrained, guided choices | | Scrollable history | One focused screen at a time | | AI responds to prompts | AI generates surfaces | | You see everything AI said | You see only what matters now | | Works for everything | Optimized for the specific flow | The tradeoffs are real: this approach requires more work to build and is more rigid to change. Adding a new widget type means updating the catalog, the prompt, and the system. But for a demo where you know the user journey and need every step to land, the control is worth it. This is why the pattern earns its cost on known, high-stakes user journeys — fintech onboarding, guided planning, structured intake flows — and why it is the wrong choice for open-ended exploration. The wizard pattern fits when the user's path is predictable enough to design for. The chat-first integrations that ship with most GenUI examples still fit better when the user's goal is discovery. ## The Tech Stack * **Flutter** with `fvm flutter` ([FVM](https://fvm.app)) for version management * **Firebase AI/Gemini Flash 3** for the LLM — see [Firebase AI Logic: get started with the Gemini API](https://firebase.google.com/docs/ai-logic/get-started) for the integration path * **GenUI** for surface rendering — [genui Flutter package](https://pub.dev/packages/genui) on pub.dev and VGV's [GenUI: AI-driven generative user interfaces for real-time adaptive experiences](https://verygood.ventures/resources/genui/) resource page. The SDK is in active development; check the pub.dev changelog for current stability before adopting in production. * **BloC** pattern throughout ([`flutter_bloc`](https://pub.dev/packages/flutter_bloc)) * **Rive** for the thinking animation — [Rive Flutter runtime](https://help.rive.app/runtimes/overview/flutter) This stack is a clean example of Flutter AI integration end-to-end: structured output from an LLM, rendered by a Flutter-native generative UI framework, managed by `flutter_bloc`, with animation handled by Rive. The full repository structure: ``` lib/ ├── onboarding/ # Hand-crafted wizard (intro, profile, focus) │ ├── intro/ │ ├── pick_profile/ │ └── want_to_focus/ ├── simulator/ # AI-driven simulator │ ├── bloc/ # SimulatorBloc, state, events │ ├── catalog/ # 28 widget type definitions │ ├── prompt/ # System prompt + initial message builder │ ├── repository/ # GenUI conversation management │ └── view/ # SimulatorView, message bubbles, animations └── design_system/ # Colors, themes, 28 base widgets ``` The lesson we kept coming back to: **the best AI interfaces are the ones that don't look like AI interfaces**. They look like well-designed apps that happen to be infinitely configurable at runtime by a language model. Chat boxes are one pattern. Wizards are another. The architecture that earns user trust is whichever one disappears into the experience. ### Shorebird: Enterprise Flutter Releases Don’t Have to Take Weeks Source: https://verygood.ventures/blog/shorebird-enterprise-flutter-releases-dont-have-to-take-weeks/ Imagine your Flutter app ships a checkout bug on a Friday afternoon. The fix is a two-line Dart change. You know exactly what it is. You have it committed, reviewed, and ready to go in under an hour. Then you wait. A day for the build. A day for QA sign-off. Two to three days for App Store review. Another day for staged rollout. By the time users are running the patched version, you've spent nearly a week, and your support queue has been full since Saturday morning. This is not a hypothetical. It's the default enterprise Flutter release cycle. And it's the problem [Shorebird](https://shorebird.dev/) was built to solve. ![The bug in production: three items totaling $112.97 display as $79.99. Users are undercharged — and the team doesn't find out until the support queue fills up](/assets/images/blog/enterprise-flutter-releases-dont-have-to-take-weeks/image1.png) *The bug in production: three items totaling $112.97 display as $79.99. Users are undercharged — and the team doesn't find out until the support queue fills up.* ## The Traditional Enterprise Flutter Release Cycle Before we look at what changes, it helps to be precise about what the baseline actually looks like. Enterprise teams don't just run `flutter build` and submit. There are governance layers, QA gates, and rollout controls that exist for good reason. Here's a representative pipeline: | Stage | What happens | Typical duration | | ----- | ----- | ----- | | **Code freeze** | Feature branch merges, dependency locks | 1–2 days | | **Build & sign** | Release build, signing, artifact packaging | 1–2 hours | | **QA cycle** | Regression testing, device matrix, UAT | 2–3 days | | **Store submission** | Upload to App Store Connect / Play Console | 1–2 hours | | **Review** | App Store review (Apple); Google review | 1–3 days (Apple) / hours–1 day (Google) | | **Staged rollout** | 5% → 25% → 100% user rollout with monitoring | 2–5 days | | **Observation** | Crash rates, session metrics, support volume | Ongoing | For a planned feature release, this pipeline makes sense. For a one-line critical fix, like a broken payment flow or an incorrect compliance disclosure, each one of those stages is an obstacle standing between your team and your users. **Traditional hotfix timeline:** 5–10 business days from "fix is ready" to "all users are running the fix." In an incident, every hour the bug stays live is another hour of lost revenue, damaged user trust, and mounting support volume. Days are not an acceptable unit of measurement for a broken checkout flow. ## Where Shorebird Fits In the Stack Shorebird builds release infrastructure for Flutter teams. Code push is its flagship capability: after your app is installed on a user's device, Shorebird can deliver Dart-level patches that install in the background and apply on the next app restart, without going through the app store. This is compliant with both the App Store Review Guidelines and Google Play policies — patches update only the Dart layer, not native binaries, which falls within the permitted over-the-air update category. A few things are important to understand before we get to the workflow: **What can be patched:** * Any Dart code change. Logic fixes, UI corrections, text changes, API integration updates. **What cannot be patched:** * Native plugin changes (anything that modifies Android or iOS native code, including method channels) * App icons or native assets * Changes to app permissions or capabilities If your fix touches Dart only, which most hotfixes do, it's patchable. ## The New Workflow: CI/CD \+ Code Push in Practice Here's the same pipeline with Shorebird integrated: | Stage | What happens | Typical duration | | ----- | ----- | ----- | | **Code freeze** | Feature branch merges, dependency locks | 1–2 days | | **Release build** | `shorebird release` instead of `flutter build` | 1–2 hours | | **QA cycle** | Regression testing, device matrix, UAT | 2–3 days | | **Store submission** | Upload to stores as before | 1–2 hours | | **Review \+ rollout** | Store review \+ staged rollout | 2–5 days | For the full release cycle, the pipeline looks roughly the same. The difference surfaces when something goes wrong after the release ships. **For a hotfix with Shorebird:** | Stage | What happens | With Shorebird | Without Shorebird | | ----- | ----- | ----- | ----- | | **Fix branch** | Dart-only fix, code review | 1–4 hours | 1-4 hours | | **Build** | `shorebird patch` vs. full release build | \~15 minutes | 1-2 hours | | **QA** | Targeted vs. full regression suite | 2–4 hours | 2-3 days | | **Delivery** | Patch rollout vs. store review \+ staged rollout | Hours to 1 day | 3-8 days | | | **Total** | **Same business day** | **5-10 business day** | **Shorebird hotfix timeline: same business day to next morning**. That's not a small improvement. For teams where incident SLAs are measured in hours, it's a structural change in what "incident resolved" means. The patch reaches the device without any store involvement. The app detects it in the background and surfaces a prompt on the next launch: ![Shorebird downloaded the two-line Dart fix silently in the background. One tap and a restart — no update prompt from the store, no waiting for review.](/assets/images/blog/enterprise-flutter-releases-dont-have-to-take-weeks/image2.png) *Shorebird downloaded the two-line Dart fix silently in the background. One tap and a restart — no update prompt from the store, no waiting for review.* The before and after, delivered same day: | ![Before](/assets/images/blog/enterprise-flutter-releases-dont-have-to-take-weeks/image1.png) | ![After](/assets/images/blog/enterprise-flutter-releases-dont-have-to-take-weeks/image3.png) | | ----- | ----- | | *Before — $79.99 (wrong)* | *After — $112.97 (patched)* | Shorebird's console gives you rollout controls comparable to what you'd configure in App Store Connect or Play Console, plus built-in patch analytics: * Set a rollout percentage (e.g., start at 10%) * Monitor crash rates and session metrics by cohort (patched vs. unpatched) * Track patch adoption across your user base * Increase rollout or roll back with a single action For enterprise teams that have rollback SLAs or staged rollout requirements, this is where you enforce them, independent of the store. ## What Changes for the Team (and What Doesn't) ### What changes **Branching strategy.** Add a patch branch convention. When a hotfix lands in `main` and the release is already live, cut a `patch/` branch from the release tag. Only Dart-only changes go here. This keeps the release history clean and makes it clear what each patch contains. **CI/CD pipelines.** Add a patch pipeline alongside your existing release pipeline. The patch pipeline is lighter: no full build matrix, targeted regression only, `shorebird patch` instead of `shorebird release`. **On-call runbook.** Update your incident response runbook. For Dart-only fixes, the hotfix path now goes through `shorebird patch` rather than an expedited store submission. Define the criteria clearly — "if the fix touches only Dart files in `lib/`, use the patch path", so engineers aren't making judgment calls during an incident. ### What doesn't change **QA gates.** Code push doesn't skip QA. It shortens the timeline because the patch scope is narrow (targeted regression rather than full regression), not because review disappears. Enterprise governance still applies; it just runs faster on a smaller surface. **Native changes.** If a fix requires a native code change, it goes through the store. No shortcut exists for that path, and none should. Shorebird doesn't change that constraint. ## The Honest Ceiling Shorebird is a significant operational improvement for the scenarios where it applies. It's worth being equally clear about where it doesn't apply. If your incident involves a native crash, a broken native plugin integration, a method channel change, or a permissions update, you're still going through the store. Plan for both paths. Teams that treat code push as a complete replacement for store release hygiene create a different class of problem, they let their release pipeline atrophy and then hit a native-layer incident with a rusty process. The strongest setup maintains both: a healthy store release pipeline for full releases and native changes, and a fast patch path for the majority of production fixes that touch only Dart. ## Getting Started [Shorebird's documentation](https://docs.shorebird.dev/) covers account setup, the CLI reference, and platform-specific release guides. If you're evaluating Shorebird for an enterprise Flutter app and want help structuring the integration — branching strategy, CI/CD pipelines, rollout governance — [reach out to the VGV team](https://verygood.ventures/contact-us/). This is the kind of delivery infrastructure work we do for enterprise clients, and it's significantly faster to get right the first time with a team that's done it before. ### Divine launched today. The interesting part isn't the nostalgia. Source: https://verygood.ventures/blog/divine-launches-vine-reboot-built-with-vgv/ This morning, [Divine](https://techcrunch.com/2026/04/29/jack-dorsey-backed-vine-reboot-divine-launches-to-public/), the Jack Dorsey-backed reboot of Vine, became publicly available on the App Store, Google Play, and Zapstore. Most of the headlines are about nostalgia: 500,000 restored six-second videos, OG creators like Lele Pons coming back, the early-2010s internet getting another shot. Nostalgia is the surface. The more interesting story is what Divine refuses to be. The team, led by Evan Henshaw-Plath ("Rabble"), built the platform to filter out AI-generated content. New videos must either be recorded directly in the app or come with [C2PA](https://c2pa.org/) provenance, an open standard that establishes how a piece of media was created and edited. The architecture runs on Nostr, with experiments underway on AT Protocol and (eventually) ActivityPub. There is no revenue model. The company is a public benefit corporation. In other words: an open-protocol, human-only video network, shipped at a moment when the rest of the feed is filling up with synthetic content. We helped build it. {% video url="https://www.youtube.com/watch?v=zUGnNIh60-0" title="Divine — Building a human-only video network with Very Good Ventures" %} ## A hybrid model Very Good Ventures came in shortly after the prototype phase. The initial codebase had been [vibe-coded](https://verygood.ventures/success-stories/divine/): moving fast, light on architectural patterns, exactly the kind of foundation that gets you to a working demo and then becomes hard to scale. Our job was to stabilize it, harden it, and prepare it for real users without killing the velocity that made it work in the first place. Ninety days later, the team had addressed 500+ issues across the application, established consistent engineering patterns, and shipped to early users on Zapstore. Today's public launch builds on that foundation. What made it possible was a workflow built around Claude. Claude Code accelerated PR generation and review. MCP integrations automated ticket creation and backlog refinement. Project rules enforced code quality, Nostr protocol compliance, and brand consistency. A small cross-functional group of engineers, testers, designers, a PM, and a program manager operated with the leverage of a much larger one. This is where the philosophy gets interesting. ## The line that isn't a contradiction Divine excludes AI-generated content from the user experience. We used AI extensively to build the user experience. Read quickly, that sounds like a contradiction. It isn't. The line Divine draws is between **the tools** and **the output**. We drew the same line. AI is allowed to make builders faster and the platform better. AI is not allowed to flood the feed with content that bypasses human creativity entirely. The first expands what people can ship. The second hollows out what's worth shipping. The way we put it in the case study: > The creativity is human. Claude just makes it ship faster. That isn't a slogan. It's an architectural decision. It shows up in the product (C2PA verification, in-app capture). It shows up in the process (AI in the dev loop, not in the content pipeline). It shows up in the business model (PBC, no ad-driven optimization for attention). ## Why it matters now A lot of social platforms are sliding toward a world where users can't tell what's real, creators can't compete with infinite synthetic supply, and the feed becomes a kind of statistical mush. Divine is a small bet in the other direction. It is also a useful proof point for how to build anything in 2026. AI acceleration is a question of *where* you put the leverage. Put it in the build pipeline and your team gets faster. Put it in the content pipeline and your product gets cheaper to fake. Divine put it in the right place. We were proud to help. --- *[Read the full case study →](https://verygood.ventures/success-stories/divine/)* *[Download Divine →](https://divine.video/)* ### GenUI Across Devices: What We Learned Building the Life Goal Simulator Source: https://verygood.ventures/blog/genui-across-devices/ ![Flutter's GenUI SDK rendering the VGV Life Goal Simulator on mobile and desktop simultaneously — the same adaptive financial dashboard on phone and monitor](/assets/images/blog/genui-across-devices/main.jpeg) For [Google Cloud Next 2026](https://cloud.withgoogle.com/next), we built the [Very Good Life Goal Simulator](https://gcn26demo.vgv.ai) — a multi-platform demo showcasing [Firebase AI](https://firebase.google.com/docs/vertex-ai-in-firebase) and [GenUI](https://github.com/flutter/genui) in a personal-finance dashboard. The app runs on Android and Web from a single Flutter codebase, and uses Generative UI to render AI responses as interactive widgets in real time: sliders, charts, filter chips, radio cards, and more. That last part is where the cross-device question gets interesting. When an LLM is composing your UI on the fly, how do you make sure those assembled surfaces look right on a 6-inch phone and a 27-inch desktop monitor? Here's what we learned. --- ## What GenUI Changes About Cross-Device Work With conventional Flutter development, cross-device layout is a bounded problem: you know the shape of every screen, and you design for each form factor. With GenUI, the LLM decides which widgets to render and how to compose them. Your `CatalogItem` definitions — the custom widget schemas you register — tell the model what it can use. But within those boundaries, the model chooses. A response might place three filter chips next to a sparkline chart, or surface a slider below a data table. The cross-device question is therefore not just "how do our screens adapt?" It's "how do AI-assembled surfaces adapt — reliably, across layouts we can't fully predict?" --- ## What We Tried First Before landing on our final approach, we ran a couple of spikes where we gave the LLM explicit device context — the screen size and the target platform (web or Android) — and let it decide which components to render based on that information. The results were interesting: the LLM was actually quite good at choosing the right components. But it didn't produce better results than responsive design would. Giving the model device information turned out not to be the advantage we expected, and it introduced two new problems. **Option 1: device-specific catalog items.** One approach was to define separate mobile and desktop variants of the same widget — `SparklineCard_mobile` and `SparklineCard_desktop`, for example — and instruct the LLM to pick the right one. The model could do this reasonably well, but the cost was a catalog that grew rapidly. A larger catalog means a larger system prompt, which eats into your context window and makes the prompt harder to maintain. **Option 2: serve a different catalog per breakpoint.** The other option was to swap the entire catalog at the repository level based on the current breakpoint. This avoided the bloated catalog problem, but introduced a worse one: it can't handle window resizing. If a user loads the app on a mobile breakpoint and then resizes their browser to desktop width, the LLM has already chosen mobile components for that surface — and it has no way to respond to the resize. The UI is stuck. That last point is important: **resizing is something you simply cannot solve with only an LLM.** A user can change their breakpoint at any time, and the model can't react to that. Flutter's constraint system can — because it re-evaluates layout on every build. That realization is what pushed us toward putting responsive behavior in the widgets themselves rather than in the model. --- ## How We Solved It The key insight was understanding the division of responsibilities: **the LLM decides which widget to show, what content to populate it with, and at what size — but the widget itself owns its internal layout and rendering.** Before diving into the problems we hit, one practical note on workflow: **we always tested catalog items in the dev menu before involving the LLM.** The app includes a component catalog where you can render each widget at different sizes in isolation. Only once a widget looked right across breakpoints did we test it end-to-end with the LLM. That two-step loop made iteration significantly faster. ### Problem 1: widgets were getting crushed on mobile The widgets didn't have responsive constraints, so on a phone screen they had nowhere to go. Charts and cards that looked great on a wide desktop viewport were too small to be usable on mobile. The fix was to give each catalog item explicit mobile and desktop size constraints. For example, the `BarChart` defines separate `barWidthMobile` (18px) and `barWidthDesktop` (46px) values with corresponding minimums, and uses `LayoutBuilder` with `.clamp(minBarWidth, maxBarWidth)` to compute the final bar width at runtime: ```dart final maxBarWidth = responsiveValue( context, mobile: _BarChartDimensions.barWidthMobile, // 18px desktop: _BarChartDimensions.barWidthDesktop, // 46px ); final minBarWidth = responsiveValue( context, mobile: _BarChartDimensions.barWidthMinMobile, // 6px desktop: _BarChartDimensions.barWidthMinDesktop, // 8px ); final barWidth = computed.clamp(minBarWidth, maxBarWidth); ``` ### Problem 2: the LLM was filling widgets with too much content Even after fixing the size constraints, content was overflowing — especially on mobile, where there's simply less space for text. The LLM had no awareness of how much room was available, so it filled widgets as if it were always rendering on a wide desktop screen. A `SectionHeader` subtitle that ran three sentences broke the visual rhythm of a question screen. A description field with too much text wrapped awkwardly inside a compact card. The widget rendered fine — the content didn't. The fix was to give the LLM explicit content rules in the system prompt alongside the widget schemas — capping `SectionHeader` subtitles to 1-2 sentences, and defining layout containers with explicit max widths (`QuestionContainer` at 650px, `SummaryContainer` at 1000px). Once we gave the LLM a defined structure with clear content expectations per slot, the overflow problems disappeared. ### Problem 3: the LLM wasn't using widgets as intended Even with size constraints and content rules in place, we found cases where the LLM used a widget correctly — but not in the way it was designed to work. The `SparklineCard` is a good example: the LLM was rendering a single card instead of the minimum two required for the component to make visual sense, and the container wasn't expanding to full width. The fix wasn't in the widget code — it was in the catalog rules. We updated the `SparklineCard` description in `finance_catalog.dart` to be explicit about usage: - **Before:** *"Use the SparklineCard widget to display a financial category with its amount and a trend sparkline."* - **After:** *"Use the SparklineCard widget to display financial categories, each with an amount and a trend sparkline — arranged in a horizontal row on desktop or stacked vertically on mobile. Always provide at least 2 cards in the `cards` array."* That one-line change was enough — and notice what else snuck in: *"arranged in a horizontal row on desktop or stacked vertically on mobile."* That's a responsive layout rule, delivered not in code but in the catalog description. The LLM now knows how the widget should behave across devices, without any changes to the widget itself. The catalog description is the LLM's only source of truth for how to use a widget — if the behavior isn't described there, the model will guess. The more precise the description, the more predictable the output across every device. **Prompt engineering and widget constraints are two sides of the same coin.** Constraints prevent widgets from rendering outside their safe range. Prompt rules prevent content from overflowing the space that remains. --- ## The Architecture That Made It Cheap to Iterate We updated catalog item constraints several times before landing on the right values. That iteration was fast because of one architectural decision: **all GenUI complexity lives inside `SimulatorRepository`**, behind a two-method API (`startConversation()` and `sendMessage()`). The bloc and UI layers never import GenUI directly. When we needed to tighten a constraint or adjust a prompt rule, we changed only the catalog or the `PromptBuilder` — no state management, no view code touched. Clean boundaries made the cross-device tuning loop fast. --- ## Key Learnings **The LLM decides content and size; your widgets own their internal layout.** The model's job is to choose which widget to show, what content to populate it with, and at what size. How that widget renders that content is Flutter's job. Keep those responsibilities separate. **Mobile and desktop constraints are not optional.** A GenUI surface can be rendered on any device at any width. Define explicit min/max values per breakpoint for every catalog item, and make them tight enough to always look good. **Prompt rules and code constraints work together.** Use the system prompt to control content density — short subtitles, focused questions, one idea per widget. Use code constraints to control size boundaries. Neither is sufficient alone. GenUI is a new way to compose UI, but it renders Flutter widgets — and Flutter widgets live in a constraint system. The principles that have always made Flutter multi-platform didn't change. Responsive design is still the right mental model; we just had to apply it in a new place. --- ## Try It Yourself The Very Good Life Goal Simulator is open source. Explore the full implementation — catalog items, size constraints, the repository boundary, and the prompt structure — in the [GitHub repository](https://github.com/VGVentures/genui_life_goal_simulator), and try the live app at [gcn26demo.vgv.ai](https://gcn26demo.vgv.ai). ### VGV Joins Automotive Grade Linux: Flutter Has Never Been More Ready For The Road Source: https://verygood.ventures/blog/vgv-joins-automotive-grade-linux-flutter-has-never-been-more-ready-for-the-road/ ![VGV Joins Automotive Grade Linux](/assets/images/blog/vgv-joins-agl/main.jpg) Flutter running inside a vehicle is a different engineering discipline than Flutter running on a phone. The display is a fixed panel, not a flexible canvas. The hardware is locked down and thermally constrained. The operating system is built for automotive grade reliability, not developer convenience. We’ve spent years working inside those constraints, [shipping production IVI software in Toyota vehicles](/blog/flutter-agency-8-year-leadership-journey/), building applications across BMW and MINI, and pushing Flutter’s render pipeline to its limits in NASCAR systems as part of [our partnership with the Trackhouse team](/success-stories/trackhouse-nascar-vip-experience/). **Today, we’re formalizing that work: VGV has joined Automotive Grade Linux (AGL).** > "Our work with VGV helped us to bring production-ready Flutter to in-vehicle infotainment. They understood from day one that automotive software is a different discipline, and they delivered accordingly. Their AGL membership is a natural extension of that work, and we look forward to continuing to build together." > > Chris Casey, Executive Director of Engineering, UX & In-Vehicle Software ## Why AGL, Why Now [Automotive Grade Linux](http://automotivelinux.org) is a shared, open source platform for automotive software development used by major OEMs and Tier 1 suppliers worldwide. ![Flutter-powered in-vehicle infotainment dashboard showing navigation, media, climate, and energy flow controls in a modern automotive cabin](/assets/images/blog/vgv-joins-agl/image4.avif) Flutter joined that story with AGL’s UCB 14 platform release, which formally added it as a supported framework for IVI development. Subsequent releases deepened the investment. For us, this trajectory confirmed what years of project work had already shown: Flutter delivers the reliability and performance that automotive HMI demands. AGL membership gives us working group access, reference hardware, and a direct channel into the development process, right where the hard problems are being solved. We joined to [contribute](/resources/open-source-packages/), not just to participate. ## The Work That Led Here Our AGL membership didn’t come from abstract interest in automotive software. It came from shipping production software in real vehicles. We partnered with Toyota to build Flutter-powered IVI systems, managing frame budgets on fixed hardware, adapting Flutter’s rendering pipeline for non phone displays, and meeting the certification standards automotive OEMs bring to their software supply chain. ![Production Flutter IVI display embedded in a vehicle dashboard, rendering navigation and trip information alongside the steering wheel](/assets/images/blog/vgv-joins-agl/image3.avif) Two members of our leadership team, Kevin Rogers and Jorge Coca, led Flutter implementation across BMW and MINI [before joining VGV](/about-us/). Two vehicle brands, different design languages, one shared platform architecture. Flutter’s [single codebase model](/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/) doesn’t just reduce development cost, it changes the engineering conversation between platform teams and brand UX teams entirely. We also partnered with the Trackhouse NASCAR racing team, where [real-time telemetry](/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/) has zero tolerance for latency. Every millisecond of render time is visible. That discipline shapes how we approach every performance critical Flutter engagement. We also contributed to Fluorite, the first console-grade engine fully integrated with the Flutter framework. This contribution is of tremendous importance in rendering 3D models inside the vehicle, as Fluorite is engineered to utilize Google’s Filament renderer and modern APIs like Vulkan to deliver hardware-accelerated, console-quality graphics optimized for lower-end and embedded automotive hardware. ## What We’re Building in the AGL Ecosystem VGV maintains a significant [open source presence](/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/) in the Flutter community. Packages, tooling, and infrastructure that other development teams rely on in production. We’re bringing that same approach to AGL: find shared engineering problems, build solutions in the open. The priorities: compositor level work with Wayland and Weston, performance tooling for resource constrained automotive hardware, and reference implementations that help teams adopt Flutter for IVI without rebuilding integration work from scratch. ![Diagram of three automotive software pillars VGV brings to the AGL ecosystem: IVI interface and user experience, advanced vehicle engineering and integration, and performance telemetry and analytics](/assets/images/blog/vgv-joins-agl/image2.avif) *"We are pleased to welcome Very Good Ventures to the Automotive Grade Linux community"*, said Dan Cauchy, Executive Director of Automotive Grade Linux. *“Their contributions to the Flutter ecosystem, including their work on Fluorite, reflect the collaborative spirit that drives AGL forward. We look forward to their participation and the technical contributions they will bring to the community."* ## The GenUI Horizon Flutter’s rendering model is a natural fit for [Generative UI](/resources/genui/) interfaces that adapt dynamically based on driver preferences, route context, or live vehicle data. In practice: [in-vehicle experiences that reconfigure](/blog/flutter-genui-meets-the-vgv-architecture/) based on who’s driving, what’s active, or what the vehicle needs to surface. Not just responsive to input responsive to state. We’ve been developing these ideas in our project work. AGL is the right community to work through them in the open, alongside the OEMs and suppliers who will actually deploy them. ## See Us in Tokyo We’ll be presenting next month at the [AGL All Members Meeting in Tokyo](https://events.linuxfoundation.org/agl-amm/): *One Codebase, Every Screen: Architecting Multi-Display Flutter Experiences Across Automotive.* Come find us. ![Keynote speaker presenting Flutter automotive architecture on a large stage, illustrating the AGL All Members Meeting in Tokyo](/assets/images/blog/vgv-joins-agl/image1.avif) ## Let's Build It Together The software defined vehicle is already here. Flutter’s position in that reality is growing because teams are shipping real software on real hardware inside real vehicles. Our AGL membership is the next step in years of production automotive Flutter work. **If you’re building [Flutter for automotive, embedded Linux, or in-vehicle infotainment](/our-services/application-development/), we want to talk. Reach out to start the conversation.** ### Exploring Cow: A Local AI Project That Brings Fun and Functionality to Your Computer Source: https://verygood.ventures/blog/joanna-may-very-good-ventures-building-a-local-ai-from-scratch-the-story-behind-cow/ {% video url="https://www.youtube.com/watch?v=HI46ij50yiA" title="Joanna May, Very Good Ventures — Building a Local AI from Scratch: The Story Behind Cow" %} In the digital landscape where cloud computing and AI are becoming increasingly dominant, the emergence of local AI solutions is a refreshing innovation. In the inaugural episode of *Very Good Engineering*, hosts Jorge Coca and Joanna dive into Joanna's latest pet project, Cow, designed to serve as a humble AI for personal use. This blog post will explore the journey of creating Cow, the lessons learned, and the fascinating intersection of game development and app engineering. ## The Genesis of Cow Joanna shares how her passion for building tools led her to create Cow. Initially inspired by the high costs associated with using cloud-based AIs like ChatGPT, she sought to develop a local alternative. "Building a local AI turned out to be really hard," Joanna admits, highlighting the technical challenges she faced. Despite the obstacles, the project became a testament to her perseverance and ingenuity. ## Understanding Cow Cow operates as a terminal interface that allows users to interact with a local AI model. Drawing comparisons to existing AI tools, Joanna explains that while Cow may not possess extensive knowledge like its cloud counterparts, it provides a unique, accessible experience. "It's like a cloud code or codex, but running locally," she states, making it an exciting option for those interested in local AI solutions. ## The Technology Behind Cow Joanna built Cow using Dart, leveraging the Nocturne library to create a terminal user interface. The project required her to integrate various components, including establishing connections with local AI models. "It's just like using Flutter, but for the terminal," she notes, emphasizing the familiar environment Dart provides. The development process also involved overcoming challenges with native bindings and package compatibility, showcasing Joanna's determination to make Cow a reality. ## The Unique Charm of Cow What sets Cow apart is not just its functionality but also its personality. Joanna took the time to create a charming ASCII art cow to serve as the project's mascot. "I had to do the ASCII art by hand," she shares, revealing the effort behind the cow's design. This attention to detail adds a fun element to the project, making it more engaging for users. ## Conclusion Cow exemplifies the spirit of ingenuity and creativity in technology. By merging her experiences in Flutter and game development, Joanna has created a local AI solution that is both functional and enjoyable. As AI continues to evolve, projects like Cow remind us of the importance of accessibility and personalization in technology. Key takeaways from this episode include the value of local AI solutions, the challenges of software development, and the joy of creating something uniquely yours. ### The Journey of a Self-Taught Developer: Navigating the Flutter Ecosystem Source: https://verygood.ventures/blog/rody-davis-google-from-self-taught-to-google-engineer/ {% video url="https://www.youtube.com/watch?v=MiNrQ6svgGw" title="Rody Davis, Google — From Self-Taught to Google Engineer" %} In today's tech landscape, many developers start their journeys in unconventional ways. One such inspiring story is that of Rody Davis, a self-taught developer who embarked on a remarkable journey from Apps Script to becoming a key player in the Flutter ecosystem. In this post, we'll explore Rody's experiences, insights, and the exciting developments within the Flutter community, particularly focusing on the innovative platform, Antigravity. ## About Rody Davis Rody Davis is a Senior Developer Advocate at Google who has made significant contributions to the Flutter community. Starting as a self-taught programmer, Rody's passion for coding led him to explore various technologies, ultimately landing him a role at Google. His journey exemplifies how determination and a willingness to learn can lead to remarkable achievements in tech. ## Discovering the Power of Coding Rody's journey into coding began unexpectedly. Growing up without any developers in his circle, he found himself drawn to technology and problem-solving. While working at a company with a cumbersome manual process, he realized that coding could simplify and automate tasks. This epiphany led him to discover Google Apps Script, which became his gateway into the coding world. ### The Learning Curve - **Self-Education:** Rody taught himself coding through various resources, including books and online tutorials. He highlights the importance of perseverance and curiosity in overcoming challenges in learning programming languages. - **First Projects:** His first significant project involved automating calendar events and forms using Apps Script, which opened his eyes to the potential of coding in solving real-world problems. ## Transitioning to Mobile Development After gaining confidence in coding, Rody ventured into mobile app development, motivated by a desire to create music applications. Inspired by an Apple keynote featuring a self-taught coder, he set out to learn iOS development, eventually building his first application using Swift. This experience laid the groundwork for his exploration of cross-platform development. ### Embracing Flutter - **Why Flutter?** Frustrated with the limitations of developing for iOS and Android separately, Rody discovered Flutter. He was impressed by the rapid development capabilities and the ability to maintain a single codebase for both platforms. - **First Flutter App:** Rody's first project with Flutter was a music app prototype that he built in just six weeks, significantly faster than his previous native apps. This experience solidified his passion for Flutter and prompted him to participate in the Flutter Create contest, where he won recognition for his project, Pocket Piano. ## The Rise of Antigravity As a Senior Developer Advocate at Google, Rody is now involved with Antigravity, an innovative platform designed to enhance the development experience with multi-agent capabilities. ### What Makes Antigravity Unique? - **Intuitive IDE:** Antigravity offers a familiar interface similar to VS Code, making it accessible for developers transitioning to this new environment. - **Multi-Agent Development:** The platform allows developers to manage multiple agents working on different tasks simultaneously, optimizing workflow and enhancing productivity. - **Integration with Google Services:** Antigravity seamlessly connects with various Google services, empowering developers to leverage the latest technologies and tools in their projects. ## Key Takeaways from Rody's Journey Rody's story serves as a testament to the power of self-education, passion, and adaptability in the ever-evolving tech world. Here are some key lessons: - **Never Stop Learning:** The tech landscape is constantly changing, and staying updated with the latest trends and tools is crucial. - **Embrace Challenges:** Overcoming obstacles is part of the journey. Whether it's learning a new programming language or tackling complex projects, persistence is key. - **Community Matters:** Engaging with the developer community, participating in contests, and sharing knowledge can open doors to new opportunities. ## Conclusion Rody Davis's journey from a self-taught developer to a key player in the Flutter ecosystem is truly inspiring. His story highlights the importance of curiosity, continuous learning, and community engagement in a developer's career. As technology continues to evolve, platforms like Antigravity are paving the way for innovative development experiences, making it an exciting time for developers. ### Introducing Very Good Engineering: A Software Engineering Podcast Source: https://verygood.ventures/blog/very-good-engineering-podcast/ ![Very Good Engineering podcast launch — a recording desk with microphone and notebook, representing the practitioner-focused engineering podcast from Very Good Ventures](/assets/images/blog/launching-very-good-engineering-the-podcast-from-engineers-for-engineers/main.jpeg) ## Introducing Very Good Engineering: A Software Engineering Podcast Great engineering stories rarely make it past the pull request. The debugging session that taught a team something new. The architecture decision that saved a product. The weird hack that turned into a pattern the whole community now uses. These stories live in Slack threads, conference hallways, and late-night pair programming sessions. Most of them never leave the room. We want to change that. Today we're launching **Very Good Engineering**, a software engineering podcast from Very Good Ventures where engineers sit down with other engineers to talk about the work itself: the products they're building, the techniques they're refining, and the problems they're losing sleep over. No buzzwords. No vendor pitches. Just the kind of conversation you'd have with a teammate who actually knows what they're doing. ## Why we're launching a Flutter podcast now We've been part of a lot of engineering conversations lately — internal retros, conference hallways, client kick-offs — and the same questions keep coming up in different forms. How do you actually ship AI features that hold up? What does good architecture look like when the stack is changing underneath you? What can game developers teach mobile engineers? These are the conversations we want to have on tape. The best antidote to hype is a conversation with someone who's shipped the thing. Someone who can walk you through the trade-offs they made, the tools they chose, and the reasons why. Whether the topic centers on AI or not, what matters is that it makes you want to go build something. That's the bar we're setting for every episode. ## What to expect Very Good Engineering features guests from inside VGV and across the broader engineering community — you can browse all our shows on the [Very Good Engineering podcasts](https://verygood.ventures/blog/?tag=Very%20Good%20Engineering) page. Expect deep dives into products, frameworks, and techniques from practitioners who lead VGV's [Flutter development services](https://verygood.ventures/our-services/application-development/) and work alongside the broader Flutter, Dart, and AI communities. Expect honest takes on what's working and what isn't, including the calls that didn't pan out and the constraints that shaped the final design. Expect the occasional tangent. The best conversations don't follow a script. We're launching with two episodes: **Rody Davis (Google)** — Rody is a prolific engineer and creator whose work spans Flutter, audio programming, and developer tooling. In this episode, we discuss [Google Antigravity IDE](https://docs.flutter.dev/tools/antigravity), developer experience, and how tooling shapes the software we ship. You can also [read the full conversation with Rody](https://verygood.ventures/blog/rody-davis-google-from-self-taught-to-google-engineer/). {% video url="https://www.youtube.com/watch?v=MiNrQ6svgGw" title="Rody Davis, Google — From Self-Taught to Google Engineer" %} **Joanna May (VGV)** — Joanna leads game engineering work at VGV and is a core maintainer on [Chickensoft](https://chickensoft.games/), the open-source toolkit for building games with Godot and C#. We explore game development, open source, and what mobile and web engineers can learn from game development practices. You can also [read the full conversation with Joanna](https://verygood.ventures/blog/joanna-may-very-good-ventures-building-a-local-ai-from-scratch-the-story-behind-cow/). {% video url="https://www.youtube.com/watch?v=HI46ij50yiA" title="Joanna May, Very Good Ventures — Building a Local AI from Scratch: The Story Behind Cow" %} Both episodes are available now, wherever you get your podcasts. ## What's next We're publishing a new episode every other week, with guests already lined up from across the Flutter, AI, and broader software engineering communities. Future episodes will cover Flutter architecture, Dart tooling, open-source engineering, and the AI systems our team and our partners are shipping. If there's an engineer whose work you think belongs on this show, tell us. Welcome to Very Good Engineering. The pull request is closed. These stories are yours now! ### Why Securing Your App Matters — And How to Do It with Firebase App Check Source: https://verygood.ventures/blog/why-securing-your-app-matters-and-how-to-do-it-with-firebase-app-check/ ![Flutter app labeled as a verified client sending requests through a Firebase App Check token validation shield, blocking an unauthorized bot](/assets/images/blog/why-securing-your-app-matters-and-how-to-do-it-with-firebase-app-check/main.jpg) Building a Generative UI app powered by Firebase AI is a significant technical achievement — but shipping a feature-rich application without a proper security strategy exposes you to real and costly risks. This includes unauthorized access to your backend resources, but also more subtle attack vectors: for example, bad actors sending an excessive number of requests directly to your LLM, burning through your token quota and driving up costs before you even notice. We'll walk through why application-level security goes beyond user authentication, and share a practical guide to implementing Firebase App Check to protect your app from unauthorized access and abuse — based on our experience securing a real Generative UI app, including the decisions we made and the gotchas we ran into along the way. ## The Threat You're Not Thinking About When most developers think about security, they jump straight to authentication. "Users need to log in before they can access anything — we're safe." That's a great start, but it's not the whole picture. Firebase credentials — API keys, project IDs, app IDs — are embedded in your web app and visible to anyone who inspects the source. A motivated attacker can extract them and use them to send requests directly to your backend — consuming your LLM token quota, triggering charges on paid Firebase services, or polluting your database with fake data. This isn't hypothetical. Teams have been caught off guard by Firebase bills in the thousands of dollars because their app was being scraped or abused by automated clients. **The core issue is that Firebase credentials are not secrets — they are identifiers.** Knowing them is enough to make requests, which is why verifying the origin of those requests is essential. ## What Is Firebase App Check? Firebase App Check addresses this by verifying where a request is coming from, not just who is making it. When a client runs your app, App Check uses a platform-level attestation provider to confirm the request originates from your legitimate app. That attestation is exchanged for an App Check token, which is automatically attached to every subsequent Firebase request. If the token is missing or invalid, the request is rejected before it ever reaches your backend — including your LLM. For web apps, the attestation provider is reCAPTCHA v3, which analyzes browser signals to determine whether traffic is coming from a real user in a legitimate browser, or from a bot or automated script. ## How We Secured Our GenUI Web App with App Check App Check was added to our project as a formal requirement during active development — not as an afterthought. The scope was deliberately focused: secure the web app to prevent credential theft and unauthorized AI requests. Since each platform requires its own attestation provider, our reCAPTCHA configuration only applies to web — iOS, Android, and macOS will each need their own providers configured if added to the distribution in the future. ### Step 1 — We Registered the Site in Google reCAPTCHA Admin We went to https://www.google.com/recaptcha/admin/create and filled in the following: - **Label**: anything descriptive, e.g. Finance App Web - **reCAPTCHA type**: Score based (v3) — free and sufficient for most use cases - **Domains**: all the domains where the app is hosted For our Firebase Hosting project, this meant adding all the domains that appeared in the Firebase console: | Domain | Purpose | |--------|---------| | your-app.web.app | Primary Firebase Hosting domain | | your-app.firebaseapp.com | Alternate Firebase Hosting domain | | Your custom domain (if any) | e.g. app.yourcompany.com | We checked the Firebase console to confirm which domains were registered and added all of them. Keep this list up to date as your project grows: if you deploy to a new domain, such as a Firebase preview channel, and forget to add it here, reCAPTCHA will silently reject requests from it with no obvious error pointing to the cause. We saved both the **Site Key** and the **Secret Key** generated at the end — we needed them in the next steps. ### Step 2 — We Registered the Web App in Firebase Console - We went to our Firebase project → Build → App Check → Apps tab - Selected the Web app - In the modal, chose reCAPTCHA v3 as the provider - Pasted the Secret Key from Step 1 - Clicked Save ### Step 3 — We Added the firebase_app_check Package In `pubspec.yaml`, we added the dependency: ```yaml dependencies: firebase_app_check: ^0.3.2 ``` **Note:** check [pub.dev](https://pub.dev/packages/firebase_app_check) for the latest version. Then ran: ```bash flutter pub get ``` ### Step 4 — We Initialized App Check in main.dart In our project, Firebase is initialized in a `main.dart` file. App Check must be activated immediately after `Firebase.initializeApp()` and before any other Firebase service is used: ```dart // Add this import import 'package:firebase_app_check/firebase_app_check.dart'; Future main({...}) async { final binding = WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); // Activate App Check right after initializeApp await FirebaseAppCheck.instance.activate( webProvider: ReCaptchaV3Provider( const String.fromEnvironment('RECAPTCHA_SITE_KEY') ), ); // ... } ``` **Never hardcode the Site Key directly in your source code.** We stored it in a `.env` file, added it to `.gitignore` to keep it out of version control, and passed it at build time via `--dart-define-from-file=.env`. ### Step 5 — We Tested Using the Debug Provider Before enabling enforcement, we verified that everything was working. Every team member needed to: 1. Run `flutter run -d chrome` — Firebase App Check prints a debug token to the console: ```sh App Check debug token: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ``` 2. Register their individual token in Firebase Console → App Check → Apps → Web app → Debug tokens → Add debug token. This allows the local development environment to pass App Check validation without a real reCAPTCHA attestation. 3. Store their own debug token in their `.env` file, and at build time pass it via `--dart-define-from-file=.env`. 4. Set the stored debug token for web, iOS, and Android in our main function from the environment variable. For example, `providerAndroid`: ```dart const debugToken = String.fromEnvironment('APP_CHECK_DEBUG_TOKEN'); await FirebaseAppCheck.instance.activate( providerAndroid: debugToken.isNotEmpty ? const AndroidDebugProvider(debugToken: debugToken) : const AndroidPlayIntegrityProvider(), ); ``` **Keep debug tokens private** — anyone who has one can bypass App Check entirely. For a full working example, see the [firebase_app_check example on pub.dev](https://pub.dev/packages/firebase_app_check/example). ### Step 6 — We Enabled Enforcement in Firebase Console Once we confirmed that App Check was working correctly, we enabled enforcement: - Firebase Console → App Check → APIs - Found Firebase AI and any other Firebase services in use - Clicked **Enforce** Once enabled, Firebase will begin rejecting requests that do not carry a valid App Check token — including unauthorized scripts attempting to use our credentials to make AI requests. Note that enforcement may take a few minutes to propagate. ## A Note on What App Check Doesn't Do With enforcement enabled, your app is significantly better protected — but it is worth being clear about what App Check covers and what it does not. - **It does not prevent all abuse.** A sophisticated attacker with a real browser and a real copy of your app can still make requests. App Check eliminates automated, script-based abuse — not every conceivable attack vector. - **It does not protect against misuse by legitimate users.** If an authenticated user abuses your app from a real browser, App Check will not stop them. Rate limiting and usage quotas are complementary tools for that scenario. ## Conclusion For AI-powered applications, App Check is one of the most important controls you can put in place. Without it, our Firebase credentials were all an attacker needed to start making LLM requests on our behalf — burning through our token quota before we even noticed. With Firebase App Check and reCAPTCHA v3 in place, only traffic originating from our legitimate web app can reach our backend. For the full Firebase App Check documentation, visit [firebase.google.com/docs/app-check](https://firebase.google.com/docs/app-check). ### Rive Animations + GenUI: Flutter Integration Guide Source: https://verygood.ventures/blog/rive-flutter-genui-integration/ ## Understanding Rive's State Machine Model When we built the Very Good Life Simulator for Google Cloud Next 2026, we faced a design challenge: Firebase AI and GenUI were generating the UI dynamically, but without a way to signal progress back to the user, the interface felt frozen during LLM inference. We chose Rive to handle that problem—its [state machine model](https://help.rive.app/runtimes/state-machines) let us decouple animation logic from widget rendering, so a designer could iterate on loading states without touching our Flutter code. Rive is an interactive animation tool that allows designers and developers to build animations with built-in logic. Rive uses a state machine model, making animations reactive and data-driven. Rive animations are exported as lightweight `.riv` files and run natively on Flutter, iOS, Android, web, and other platforms. The Rive Flutter package integrates seamlessly with the Flutter widget tree, treating animations as first-class UI components. Use Rive for interactive or state-driven animations designed by a dedicated designer. For simple, fire-and-forget animations, [Lottie](https://lottiefiles.com/blog/working-with-lottie-animations/lottiefiles-or-rive) is still a solid option. ## Core Concepts: State Machines, Transitions, and Inputs ![Rive state machine: boolean, number, and trigger inputs driving idle, loading, and success Flutter animation states](/assets/images/blog/rive-flutter-genui-integration/core_concepts.avif) The real power of Rive comes from its state machine design. A state machine consists of states, transitions, and inputs. Flutter interacts with it by reading and writing input values at runtime. Understanding this model is essential before diving into implementation. There are different types of state machine inputs: - **Boolean:** Toggle between two states (e.g., `isHovered`, `isActive`) - **Number:** Drive animations with numeric values (e.g., `progress 0.0-1.0`, `speed`) - **Trigger:** Fire a one-shot event (e.g., `tap`, `explode`, `reset`) This separation of data (inputs) from presentation (animation) is what makes Rive powerful in AI-driven contexts. The LLM controls the inputs; the designer controls the visual response. ## Basic Usage: Playing a Simple Animation The simplest way to embed a Rive animation is using the `RiveAnimation` widget. It handles loading, asset management, and rendering automatically. First, add the rive package to your `pubspec.yaml` file: ```yaml dependencies: rive: ^0.14.4 # Always use the latest stable version ``` Then, use the `RiveAnimation` widget to load and display an animation: ```dart import 'package:flutter/material.dart'; import 'package:rive/rive.dart'; class SimpleRiveWidget extends StatelessWidget { const SimpleRiveWidget({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: RiveAnimation.asset( 'assets/animations/my_animation.riv', animations: const ['idle'], // Animation clip name fit: BoxFit.contain, ), ), ); } } ``` The `RiveAnimation.asset()` constructor accepts several key parameters: | Parameter | Type | Description | | :---: | :---: | ----- | | animations | List\? | Named animation clips to play. If empty, plays the default | | stateMachines | List\? | State machine names to activate. Used for interactive animations | | alignment | Alignment | How the animation aligns inside its bounds | | onInit | OnInitCallback? | Callback fired when the Rive artboard is initialized | | controllers | List\? | Manual controllers for programmatic control | | artboard | String? | Name of the artboard to use (if the .riv has multiple) | ## State Machines and Interactivity Beyond static playback, [state-driven architecture](https://verygood.ventures/blog/why-we-use-flutter-bloc/) unlocks true interactivity by letting your Flutter code read and write animation inputs at runtime. Use `onInit` to capture `StateMachineController` references, then manipulate inputs from your Flutter widget. Always call `_controller?.dispose()` in the widget's `dispose()` method; the `StateMachineController` holds native resources that must be explicitly released, or you risk memory leaks under repeated navigation. ## Advanced Patterns Use [`RiveAnimation.network()`](https://help.rive.app/runtimes/overview/flutter) to load animations from a remote URL. When you need to pre-load animations or share assets across widgets, use `RiveFile` directly. Rive animations can also be driven by scroll position, sensor data, or any numeric value. Rive 0.12+ supports events; animations can fire named events back to Flutter. This is useful for triggering haptics, sound, or navigation at specific interaction moments. ## Best Practices ### Performance - Pre-load `.riv` files in `initState` or a `FutureBuilder`; never load them inside `build()` - Reuse `RiveFile` instances when displaying the same animation multiple times - Use `RepaintBoundary` around Rive widgets to isolate repaints from the rest of the tree - Prefer smaller, focused `.riv` files over large monolithic ones ### Architecture - Keep `StateMachineController` in `StatefulWidget` state, not in a BuildContext-dependent callback - Expose animation inputs through a `ChangeNotifier` or `Bloc` for testability - Use const constructors where possible to prevent unnecessary widget rebuilds ## How Rive Powers AI-Driven Interfaces: The Very Good Life Simulator To make this concrete, we partnered with Google to build the [Very Good Life Simulator](https://verygood.ventures/events/google-cloud-next-2026/), a financial planning app that combines Firebase AI, [Google's GenUI framework for AI-driven Flutter widget rendering](https://docs.flutter.dev/ai/genui), and Rive animations. The interplay between these three systems is where the state machine patterns above come alive. The app uses `rive: ^0.14.4`, which uses the new Rive Flutter API. The new API separates asset loading (`FileLoader`) from rendering (`RiveWidget`) and exposes lifecycle states as sealed classes (`RiveLoaded`, `RiveLoading`, `RiveFailed`). `RiveNative.init()` is called once at app startup (`bootstrap.dart`), before Firebase initializes. Two `.riv` files live in `assets/animations/`: | Asset | Widget | Purpose | | :---: | :---: | ----- | | `thinking.riv` | ThinkingAnimation | Inline spinner while the LLM generates a response | | `loading.riv` | LoadingOverlay | Full-screen overlay for major page transitions | ![Very Good Life Simulator Flutter app — ThinkingAnimation inline spinner and LoadingOverlay full-screen Rive transition wired to GenUI bloc state](/assets/images/blog/rive-flutter-genui-integration/rive_example.avif) ### The GenUI Contract: Driving Rive with JSON The bridge between [AI-generated UI](https://verygood.ventures/resources/genui/) and Rive is a single JSON boolean declared in the `AppButton` catalog item's schema: ```dart 'showLoadingOverlay': S.boolean( description: 'Whether to show the full-screen loading animation when this ' 'button is pressed. Use for major transitions like navigating to the summary dashboard.', ), ``` The prompt instructs the model to set `showLoadingOverlay: true` on the button that triggers the summary/dashboard transition. When the user taps that button, the key insight is simplicity: the contract is a single boolean. When the LLM designates a button as a major transition, the event flow is straightforward. The button press fires `SimulatorLoadingOverlayRequested` into the bloc, which flips `showLoadingOverlay` to `true`. `SimulatorView` layers a `Positioned.fill(child: LoadingOverlay())` on top. When the LLM response arrives and navigation completes, the bloc resets the state to `false`, and independently, the `.riv` event fires `GeneralEvent('complete')`, aligning the animation lifecycle with the navigation. This dual-sync (bloc state and animation event) keeps the system loosely coupled: the Rive file can change its trigger name or timing without touching Flutter code. For buttons without `showLoadingOverlay`, an inline `ThinkingAnimation` replaces the button while `isLoading` is true. The button is kept in the tree but hidden to prevent layout shifts. ## Going Deeper: Extending the GenUI-Rive Integration Currently, the GenUI-Rive integration supports binary overlay logic. Below are four patterns to extend this approach. These are optional extensions that show the full reach of the framework. ### State Machine Inputs from JSON The most direct extension is exposing Rive state machine inputs as schema properties. The new Rive API exposes state machine inputs via `controller.stateMachine.inputs`. You can set boolean, number, or trigger inputs from GenUI properties: ```dart // After RiveLoaded: final inputs = state.controller.stateMachine.inputs; final progress = inputs.whereType().firstWhere((i) => i.name == 'progress'); progress.value = json['progress'] as double; final isSuccess = inputs.whereType().firstWhere((i) => i.name == 'isSuccess'); isSuccess.value = json['animationVariant'] == 'success'; ``` In the [catalog item schema](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/), this becomes: ```dart 'animationVariant': S.string( enumValues: ['loading', 'success', 'error'], description: 'Visual state to drive the animation into.', ), ``` This lets the LLM pick the semantic state; the Rive file handles all the motion design. ### Progress-Driven Animations For a goal completion meter, expose a `number` input (0–100) and bind it to a GenUI `MetricCard` or summary value. The Rive designer builds the motion; GenUI just pipes the number: ```dart progressInput.value = (json['completionPercent'] as num).toDouble(); ``` ### One-Shot Triggers Rive events can drive multi-step flows by firing named events back to Flutter. Instead of generic `'complete'` events, the `.riv` file can fire named `GeneralEvent`s like `'onCtaReady'` or `'onErrorShow'`. The widget listens and maps each to a `UserActionEvent` dispatched back into GenUI: ```dart void _onRiveEvent(Event event) { if (event case GeneralEvent(name: final name)) { ctx.dispatchEvent(UserActionEvent(name: 'riveEvent:$name', ...)); } } ``` This turns the animation into an active participant in the conversation. For example, the LLM can wait for `'riveEvent:introComplete'` before showing the first question. ### Scroll-Driven Scrubbing For motion-following interactions, set the state machine's `time` or a number input to `ScrollController.offset`. This works well for hero sections or onboarding flows where motion should follow the user's gesture instead of playing automatically. ## The Separation of Concerns Each of these patterns follows the same underlying principle: let Rive own the motion, let GenUI own the data. The [catalog item schema](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/) acts as the contract, the LLM makes semantic decisions (which state?, what progress value?, when to fire a trigger?), while the `.riv` file handles the visual design. This separation of concerns means designers can iterate on motion without touching Flutter code, and the AI can drive animations without needing to understand their internal implementation. The payoff is responsive, data-driven animation without rebuilding the entire rendering pipeline. Because Rive owns the motion and GenUI owns the data, designers and AI systems can evolve independently. You get interfaces that don't just display information—they respond to it. ### Where Does Your Design System End and the LLM Begin? Source: https://verygood.ventures/blog/where-does-your-design-system-end-and-the-llm-begin/ When I first heard about [GenUI](https://github.com/flutter/genui), a package that lets an LLM generate Flutter UIs that change in real time based on the conversation, my very first thought wasn't just "this is cool". It was a question: _What do I actually control, and what does the LLM decide?_ The colors, the typography, the spacing — who actually owns that? The answer lives in the distinction between two things: the Design System and the Catalog. Let me walk you through how it works. ## The confusion When you hear "the LLM generates UI", it's natural to picture an AI spitting out raw widgets, arbitrary colors, random layouts, fonts it made up on the spot. The idea of handing design decisions to a language model feels terrifying if you care about consistency. If you've put real work into a design system, that's a fair concern. And now what, an LLM just freestyle invents buttons? Picks whatever font feels right? Decides on its own that today is a good day for Comic Sans and hot pink? Or whether this should be a row, a column… or somehow both? ![This is fine](/assets/images/blog/where-does-your-design-system-end-and-the-llm-begin/thisisfine.avif) So the question became: how does GenUI respect an existing design system? ## The Design System: Your Territory The Design System is entirely yours. GenUI works on top of it, completely unaware of what's inside — at least not directly. Take the GCN26 demo app as an example, a Flutter finance dashboard built for Google Cloud Next 2026. Its design system lives under `lib/design_system/` — color tokens, typography, spacing, and a set of purpose-built financial widgets — all completely separate from GenUI: ```diff lib/design_system/ ├── app_colors.dart # color tokens ├── app_text_styles.dart # Poppins-based typography ├── spacing.dart # consistent spacing constants ├── themes.dart # light/dark theme configs └── widgets/ ├── app_button.dart ├── metric_cards.dart ├── bar_chart.dart └── ... ``` These widgets are completely independent from GenUI. They're just Flutter widgets. You own 100% of what they look like, how they behave, what props they accept. This layer stays entirely in your hands. **You control the components. That's the answer to half of the question.** ## The Catalog: The LLM's Menu The Catalog is where the handoff happens. A `CatalogItem` is a wrapper you write around a design system widget. It does two things: 1. Defines a JSON schema, so the LLM knows what props exist, their types, and constraints 2. Provides a `widgetBuilder` — a function that takes the LLM's JSON and constructs the actual Flutter widget The schema is what controls how much freedom the LLM has — no more, no less. Here's a simplified example for an `AppButton`: ```dart final appButtonItem = CatalogItem( name: 'AppButton', dataSchema: S.object( properties: { 'label': S.string(description: 'The button text.'), 'variant': S.string( description: 'Visual style of the button.', enumValues: ['filled', 'outlined'], ), 'action': S.string(description: 'Event dispatched on tap.'), }, ), widgetBuilder: (ctx) { final json = ctx.data as Map; return AppButton( label: json['label']! as String, variant: json['variant'] == 'outlined' ? AppButtonVariant.outlined : AppButtonVariant.filled, onPressed: () => ctx.dispatchEvent( UserActionEvent(name: json['action']! as String), ), ); }, ); ``` The LLM sees the schema. It knows there's a component called `AppButton`, that takes a `label`, a `variant` of either `filled` or `outlined`, and an `action`. That's it — nothing more. Here's a second example — `MetricCard`, a financial summary widget. Notice the `deltaDirection` field: the LLM can only say `"positive"` or `"negative"`. Your widget decides what color that maps to. The LLM never touches the color directly. ```dart final metricCardItem = CatalogItem( name: 'MetricCard', dataSchema: S.object( properties: { 'cards': S.list( items: S.object( properties: { 'label': S.string(description: 'Metric name (e.g. "Fixed costs")'), 'value': S.string(description: r'Primary value (e.g. "$4,319")'), 'delta': S.string(description: r'Change indicator (e.g. "+1.2%")'), 'deltaDirection': S.string( description: 'Whether the delta is good or bad for the user', enumValues: ['positive', 'negative'], ), }, ), ), }, ), widgetBuilder: (ctx) { final cards = (ctx.data['cards'] as List).cast>(); return MetricCardsLayout( cards: cards.map((c) => MetricCard( label: c['label'] as String, value: c['value'] as String, delta: c['delta'] as String?, deltaDirection: c['deltaDirection'] == 'positive' ? MetricDeltaDirection.positive : MetricDeltaDirection.negative, )).toList(), ); }, ); ``` And this is the key point: the schema defines exactly how much creative freedom the LLM gets. A `variant` enum with two options means the LLM picks one of two. But you could go further — expose a pre-configured color palette, a set of font sizes, even a font family enum. You're giving the LLM creative room, but within a space you designed. No Comic Sans, no hot pink, no surprises. The LLM works within whatever boundaries you set. **The LLM fills in the data, not the design. That's the other half of the answer.** ## What the LLM Actually Generates Once the Catalog is assembled, GenUI uses it to build a system prompt that describes every available component and its schema. When the LLM responds, it outputs structured JSON like this: ```json { "version": "v0.9", "createSurface": { "surfaceId": "onboarding_screen", "components": [ { "id": "root", "component": "Container", "child": "content" }, { "id": "content", "component": "Column", "children": ["header", "body", "cta"] }, { "id": "header", "component": "SectionHeader", "title": "Get started" }, { "id": "body", "component": "BodyText", "text": "Set up your profile in a few steps." }, { "id": "cta", "component": "AppButton", "label": "Continue", "variant": "filled", "action": "onboarding_start" } ] } } ``` GenUI's `SurfaceController` receives this, looks up each component name in the catalog, and calls the corresponding `widgetBuilder`. The result is a fully rendered, on-brand Flutter screen — assembled by an LLM, built from your design system. **The LLM decided the composition. You decided the components.** ## Where the Lines Actually Blur Here's the part that surprised me: the Catalog is also where you inject intent. Each `CatalogItem` can include a natural language description of the component. And on top of that, you can define a separate set of widget rules — a few paragraphs in the system prompt that tell the LLM when and how to use each component correctly: ```diff AppButton: Use "filled" for primary actions (one per screen max). Use "outlined" for secondary or destructive actions. Never place two "filled" buttons on the same surface. FilterBar: Always pair with an "action" that refreshes visible content when the selected filter changes. ``` This is where the design intent lives. Not just "here's the schema", but "here's the reasoning behind it." The LLM picks this up and makes better compositional decisions as a result. So the Catalog isn't just a translation layer — it's also where you encode your design system's philosophy into a form the LLM can reason about. ## The Mental Model If you've ever learned about APIs using the restaurant analogy, this works the same way. The **Design System** is the kitchen. The chefs (you) decide every recipe, every ingredient, every plating rule. Nothing leaves the kitchen that wasn't designed by you. The **Catalog** is the menu. It's a curated list of what the kitchen can make, written in a language the waiter (the LLM) can read and order from. The **LLM** is the waiter. It takes the customer's order (the user's conversation), looks at the menu, and places an order. It can only order what's on the menu, working strictly within what the kitchen offers. The customer never knows there's a menu involved. They just see the food. ## Practical Takeaways If you're integrating GenUI into a project with an existing design system, here's how the responsibilities break down: | Layer | Your job | LLM's job | |---|---|---| | Design System | Build and own the widgets | Works on top of it, never inside it | | Catalog Item | Define the schema + widgetBuilder | Read the schema, pick the right component | | Widget Rules | Write usage guidelines | Follow them when composing screens | | Surface JSON | Nothing | Generate it based on the conversation | ## Final Thought The thing that surprised me most is how much control you retain. GenUI doesn't hand the wheel to the LLM — it gives the LLM a carefully scoped set of tools and says "build something with these." Your Design System is the craft. The Catalog is the contract. And the LLM is just doing its job within the boundaries you set. Once I understood that, integrating GenUI stopped feeling risky and started feeling like a superpower. ### GenUI Meets the VGV Architecture Source: https://verygood.ventures/blog/flutter-genui-meets-the-vgv-architecture/ ![GenUI with VGV architecture — side-by-side Flutter device mockups showing a plain chat interface transforming into a production-grade financial dashboard using Flutter's GenUI SDK](/assets/images/blog/genui-meets-the-vgv-architecture/main.png) Flutter's [GenUI SDK](https://docs.flutter.dev/ai/genui) is a framework for building generative UI — you define a catalog of widgets with JSON schemas, and an LLM decides which widgets to render, with what data, at runtime. The [official samples](https://github.com/flutter/genui/tree/main/examples) are intentionally simple: they teach you how GenUI works, not how to architect a Flutter app around it. That's the right call for a learning resource, but it leaves a gap when you're building for production. At Very Good Ventures, we built a [Life Goal Simulator](https://verygood.ventures/events/google-cloud-next-2026/) — a GenUI-powered financial planning assistant for [Google Cloud Next 2026](https://verygood.ventures/events/google-cloud-next-2026/) that walks users through a personalized, multi-step conversation, rendering custom financial widgets in real time. The simulator uses the same GenUI SDK as the official samples, but the architecture we applied looks quite different. This post walks through how we structured it, why we made the choices we did, and how VGV's architecture principles apply to this new category of AI-driven UI. ## The Starting Point: How the Samples Work The [official GenUI travel_app sample](https://github.com/flutter/genui/tree/main/examples/travel_app) is a great way to learn the SDK. Its architecture is intentionally flat — everything lives in a single `StatefulWidget`: ```dart // travel_planner_page.dart — the entire app in one file class _TravelPlannerPageState extends State { late final SurfaceController _surfaceController; late final A2uiTransportAdapter _adapter; late final Conversation _uiConversation; late final GoogleGenerativeAiClient _client; final _messages = ValueNotifier>([]); final _isProcessing = ValueNotifier(false); // ... setup, streaming, rendering all in one place } ``` The widget creates the AI client, manages the conversation, handles streaming, and renders the UI. State lives in `ValueNotifier`. There's no repository, no state management library, and no dependency injection. For understanding GenUI, this is ideal: read one file and you can understand the entire flow. But in production, this structure creates problems: - **Testability**: You can't unit test the conversation logic without rendering the entire widget tree. - **Swappability**: Changing the AI provider means rewriting the presentation layer. - **Complexity management**: As you add error handling, loading states, page navigation, and 26 custom widgets, one file becomes unmanageable. - **Team collaboration**: Multiple developers can't work on the AI integration and the UI simultaneously without constant merge conflicts. ## VGV's Layered Architecture VGV applies a four-layer architecture with strict unidirectional data flow — the same structure we use across every Flutter project. ![VGV four-layer Flutter architecture diagram — Presentation, Business Logic, Repository, and Data layers with unidirectional data flow arrows, the architectural pattern applied to Flutter's GenUI SDK](/assets/images/blog/genui-meets-the-vgv-architecture/four_layers.png)

Engineering Docs

The four layers, documented in full

Flutter's official architecture recommendations point to our write-up of how data, repository, business logic, and presentation stay separated on every project we join.

Read the layered architecture docs

Each layer only talks to the one directly below it. The presentation layer never touches the API client. The Bloc never imports Flutter (keeping it framework-agnostic). The repository never depends on other repositories. This isn't a GenUI-specific pattern — it's the same architecture we use for every Flutter project. The insight is that **GenUI doesn't require a new architecture**. The LLM is just another data source, and the GenUI SDK is just another API client. The same layered separation that works for REST APIs and databases works for generative AI. Here's how the Flutter GenUI architecture maps to the Life Goal Simulator: ``` lib/simulator/ ├── view/ # Presentation layer │ ├── simulator_page.dart │ ├── simulator_view.dart │ └── widgets/ ├── bloc/ # Business logic layer │ ├── display_message.dart │ ├── simulator_bloc.dart │ ├── simulator_event.dart │ └── simulator_state.dart ├── repository/ # Repository layer │ ├── simulator_repository.dart │ └── simulator_conversation_event.dart ├── catalog/ # GenUI component definitions │ ├── finance_catalog.dart │ └── items/ # 26 custom catalog items └── prompt/ # LLM prompt construction └── prompt.dart ``` To see how this works, let's trace a message through each layer, starting from the bottom. ## The Repository: Wrapping the GenUI SDK ![SimulatorRepository dependency injection diagram — ChatModel, Catalog, SurfaceController, and ErrorReportingRepository injected into the central repository, which emits a typed event stream for the Bloc layer](/assets/images/blog/genui-meets-the-vgv-architecture/repository.png) The `SimulatorRepository` is where GenUI's moving parts disappear behind a two-method API. It receives the `Catalog` and `SurfaceController` from the presentation layer (since those are rendering concerns), then wires them together with the `A2uiTransportAdapter` and `PromptBuilder` behind a clean interface: ```dart class SimulatorRepository { SimulatorRepository({ required ChatModel chatModel, required ErrorReportingRepository errorReporting, required Catalog catalog, required SurfaceController surfaceController, }); Stream get events => _controller.stream; Future startConversation() async { /* ... */ } Future sendMessage(String text) async { /* ... */ } Future dispose() async { /* ... */ } } ``` That's the entire public API. Every dependency is injected: - **`ChatModel`** is the abstract interface from [Dartantic](https://pub.dev/packages/dartantic_ai). The `SimulatorRepository` doesn't know whether it's talking to Firebase AI, Google AI, Anthropic, or any other provider — it just calls `sendStream()`. The `SimulatorPage` decides which concrete implementation to use (currently `FirebaseAIChatModel`), making provider swaps a one-line change. - **`Catalog`** and **`SurfaceController`** are rendering concerns created by the `SimulatorPage` — the repository uses the catalog for prompt building and the controller for conversation wiring, but doesn't own them. `startConversation()` composes the system prompt, creates the transport adapter, and starts the conversation. `sendMessage()` sends a user message. The repository exposes a `Stream` that the Bloc subscribes to. This split is deliberate. The catalog defines *what widgets can render* — a presentation concern. The `SurfaceController` *renders surfaces* — also presentation. The `ChatModel` is the AI backend — a data concern owned by the composition root. The repository wires them together but creates none of them, staying focused on conversation management. ### Domain Event Mapping The key design decision is the **domain event mapping**. GenUI's internal `ConversationEvent` types are implementation details — part of the [A2UI protocol](https://docs.flutter.dev/ai/genui) (Google's Agent-to-UI standard) that the SDK uses under the hood. The repository translates them into domain-specific [sealed classes](https://dart.dev/language/class-modifiers#sealed): ```dart sealed class SimulatorConversationEvent { const SimulatorConversationEvent(); } final class SimulatorConversationWaiting extends SimulatorConversationEvent { const SimulatorConversationWaiting({required this.isWaiting}); final bool isWaiting; } final class SimulatorConversationTextReceived extends SimulatorConversationEvent { const SimulatorConversationTextReceived(this.text); final String text; } final class SimulatorConversationSurfaceAdded extends SimulatorConversationEvent { const SimulatorConversationSurfaceAdded(this.surfaceId); final String surfaceId; } final class SimulatorConversationError extends SimulatorConversationEvent { const SimulatorConversationError(this.message); final String message; } ``` This mapping matters because it creates a **seam**. If the GenUI SDK changes its event types (and it will — it's [in alpha](https://docs.flutter.dev/ai/genui)), only the repository needs updating. The Bloc and presentation layer are completely insulated. Inside the repository, the streaming pipeline handles the LLM-to-GenUI bridge: ```dart Future _handleSend(ChatMessage message) async { _history.add(_convertDataPartsToText(message)); final messages = [ ChatMessage.system(_systemPrompt), ..._history, ]; final adapter = _conversation!.transport as A2uiTransportAdapter; final buffer = StringBuffer(); try { await for (final result in _chatModel.sendStream(messages)) { final text = result.output.text; if (text.isNotEmpty) { buffer.write(text); adapter.addChunk(text); } } _history.add(ChatMessage.model(buffer.toString())); } on Object catch (e, st) { if (buffer.isNotEmpty) { // Save whatever was streamed so retry has full context. _history.add(ChatMessage.model(buffer.toString())); } else { // Zero chunks arrived — pop the dangling user message so history // doesn't end with two consecutive user turns on the next send, // which Firebase AI rejects as INVALID_ARGUMENT. _history.removeLast(); } await _errorReporting.recordError(e, st, reason: 'AI sendStream error'); _controller.add(SimulatorConversationError('AI error: $e')); } } ``` Each chunk from the LLM is fed to the `A2uiTransportAdapter`, which parses the JSON and tells the `SurfaceController` to update the UI. The repository manages the message history, system prompt, and the streaming lifecycle — none of which the Bloc needs to know about. You'll notice the repository manages `_history` (the list of `ChatMessage`s) directly rather than delegating to a separate data source. In a strict layered architecture, message history could be its own data layer concern — a local database or in-memory store that the repository composes. We kept it inline because the history is tightly coupled to the streaming lifecycle: each `_handleSend` call appends the outgoing message, streams the response, and appends the completed response in a single transaction. Extracting it would add a layer of indirection without a clear benefit today. If we later needed persistence across sessions, or multiple features sharing the same conversation history, that's when the extraction would earn its keep. ## The Bloc: Managing Conversation State The `SimulatorBloc` consumes the repository's event stream and manages the UI state. It follows [VGV's standard Bloc conventions](https://engineering.verygood.ventures/development/state_management/bloc_state_handling/) — if you've worked with [`flutter_bloc`](https://pub.dev/packages/flutter_bloc), these patterns will look familiar: sealed events with past-tense naming, a single state class with a status enum and `copyWith`. ```dart // Events — past tense, describing what happened sealed class SimulatorEvent { const SimulatorEvent(); } final class SimulatorStarted extends SimulatorEvent { /* ... */ } final class SimulatorMessageSent extends SimulatorEvent { /* ... */ } final class SimulatorSurfaceReceived extends SimulatorEvent { /* ... */ } final class SimulatorContentReceived extends SimulatorEvent { /* ... */ } final class SimulatorLoadingChanged extends SimulatorEvent { /* ... */ } final class SimulatorErrorOccurred extends SimulatorEvent { /* ... */ } final class SimulatorRetried extends SimulatorEvent { /* ... */ } // ... plus loading overlay events ``` ```dart // State — Equatable + status enum + copyWith final class SimulatorState extends Equatable { const SimulatorState({ this.status = SimulatorStatus.initial, this.pages = const [], this.currentPageIndex = 0, this.isLoading = false, this.pendingPageIndex, this.showLoadingOverlay = false, this.error, }); final SimulatorStatus status; final List> pages; final int currentPageIndex; final bool isLoading; final int? pendingPageIndex; final bool showLoadingOverlay; final String? error; @override List get props => [ status, pages, currentPageIndex, isLoading, pendingPageIndex, showLoadingOverlay, error, ]; } ``` The state models the UI as a list of **pages**, where each page contains a list of **display messages**. Display messages are themselves a sealed hierarchy: ```dart sealed class DisplayMessage extends Equatable { const DisplayMessage(); } final class UserDisplayMessage extends DisplayMessage { /* ... */ } final class AiTextDisplayMessage extends DisplayMessage { /* ... */ } final class AiSurfaceDisplayMessage extends DisplayMessage { /* ... */ } ``` This is a GenUI-specific modeling choice. In a traditional chat app, you'd have a flat list of messages. Here, because GenUI surfaces are full-screen interactive UIs (not chat bubbles), each surface gets its own page. When the AI creates a new surface, the Bloc adds a new page and animates to it: ```dart void _onSurfaceReceived( SimulatorSurfaceReceived event, Emitter emit, ) { final existingPageIndex = state.pages.indexWhere( (page) => page.any( (m) => m is AiSurfaceDisplayMessage && m.surfaceId == event.surfaceId, ), ); if (existingPageIndex != -1) { // Surface already exists — navigate to it emit(state.copyWith(currentPageIndex: existingPageIndex)); } else { // New surface — create a new page final message = AiSurfaceDisplayMessage(event.surfaceId); final pages = [...state.pages, [message]]; emit(state.copyWith(pages: pages, currentPageIndex: pages.length - 1)); } } ``` The Bloc also handles text streaming — as the LLM responds, text chunks arrive one at a time and get concatenated into the current page's last text message. This is a detail the presentation layer doesn't need to think about. For more on how Bloc works with stream subscriptions like this, see [How to Use Bloc with Streams and Concurrency](https://verygood.ventures/blog/how-to-use-bloc-with-streams-and-concurrency/). ## The Presentation Layer: Page Creates, View Renders The presentation follows VGV's standard Page/View split: **`SimulatorPage`** is the composition root. It creates the `Catalog` and `SurfaceController` (rendering concerns), injects them into the repository, and passes the `SurfaceController` directly to the view — keeping GenUI types out of the Bloc entirely: ```dart class SimulatorPage extends StatefulWidget { @override State createState() => _SimulatorPageState(); } class _SimulatorPageState extends State { late final Catalog _catalog; late final SurfaceController _surfaceController; @override void initState() { super.initState(); _catalog = buildFinanceCatalog(); _surfaceController = SurfaceController(catalogs: [_catalog]); } @override Widget build(BuildContext context) { return BlocProvider( create: (_) => SimulatorBloc( simulatorRepository: SimulatorRepository( chatModel: FirebaseAIChatModel(...), errorReporting: DevErrorReportingRepository() catalog: _catalog, surfaceController: _surfaceController, ), )..add(SimulatorStarted(...)), child: SimulatorView( profileType: widget.profileType, surfaceHost: _surfaceController, ), ); } } ``` The `SurfaceController` flows from the Page to the view *and* to the repository — but never through the Bloc. The Bloc state contains only plain Dart types: strings, enums, lists of `DisplayMessage`. No GenUI imports. **`SimulatorView`** renders the UI based on Bloc state. It uses a `BlocConsumer` — `listener` for page navigation animations, `builder` for the widget tree: ```dart BlocConsumer( listenWhen: (previous, current) => previous.currentPageIndex != current.currentPageIndex, listener: (context, state) { _pageController.animateToPage( state.currentPageIndex, duration: const Duration(milliseconds: 1200), curve: Curves.easeInOutCubic, ); }, builder: (context, state) { // Render pages with GenUI Surface widgets }, ) ``` The view never touches the repository or the AI client. It reads pages from the Bloc state, renders `Surface` widgets from the GenUI SDK for each `AiSurfaceDisplayMessage`, and dispatches events when the user interacts. ## The Catalog: Domain-Specific Components as a First-Class Concern ![Life Goal Simulator financial widget catalog on iPhone — metric cards, line chart, and slider components from the 26-item Flutter GenUI catalog rendered as a production financial planning UI](/assets/images/blog/genui-meets-the-vgv-architecture/catalog.png) The official samples define their catalog inline with the page. In the Life Goal Simulator, the catalog is a separate, structured layer with **26 custom financial widgets**, each defined in its own file. (For an introduction to building catalog items, see VGV's [Flutter GenUI shopping assistant tutorial](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/).) ``` catalog/ ├── finance_catalog.dart # Composes the full catalog └── items/ ├── metric_cards_catalog_item.dart ├── line_chart_catalog_item.dart ├── pie_chart_catalog_item.dart ├── gcn_slider_catalog_item.dart ├── radio_card_catalog_item.dart ├── filter_bar_catalog_item.dart └── ... (26 items total) ``` Each catalog item follows a consistent structure: a JSON schema (telling the LLM what properties it can set), and a widget builder (turning that JSON into Flutter widgets): ```dart final _schema = S.object( description: 'A grid of cards highlighting key financial metrics...', properties: { 'cards': S.list( items: S.object( properties: { 'label': A2uiSchemas.stringReference( description: 'Short label (e.g. "Fixed costs").', ), 'value': A2uiSchemas.stringReference( description: r'Primary value (e.g. "$4,319").', ), 'delta': A2uiSchemas.stringReference( description: 'Optional delta text (e.g. "+1.2%").', ), }, required: ['label', 'value'], ), ), }, ); final metricCardsItem = CatalogItem( name: 'MetricCard', dataSchema: _schema, widgetBuilder: (ctx) { final json = ctx.data as Map; final rawCards = json['cards']! as List; // ... parse JSON, build MetricCard widgets return MetricCardsLayout(cards: cards); }, ); ``` The `buildFinanceCatalog()` function composes the full catalog by starting with the SDK's basic items, removing the generic ones we don't need, and adding our domain-specific widgets: ```dart Catalog buildFinanceCatalog() { return BasicCatalogItems.asCatalog() .copyWithout( itemsToRemove: [ BasicCatalogItems.button, BasicCatalogItems.checkBox, BasicCatalogItems.slider, // ... remove 15 generic widgets ], ) .copyWith( catalogId: financeCatalogId, newItems: [ metricCardsItem, lineChartItem, pieChartItem, gcnSliderItem, // ... 26 domain-specific widgets ], systemPromptFragments: [_financeWidgetRules], ); } ``` This separation pays off in three ways: 1. **Each catalog item is independently testable.** We have unit tests for every single one — verifying schema parsing, widget rendering, event dispatching, and data model bindings. These granular tests also serve as reliable feedback signals for CI pipelines and AI-assisted development workflows. 2. **The catalog is reusable.** If we build another financial planning feature, we can import the same catalog without duplicating widget definitions. 3. **Prompt engineering is co-located with the schema.** The `_financeWidgetRules` string tells the LLM *when* to use each widget, while the JSON schema tells it *how*. Both live in the catalog layer, not scattered across the app. Co-locating prompt fragments with their schemas reduces mismatches between what the LLM is told to render and what the catalog can actually build. ## Prompt Management: Separated from UI The system prompt is another concern that the official samples embed directly in the widget. In the Life Goal Simulator, prompt construction is its own module: ```dart class PromptBuilder { /// The system prompt that defines the AI's persona and rules. static String buildSystemPrompt() { return r''' You are a knowledgeable, empathetic life goal simulator... ## Conversation Flow You drive the conversation step by step... ## Summary Screen (REQUIRED) After gathering enough information... '''; } /// The initial user message from onboarding selections. static String buildInitialUserMessage({ required ProfileType profileType, Set focusOptions = const {}, String customOption = '', }) { // Compose the opening message based on user's profile } } ``` This separation means the team working on prompt engineering doesn't need to touch the widget code, and the prompt can be tested in isolation — verifying that different profile types and focus options produce the expected initial messages. If you later need to update prompts without redeploying, Firebase Remote Config or Firebase AI's prompt templates are straightforward drop-in replacements — the `PromptBuilder` class gives you a single integration point. ## Testability: Everything in Isolation ![Flutter GenUI test architecture — three independent test layers showing widget tests with mocked Bloc, Bloc tests with mocked repository, and catalog item tests requiring no LLM or network connection](/assets/images/blog/genui-meets-the-vgv-architecture/tests.png) Because every layer is separated, every layer is independently testable. The test directory mirrors `lib/` exactly: ``` test/simulator/ ├── bloc/ # Bloc tests with mocked repository ├── catalog/items/ # Tests for all 26 catalog items ├── prompt/ # Prompt builder tests └── view/ # Widget tests with mocked Bloc ``` **Bloc tests** mock the repository and verify state transitions using [`blocTest`](https://pub.dev/packages/bloc_test): ```dart blocTest( 'SimulatorSurfaceReceived creates a new page', build: () { when(() => repository.events).thenAnswer((_) => Stream.empty()); return SimulatorBloc(simulatorRepository: repository); }, act: (bloc) => bloc.add(const SimulatorSurfaceReceived('surface-1')), expect: () => [ SimulatorState( pages: [ [const AiSurfaceDisplayMessage('surface-1')] ], currentPageIndex: 0, ), ], ); ``` **Catalog item tests** verify that each widget renders correctly from JSON data, without needing an LLM, a network connection, or the rest of the app. **Prompt tests** verify the prompt builder produces correct output for different onboarding selections. In the official sample, none of this is possible without rendering the entire `TravelPlannerPage` and intercepting network calls. ## The Comparison | Concern | Official Sample | VGV Architecture | |---------|-----------------|------------------| | AI client setup | Widget's `initState()` | Repository constructor | | Conversation state | `ValueNotifier` | `SimulatorBloc` with sealed events/states | | GenUI plumbing | Inline in widget | `SimulatorRepository` | | Rendering infrastructure | Created in widget | Page creates `Catalog` + `SurfaceController`, injects into repo and view | | Domain events | Raw `ConversationEvent` | Sealed `SimulatorConversationEvent` | | Display models | Mixed `ChatMessage` list | Sealed `DisplayMessage` hierarchy | | Catalog | Inline in page | Separate `catalog/` layer with per-item files | | System prompt | String literal in widget | Dedicated `PromptBuilder` class | | Presentation | One `StatefulWidget` | `SimulatorPage` (composition root) + `SimulatorView` (rendering) | | Bloc purity | N/A | No GenUI imports — only plain Dart types in state | | Unit testability | Widget tests only | Every layer independently testable | | Custom widgets | Few, inline | 26 items, each in its own file with tests | Want to see this architecture in action? Try the [Life Goal Simulator live demo](https://verygood.ventures/events/google-cloud-next-2026/). ## Key Takeaways **GenUI doesn't need a special architecture.** The LLM is a data source. The GenUI SDK is an API client. The repository pattern wraps it. Bloc manages state. Widgets render. The same principles that make traditional Flutter apps maintainable work here too. **The repository is the critical abstraction.** GenUI introduces several interacting components — `Conversation`, `A2uiTransportAdapter`, `PromptBuilder`. The repository hides the conversation plumbing behind `startConversation()` and `sendMessage()`. Every dependency is injected: the `ChatModel` (so you can swap LLM providers), the `Catalog` and `SurfaceController` (rendering concerns owned by the presentation layer). When the SDK evolves (and it will), only one file changes. **Keep SDK types out of your Bloc.** It's tempting to thread `SurfaceHost` through Bloc state so the view can access it. But the Bloc shouldn't know about GenUI's rendering types. Instead, the Page — which is already the composition root — creates the `SurfaceController` and passes it to both the repository and the view directly. **Domain events create a seam.** Translating GenUI's internal events into your own sealed classes protects the rest of your app from SDK changes and makes the event flow explicit and exhaustive. **Catalog items deserve their own layer.** In the Life Goal Simulator, each of the 26 financial widgets has a schema, a builder, and behavioral rules. Giving each its own file makes them independently testable and reusable across features. **Prompt engineering is code.** The Life Goal Simulator's `PromptBuilder` class treats system prompts and initial messages with the same rigor as any other module — separate files, testable functions, version-controlled alongside the catalog they reference. ## Conclusion The official samples are the right place to learn how GenUI works. This post covered how to architect around it. The two together give you everything you need to take Flutter's GenUI SDK from prototype to production. --- *The Life Goal Simulator was built by [Very Good Ventures](https://verygood.ventures). Check out the [live demo](https://verygood.ventures/events/google-cloud-next-2026/) and our [Flutter engineering docs](https://engineering.verygood.ventures/).* ### Getting Started with GenUI: Flutter Dynamic and AI-Driven UIs Source: https://verygood.ventures/blog/getting-started-with-genui/ ![A smartphone with UI components materializing from flowing light streams against a neural network background](/assets/images/blog/getting-started-with-genui/main.avif) Flutter has always made it easy to build beautiful, fast UIs. But, what if the UI itself could be generated on the fly in response to what a user actually needs? That's the idea behind GenUI, the Flutter new SDK for building generative user interfaces. This article walks you through what GenUI is, why it matters, and how to get your first GenUI-powered app running. > **Note:** GenUI is highly experimental — expect breaking changes. It's great for prototyping, so treat it accordingly. ## What is GenUI? The traditional Flutter UIs are meant to be declarative and static where you write a `Column` with a `Text` and a `Button`, and that's what users see every time, regardless of context. GenUI flips this model. Instead of hard-coding widget trees, you give an AI agent a catalog of your widgets and let it decide which ones to render, and how to compose them, based on the user's intent. ### The difference: | Traditional approach | GenUI approach | |---|---| | LLM responds: "Here are three hotels in Tokyo…" | LLM renders an interactive hotel carousel widget | | LLM responds: "Please answer the following questions…" | LLM renders a form with sliders, date pickers, and checkboxes | | Screen layout defined at compile time | Screen layout assembled at runtime from your widget catalog | Rather than replacing walls of text with better text, GenUI replaces them with real, interactive Flutter widgets. ## The Three Core Concepts Before writing any code, it helps to understand how GenUI thinks about your app. **Catalog:** A vocabulary of widgets you expose to the AI. Each entry includes a widget name, a JSON schema describing its properties, and a builder function that renders it. The AI can only use widgets in the catalog — it cannot create new ones. **Surface:** A region of your UI where AI-generated content appears. You place a `GenUiSurface` widget in your layout, and GenUI fills it with whatever the agent produces. **Conversation:** The stateful interaction loop. `GenUiConversation` manages the full history of messages between your user and the agent, so each new request has context from everything that came before. ### The flow looks like this: 1. User sends a prompt 2. Your app forwards it to the AI agent, along with the widget catalog as a set of tools 3. The agent answers not with text, but with structured JSON describing which widgets to render 4. GenUI deserializes that JSON and builds the widget tree on `Surface` 5. The user interacts with those widgets; state changes flow back to the agent as context for the next turn Even though Flutter still does all the rendering. ## Prerequisites You will need: - Flutter SDK installed (preferably the one from the stable channel) - Basic Flutter knowledge - A free Gemini API key from the Google AI Studio or a `FirebaseAIChatModel` provided by the `dartantic_firebase_ai` package ## Step 1: Add Dependencies The fastest path to a working GenUI app uses `dartantic_ai` directly — no server required, just your API key. Add the following dependencies to your `pubspec.yaml` file: ```yaml dependencies: dartantic_ai: ^3.x.x genui: ^0.8.0 genui_dartantic: ^0.x.x json_schema_builder: ^0.1.3 ``` ## Step 2: Define your Widget Catalog The catalog is where you tell the AI what it can build. Each `CatalogItem` has three parts: 1. A **name**: what the AI uses to refer to this widget 2. A **JSON schema**: the shape of the data the AI must provide 3. A **builder**: the Flutter widget that renders from that data As an example, let's introduce an `InfoCard` widget the AI can use to display a title and a description. ```dart import 'package:flutter/material.dart'; import 'package:genui/genui.dart'; final infoCatalog = Catalog( components: [ CatalogItem( name: 'InfoCard', dataSchema: S.object( properties: { 'title': A2uiSchemas.stringReference( description: 'Header text displayed in the card', ), 'description': A2uiSchemas.stringReference( description: 'Informative text displayed in the card', ), }, required: ['title', 'description'], ), widgetBuilder: (ctx) { final json = ctx.data as Map; final titleValue = json['title']; final descriptionValue = json['description']; return BoundString( dataContext: ctx.dataContext, value: titleValue, builder: (data, _) { return BoundString( dataContext: ctx.dataContext, value: descriptionValue, builder: (context, _) => Card( margin: const EdgeInsets.all(8), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( titleValue as String? ?? '', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text(descriptionValue as String? ?? ''), ], ), ), ), ); }, ); } ), ], ); ``` A few things to take into consideration: - The JSON schema is how the AI knows what properties to supply. It acts as the contract between your agent and your UI. - The `builder` function receives the agent's data and returns a standard Flutter widget. Any widget you can write in Flutter, you can expose through a `CatalogItem`. - The AI cannot use a widget unless it's in the catalog. This is a feature, not a limitation — it keeps generated UIs within your design system. ## Step 3: Set Up the Conversation The next step is to wire up the `GenUIConversation` with your catalog and the model. `genui_dartantic` provides `DartanticContentGenerator`, a ready-made `ContentGenerator` implementation backed by the `dartantic_ai` agentic framework. It supports multiple AI providers (Google, OpenAI, Anthropic, Mistral, and more) through a unified API, so you can swap backends without touching the rest of your code. ```dart import 'package:genui/genui.dart'; import 'package:google_generative_ai/google_generative_ai.dart'; const _apiKey = 'YOUR_GEMINI_API_KEY'; // Only for local development late final GenUiConversation conversation; void initConversation() { conversation = GenUiConversation( contentGenerator: DartanticContentGenerator( model: 'gemini-3-flash-preview', provider: Providers.google(apiKey: _apiKey), catalog: infoCatalog, systemInstruction: ''' You are a helpful assistant. When responding, always use the InfoCard widget from your catalog to display information visually. Never respond with plain text alone. ''', ), ); } ``` The `systemInstruction` plays a key role in the setup. The AI needs explicit direction to use catalog tools rather than defaulting to plain text responses. Be specific about when and how you want it to use your widgets. ## Step 4: Build the UI Now place a `GenUiSurface` in your widget tree. This is the region where the agent's generated content will appear. ```dart import 'package:flutter/material.dart'; import 'package:genui/genui.dart'; class GenUiDemo extends StatefulWidget { const GenUiDemo({super.key}); @override State createState() => _GenUiDemoState(); } class _GenUiDemoState extends State { final _controller = TextEditingController(); bool _loading = false; @override void initState() { super.initState(); initConversation(); } Future _send() async { final prompt = _controller.text.trim(); if (prompt.isEmpty) return; setState(() => _loading = true); _controller.clear(); await conversation.sendMessage(prompt); setState(() => _loading = false); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('GenUI Demo')), body: Column( children: [ // The surface where AI-generated widgets appear Expanded( child: GenUiSurface( conversation: conversation, surfaceId: 'main', ), ), if (_loading) const LinearProgressIndicator(), Padding( padding: const EdgeInsets.all(8), child: Row( children: [ Expanded( child: TextField( controller: _controller, decoration: const InputDecoration( hintText: 'Ask something...', border: OutlineInputBorder(), ), onSubmitted: (_) => _send(), ), ), const SizedBox(width: 8), IconButton.filled( onPressed: _loading ? null : _send, icon: const Icon(Icons.send), ), ], ), ), ], ), ); } } ``` Run the app, type a prompt like "Tell me three fun facts about the ocean", and the AI will respond by composing `InfoCard` widgets rather than a block of text. ## Step 5: Add an Interactive Widget Static display widgets are a good start, but GenUI becomes compelling when widgets feed data back to the agent. Let's take a look at a `RatingWidget` that the user can interact with: ```dart CatalogItem( name: 'RatingWidget', dataSchema: S.object( properties: { 'label': A2uiSchemas.stringReference( description: 'The title text', ), 'value': S.number( description: 'Current rating value, must be between 1 and 5', ), }, required: ['label', 'value'], ), widgetBuilder: (ctx) { final json = ctx.data as Map; final label = json['label'] as String; final value = (json['value'] as num).toDouble(); return BoundString( dataContext: ctx.dataContext, value: label, builder: (context, title) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title ?? label), Slider( value: value, min: 1, max: 5, divisions: 4, label: value.toStringAsFixed(0), onChanged: (newValue) { // Update the DataModel so the agent sees the new state ctx.dataContext.update(json['_id'] as String, {'value': newValue}); // Instead of manual rebuild, the Slider could be wrapped // into BoundNumber, another GenUI reactive binding widget setState(() {}); }, ), ], ); }, ); }, ), ``` `BoundString` and `BoundNumber` are GenUI's reactive binding widgets. Rather than managing local state with `StatefulBuilder` and calling `setState`, you wrap the relevant part of your tree in a bound widget and it rebuilds automatically whenever the underlying value in the `DataContext` changes. `BoundString` resolves a label (which may be a literal or a data-model path reference), while `BoundNumber` does the same for numeric values. When the user moves the slider, `ctx.dataContext.update` writes the new value back to GenUI's central state store. In the next message, that updated state flows back to the agent as context — enabling true bidirectional interaction between the user and the LLM. ## What to Build Next Once you're comfortable with the basics, a few directions worth exploring: **More widget types:** The catalog can hold anything Flutter can render: carousels, date pickers, charts, maps. The richer your catalog, the more expressive the AI's responses. **Better system instructions:** How you prompt the agent determines the quality of its output. Spend time iterating on your `systemInstruction` — it's as important as the catalog itself. **Switching providers:** One of `dartantic_ai`'s strengths is provider portability. Swapping from Google to OpenAI or Anthropic requires changing only the `Providers.*` argument and the model name; the rest of your code stays the same. **Firebase AI for production:** Embedding an API key directly in the app is for local experimentation only. For a shipped app, switch to `dartantic_firebase_ai`, which uses Firebase AI Logic for secure, server-side model calls, keeping your credentials off the client. **The A2UI protocol:** For server-side agent architectures, `genui_a2a` connects your Flutter app to any backend that implements the A2UI protocol over WebSockets. Note that this area is still actively evolving — the team is splitting the library into a pure-Dart `genui_core` package (responsible for A2UI message parsing, JSON Pointer-based state management, and expression evaluation) and a Flutter-only `genui` renderer on top of it. The goal is to make the core logic fully portable to non-Flutter environments. As of writing, the milestone is roughly 40% complete, so keep an eye on that issue for breaking changes before committing to the current API surface. **Logging:** Enable GenUI's built-in logging during development to see exactly what flows between your app and the agent: ```dart import 'package:logging/logging.dart'; import 'package:genui/genui.dart'; final logger = configureGenUiLogging(level: Level.ALL); void main() { logger.onRecord.listen((record) { debugPrint('${record.loggerName}: ${record.message}'); }); runApp(const MyApp()); } ``` ## Wrapping Up GenUI changes the question from "what should this screen look like?" to "what widgets should I give the AI to work with?". Your job shifts from assembling widget trees by hand to designing and curating the system that produces them. It's early days — the package is in alpha and the API will change. But the underlying idea is solid, and the Flutter team is investing in it seriously. Now is a good time to experiment, build prototypes, and share feedback. The full GenUI documentation lives at [docs.flutter.dev/ai/genui](https://docs.flutter.dev/ai/genui), and the official examples are worth studying once you've got the basics down. ## Resources - [GenUI SDK on pub.dev](https://pub.dev) - [Official getting started guide](https://docs.flutter.dev/ai/genui) - [GenUI GitHub repository](https://github.com/flutter/genui) - [dartantic_ai on pub.dev](https://pub.dev) - [genui_dartantic on pub.dev](https://pub.dev) ### Getting Started with Wingspan: AI-Assisted Flutter Development Source: https://verygood.ventures/blog/wingspan-tutorial-demo-fishtracker/ Wingspan is an AI-assisted development framework for Claude Code that orchestrates your full development lifecycle — brainstorming, planning, building, and quality review — while enforcing Very Good Ventures' [engineering standards](/solution/very-good-start/) throughout. Built on lessons from shipping production Flutter apps, it catches edge cases brainstorming misses, flags architectural violations before they ship, and keeps your codebase to consistent patterns even as you move fast. This tutorial teaches each Wingspan skill using a real app — a Fish Tank Tracker — as an ongoing example. We built this app from a blank slate to a fully functional Flutter application with AI-powered species identification, image management, and water parameter tracking. Along the way, Wingspan's review agents caught real bugs, its planning agents found over twenty edge cases we'd missed, and its build phase produced clean, well-tested code. ![The Fish Tank Tracker app built with Wingspan — gallery view with livestock and plant filters, entry detail page with AI species identification, and water parameter tracking](/assets/images/blog/wingspan-tutorial-demo-fishtracker/fish-tank-tracker-screens.png) You don't need to build the same app. The skills work on any project. Follow along with your own idea, or use the examples here to learn the workflow. --- ## Prerequisites Before installing Wingspan, you need Claude Code running. If you already have it, skip to [Installing Wingspan](#installing-wingspan). ### Installing Claude Code [Claude Code](https://code.claude.com/docs/en/overview) is Anthropic's command-line interface for running Claude. Install it globally: ```bash npm install -g @anthropic-ai/claude-code ``` You'll need an Anthropic API key or an active Claude subscription. Run `claude` in your terminal to authenticate. > **Already using Claude Code?** Jump to [Installing Wingspan](#installing-wingspan). ### Installing Wingspan Navigate to your Flutter project and load Wingspan. Inside Claude Code: ```bash /plugin marketplace add VeryGoodOpenSource/very_good_claude_marketplace /plugin install vgv-wingspan@very_good_claude_marketplace ``` That's it. Wingspan's skills are now available as slash commands. --- ## The Wingspan Workflow Wingspan follows a four-phase workflow: **brainstorm**, **plan**, **build**, and **review**. Each phase produces documents that feed into the next, so you can start a fresh Claude Code session between steps without losing work. ```bash /brainstorm → /plan → /build → /review ↕ ↕ /refine-approach /plan-technical-review ``` You don't have to follow it linearly—jump in at any phase based on what you already know, and loop back to refine decisions before moving forward. You don't have to use every phase. Simple bug fix? Jump to `/build`. Already know what you want? Start at `/plan`. Wingspan adapts to what you need. ### Quick Reference | Skill | Command | When to use | |-------|---------|-------------| | Brainstorm | `/brainstorm ` | Exploring a new feature or big change | | Refine Approach | `/refine-approach` | Tightening a brainstorm or plan before moving on | | Plan | `/plan ` | Turning ideas into a step-by-step implementation plan | | Plan Technical Review | `/plan-technical-review` | Validating a plan with opposing review perspectives | | Build | `/build ` | Writing code and tests from a plan | | Review | `/review [path]` | Running quality review agents to assess code quality | | Hotfix | `/hotfix ` | Emergency bug fixes with enforced review | **Wingspan is a guide, not a cage.** We used the full sequence for the initial app build, a shorter version for adding the entry detail feature, and a mix of structured and ad-hoc work for polish and refactoring. The workflow adapts to how you actually work. You don't need to memorize any of these commands. When each phase finishes, Wingspan presents multiple-choice options for what to do next — refine your brainstorm, move to planning, start building, or clear context and pick up later. You can learn the workflow just by following the prompts. --- ## Phase 1: Brainstorm ### What it does `/brainstorm` opens a collaborative dialogue to explore your idea. Instead of dumping requirements, Wingspan asks targeted questions (often multiple-choice) to help you think through purpose, users, constraints, and trade-offs. It proposes approaches and captures decisions in a persistent document. ### Try it ```bash /brainstorm add a way to photograph fish and identify species using AI ``` Wingspan will ask you questions one at a time. Here's what a typical exchange looks like: **Wingspan asks:** > Should the AI identification happen automatically after capture, or on-demand? > 1. Automatically after every photo > 2. On-demand via a button > 3. Both — auto-identify but allow re-identification You pick an option, Wingspan asks the next question. After several rounds, it proposes two to three concrete approaches with trade-offs: > **Approach A: Cubit with debounced auto-save** (Recommended) > > The edit page gets its own Cubit that handles inline saves... > > - Pros: Follows existing patterns, clean separation > - Cons: Needs a new cubit + state > - Best when: Consistency with existing architecture matters You choose an approach, and Wingspan writes a brainstorm document to `docs/brainstorm/`: ```bash docs/brainstorm/2026-03-30-gallery-entry-detail-brainstorm-doc.md ``` This document captures what you're building, why this approach, key decisions, and open questions. It becomes the input to the next phase. ### What we learned building the Fish Tank Tracker When we brainstormed the initial app concept, Wingspan asked about navigation layout and generated ASCII mockups to compare options before writing any code. Later, when brainstorming the image cropping feature, it explored three package options (`image_cropper`, `crop_image`, `croppy`) with pros and cons for each, and we picked the native crop UI approach. ### Tips - **Give context up front.** `/brainstorm add authentication` produces generic results. `/brainstorm add email/password and OAuth login for our e-commerce app that currently has guest checkout` produces focused questions. - **Push back.** If Wingspan suggests something you disagree with, say so. The brainstorm adapts to your feedback. - **Run `/refine-approach` after.** This catches contradictions and gaps in the brainstorm before you invest in planning. When we refined our initial app brainstorm, it caught logical contradictions we'd glossed over in the excitement of ideation. --- ## Phase 2: Plan ### What it does `/plan` transforms your brainstorm into a structured implementation plan. It launches research agents in parallel to understand your codebase and identify gaps, then produces an actionable document with acceptance criteria, technical design, and implementation order. ### Try it ```bash /plan implement the gallery entry detail page from our brainstorm ``` If a recent brainstorm document exists, Wingspan picks it up automatically: > Found brainstorm from 2026-03-30: gallery-entry-detail. Using as context for planning. ### What happens behind the scenes Wingspan runs multiple agents in parallel: 1. **Codebase review agent** — reads your existing code to understand patterns and conventions 2. **User-flow analysis agent** — analyzes the feature for completeness and edge cases 3. **External research agents** (when needed) — fetches docs for frameworks and libraries The user-flow analysis agent is particularly valuable. When planning the entry detail page, the flow agent found twelve gaps the brainstorm had missed: > **Gap: Debounce-on-close data loss** > > When the user edits the name and immediately presses back, the debounce timer may not have fired yet. The pending save is lost. The user sees their edit on screen, presses back thinking it saved, and returns to find the old name. > > **Gap: currentIndex out of bounds after deletion** > > If the user deletes the last entry in a list, the currentIndex may point beyond the new list length, causing a range error. These are real bugs that would have shipped without this analysis. Across two planning phases for the Fish Tank Tracker, the user-flow agent found over twenty edge cases—debounce failures, index-out-of-bounds errors, race conditions—the kind that typically slip to production. When we planned a major refactor merging the "add entry" and "edit entry" flows, the flow agent found eleven more gaps. These included orphaned image handling on cancel, app-kill recovery, and race conditions between debounced saves and the cancel action. ### The plan document The output lands in `docs/plan/`: ```bash docs/plan/2026-03-30-feat-gallery-entry-detail-page-plan.md ``` It includes: - Title, type, and acceptance criteria - Technical design with file paths and code sketches - Implementation order - Risks and mitigations - What's explicitly out of scope ### Optional: Technical Review Before building, you can validate the plan: ```bash /plan-technical-review ``` This runs two agents with deliberately opposing perspectives: - A **code-simplicity reviewer** that flags over-engineering and YAGNI violations - A **VGV-standards reviewer** that checks architectural rigor This tension produces a plan that avoids both gold-plating and shortcuts. When we ran this on the initial Fish Tank Tracker plan, the simplicity reviewer flagged six YAGNI items (features we didn't need yet) while the VGV reviewer upgraded the architecture to a multi-package monorepo. The final plan was both leaner and more production-grade than either perspective alone would have produced. ### Refining plans with your own feedback Plans aren't rigid. When our unified entry flow plan suggested auto-deleting entries with empty names on close, we pushed back: "I'm fine if the name is empty; there should always be an image." Wingspan updated the plan, and that single piece of feedback eliminated an entire category of complexity—no `PopScope` interception, no auto-delete logic, no special back-navigation handling. The implementation was significantly simpler as a result. ### Tips - **Skip external research when your codebase has strong patterns.** Wingspan decides automatically, but you can guide it: "My codebase already has good examples of this pattern." - **You can skip planning entirely.** For small bug fixes or simple changes, go straight to `/build` with a description. - **Not every plan needs a technical review.** We used `/plan-technical-review` for the initial app build but skipped it for subsequent features where the patterns were already established. Use it when the stakes are high or when you're exploring unfamiliar territory. --- ## Phase 3: Build ### What it does `/build` executes your plan—writing code, writing tests, and running static analysis. It follows the plan's implementation order and validates at each step. ### Try it ```bash /build docs/plan/2026-03-30-feat-gallery-entry-detail-page-plan.md ``` Wingspan summarizes the scope and asks for confirmation before starting: > **Plan scope:** 6 tasks, ~8 files to create/modify, moderate complexity. > > 1. Start building > 2. Review the plan first > 3. Adjust scope ### What happens during the build Wingspan breaks the plan into phased tasks and tracks progress as it works through each one: ![Build phase todo list showing phased tasks with validation checkpoints](/assets/images/blog/wingspan-tutorial-demo-fishtracker/update-todos.png) You don't need to manage these steps — Wingspan handles them automatically. For each task in the plan, it writes the code, writes tests alongside it, runs `flutter analyze` and `flutter test` to validate, and gives you a brief progress update before moving to the next task. If something fails, it fixes the issue and re-runs validation. The initial Fish Tank Tracker build produced six packages and four features across roughly fifty files, using sealed classes, Bloc/Cubit state management, and go_router navigation. It shipped with twenty-three passing tests and zero analysis issues. ### Tips - **Clear context between phases.** Wingspan offers this option after each phase. A fresh context window produces better results for the next phase. --- ## Phase 4: Review ### What it does `/review` launches four review agents in parallel to assess code quality and catch issues before they reach PR. You can run it on changed files, specific paths, or the full project. ### Try it ```bash /review ``` Or target a specific path: ```bash /review lib/entry_detail/ ``` ### The four review agents - **VGV standards agent** — conventions, doc comments, naming - **Code simplicity agent** — YAGNI audit, dead code, unnecessary abstractions - **Test quality agent** — coverage gaps, anti-patterns, missing edge cases - **Architecture agent** — layer separation, dependency direction, Bloc/Cubit correctness The quality review isn't ceremonial; it catches real issues. Here's what the agents found building the Fish Tank Tracker: **During the initial build**, the code-simplicity agent found broken wiring that left the AI identification button connected to the UI, but the Cubit event handler was never wired up—no crashes, no errors, just silent failure. Without this review, the feature would have shipped appearing to work in development but doing nothing in production. The test-quality agent flagged missing widget tests and failure-path coverage. **When building the entry detail page**, the architecture agent found a `TextEditingController` initialized inside `build()`—a common Flutter anti-pattern that causes subtle lifecycle bugs (controllers are recreated on every rebuild, losing their state). The VGV agent flagged missing error handling in the save method, which could have masked database failures silently. **During a major refactor** (unifying the add and edit entry flows), the architecture agent caught a layer violation: a widget was calling the repository directly instead of going through the cubit. This broke the pattern used everywhere else in the codebase and would have left the business logic untestable without a widget test. After the review, Wingspan categorizes findings and asks you how to proceed—you stay in control of what gets fixed: ![Fix scope dialog showing options for addressing critical issues, all issues, or just bugs](/assets/images/blog/wingspan-tutorial-demo-fishtracker/fix-scope.png) You choose the scope, and Wingspan fixes the selected issues automatically, re-running analysis and tests after each fix. ### The limits of automated review and where domain knowledge takes over The review agents missed one issue: type-only changes (switching a fish's category without editing its name) weren't saving. The save method had an empty-name guard that prevented category changes on unnamed entries. Four review agents ran, and none flagged it. Similarly, the agents also recommended deprecated packages (`google_generative_ai`, now `firebase_ai`) and a Gemini model (`gemini-2.0-flash`) that was blocked for new users despite appearing in official documentation. These required manual debugging with curl to resolve. This is where domain knowledge—yours, not the AI's—becomes the final checkpoint. Use Wingspan to catch what patterns can reveal; use your own testing to catch what only behavior reveals. You know your users' workflows. You understand why certain shortcuts matter. You can tap through your app and spot the UI bugs, race conditions under load, and edge cases that only emerge when you live with the software. Wingspan handles the scaffolding; you handle the ship-readiness. The takeaway: automated review catches architectural and pattern issues exceptionally well, but it can't replace running the app, tapping through the flows yourself, and thinking through the workflows your users actually follow. ### Tips - **The quality review is when architecture and pattern violations surface.** Skipping it means shipping design debt disguised as working code. - **Manual testing still matters.** Run the app. Tap every button. Try edge cases. The AI catches patterns; you catch behavior. --- ## Working Outside the Phases Not everything needs the full brainstorm-plan-build-review cycle. During the Fish Tank Tracker development, most quick fixes and polish work bypassed the structured phases—by design. ### Quick changes Just describe what you need in natural language: ```bash Change the livestock icon from a paw print to a fish icon ``` ```bash The camera preview should be centered instead of left-justified ``` ```bash Pin the intl dependency to ^0.20.2 ``` ```bash The API key field shouldn't be obscured like a password ``` Wingspan handles these without invoking a skill, allowing small changes to flow in parallel with structured workflows. We added scientific name editing, a retake photo feature, a custom SVG fish icon, and various UI fixes this way—all flowing in parallel with the structured Wingspan workflows. ### Mixing structured and ad-hoc work You can also use individual skills for specific decisions. When choosing an image cropping package, we ran `/brainstorm` for that single decision. It explored three options with trade-offs, and we picked one in under two minutes. No plan or build phase needed; we implemented it directly. ### Emergency fixes We never needed `/hotfix` during the Fish Tank Tracker build — nothing was in production yet. But if the app had been live and users reported a crash on the crop button, this is what we'd reach for: ```bash /hotfix users are getting a crash when they tap the crop button on Android ``` `/hotfix` applies a minimal fix with enforced review and testing, skipping the brainstorm and planning phases. It's designed for production emergencies where speed matters but you still want guardrails. --- ## Putting It All Together Here's how a typical feature development flows with Wingspan: ### The full cycle (new features, big changes) ```bash /brainstorm add image cropping to the edit page → Explores packages, UX approaches, placement decisions → Output: docs/brainstorm/2026-03-31-image-crop-brainstorm-doc.md /refine-approach → Catches gaps and contradictions /plan implement image cropping from our brainstorm → Codebase review, user-flow analysis, external docs research → Output: docs/plan/2026-03-31-feat-image-crop-plan.md /plan-technical-review → Simplicity vs rigor review /build docs/plan/2026-03-31-feat-image-crop-plan.md → Code + tests + analysis /review → Four agents assess quality in parallel → Fixes critical/important findings ``` ### The short cycle (medium changes) ```bash /brainstorm add a way to edit entries in the gallery → Quick Q&A, approach selection /plan implement entry detail page → Picks up brainstorm, runs research agents /build docs/plan/... → Full implementation /review → Quality check before merge ``` ### No cycle (small changes, bug fixes) ```bash The empty state shows a paw print icon, change it to a fish ``` Or for production bugs: ```bash /hotfix users are seeing a render overflow on the edit page when the keyboard opens ``` --- ## What to Expect After building the Fish Tank Tracker from scratch with Wingspan, a few patterns stood out: **Parallel review agents catch real bugs.** Broken wiring, crash-causing anti-patterns, and architectural violations. Not theoretical issues—actual problems that would ship otherwise. The review phase paid for itself on the very first build. **The user-flow analysis agent stands out.** It consistently finds edge cases brainstorming misses: debounce data loss, index-out-of-bounds errors, race conditions between concurrent operations. These are the bugs that typically ship to production. **The workflow is flexible by design.** You don't always need every step. We skipped `/refine-approach` and `/plan-technical-review` for well-scoped features, used `/brainstorm` for a two-minute package selection, and handled a dozen quick fixes with plain natural language. Rigidity would have made it unusable. Flexibility makes it practical. **Human judgment still drives the product.** We pushed back on implementation choices (no auto-deleting empty entries) and caught bugs agents missed (type-only saves failing). We also spotted UI issues that required actually running the app (overlapping buttons, render overflows) and made UX decisions that shaped the architecture (relocating the AI button near the fields it fills). Wingspan handles the scaffolding and catches the details; the developer sets the direction. **How you frame the project matters.** Calling something a "demo app" gives the AI permission to cut corners. If you want production-grade architecture, set that expectation upfront. The AI responds to the same context cues a junior developer would. Tell Wingspan you're shipping production, and it'll enforce architectural rigor. The AI doesn't know your intention unless you state it explicitly. **AI tools have knowledge gaps.** Deprecated packages, blocked API models, missing platform configuration—these are areas where the developer's real-world testing complements what AI agents can verify. We're building detection for these gaps into future Wingspan versions. --- ## Next Steps - **[Wingspan on GitHub](https://github.com/VeryGoodOpenSource/wingspan)** — Installation, full skills reference, and source code - **[Very Good Ventures](https://verygood.ventures/contact-us/)** — Questions about Wingspan for your team ### Fluttering Forward: How Abdallah Shaban Is Shaping Flutter's Enterprise Story at Google Source: https://verygood.ventures/blog/fluttering-forward-abdallah-shaban-google-flutter-enterprise-story/ {% video url="https://www.youtube.com/watch?v=Uglr0Gqps8A" title="Abdallah Shaban, Google — Fluttering Forward: Innovation and Community in Tech" %} In the dynamic landscape of technology, product management plays a crucial role in bridging the gap between innovation and user needs. [In a recent episode of *Build to Succeed*](https://verygood.ventures/podcasts/abdallah-shaban-google-fluttering-forward/), Abdallah Shaban, an Outbound Product Manager at Google, shared his journey and insights into the world of product management, particularly within the realms of open-source frameworks like Flutter and Dart. His experiences provide valuable lessons for aspiring product managers and tech enthusiasts alike. ## Understanding the Role of an Outbound Product Manager Abdallah's journey into product management is both inspiring and enlightening. Initially working with the Flutter and Go teams, he has recently expanded his role to include the Genkit team, focusing on open-source products at Google. Abdallah describes the Outbound Product Manager role as unique, emphasizing three main pillars: gathering customer insights, collaborating with product and marketing teams, and actively engaging with the community. This holistic approach not only enhances product development but also ensures that user feedback is integral to the process. ## Keeping a Well-Rounded Life: Music and Dance Beyond the professional realm, Abdallah emphasizes the importance of a well-rounded life. As a musician who plays guitar and keyboards, and a dancer who has embraced Latin dance, he finds that his hobbies contribute significantly to his work. Abdallah notes that both music and dance require a blend of technical skill and improvisation, paralleling the adaptability necessary in product management. ## A Journey Through Technology and Innovation Abdallah's career journey began in Jordan, where he launched the first 4G network in the country. His desire for a broader perspective led him to California, where he pursued an MBA and became the first product manager at Petco. His subsequent roles at Amazon and then co-founding Celest, a Y Combinator-funded startup, further honed his skills, particularly in developer tooling and community engagement. Abdallah's passion for Flutter, which he has followed since its inception, drove him to advocate for Dart SDKs at Amazon and later at Google, solidifying his expertise in the field. ## The Importance of Community Engagement One of Abdallah's key insights is the value of community engagement in product management. He stresses the importance of being an advocate for both the product and the users, ensuring that the narrative remains cohesive across different platforms, highlighting the necessity of a unified message in today's fast-paced tech environment. ## Key Takeaways Abdallah Shaban's journey through product management showcases the significance of adaptability, community engagement, and a well-rounded life in achieving success. Aspiring product managers should take note of his approach, which emphasizes the importance of understanding user needs, fostering community relationships, and embracing diverse interests that enhance professional growth. You can catch the full conversation with Abdallah Shaban on [*Build to Succeed*](https://verygood.ventures/podcasts/abdallah-shaban-google-fluttering-forward/) wherever you get your podcasts. ### Why Every Flutter Team Needs a Design System: Lessons from Widgetbook's Lucas Josefiak Source: https://verygood.ventures/blog/widgetbook-design-systems-lucas-josefiak-flutter/ {% video url="https://www.youtube.com/watch?v=dZ4w87UNakE" title="Lucas Josefiak, Widgetbook — Role of Design Systems in Software Development" %} In the fast-paced world of Flutter development, effective design collaboration is key to success. [In this episode of *Build to Succeed*](https://verygood.ventures/podcasts/lucas-josefiak-widgetbook-design-systems/), we sit down with Lucas Josefiak, the founder of Widgetbook, to explore how his innovative platform is transforming the way Flutter teams work together. Join us as we dive into the competitive spirit that drives Lucas and the significant impact of developer tooling on modern design systems. ## The Birth of Widgetbook Lucas Josefiak founded Widgetbook with a vision to enhance design collaboration for Flutter teams. His journey reflects a deep understanding of the challenges faced by developers and designers alike. By leveraging his competitive nature, Lucas has been able to rapidly evolve his company, ensuring it meets the growing demands of the industry. ## Scalable Design Collaboration At the heart of Widgetbook is its capability to facilitate scalable design collaboration. This is crucial for Flutter teams looking to streamline their workflows. Lucas explains how his platform allows teams to align on design systems effectively, thus reducing friction and enhancing productivity. By providing tools that simplify communication and design sharing, Widgetbook empowers teams to focus on creating outstanding user experiences. ## The ROI of Developer Tooling In the podcast, Lucas emphasizes the return on investment (ROI) that comes from investing in developer tooling. He discusses how organizations can save time and resources by using effective tools that streamline the development process. For instance, by shifting left in the development cycle, teams can identify UI bugs earlier, which not only speeds up the development process but also improves the overall quality of the product. ## Importance of Design Systems Design systems play a pivotal role in ensuring consistency across applications. Lucas highlights how Widgetbook supports the implementation of robust design systems, making it easier for teams to maintain a cohesive look and feel in their apps. This consistency not only enhances user satisfaction but also strengthens brand identity. ## Shifting Left: Finding UI Bugs Faster One of the key strategies Lucas advocates for is "shifting left" in the development process. This approach involves addressing potential issues earlier in the workflow, particularly when it comes to UI bugs. By integrating testing and feedback loops early on, teams can significantly reduce the chances of encountering major issues later in the development cycle, leading to smoother launches and happier users. ## Key Takeaways Lucas Josefiak's insights into design collaboration and developer tooling provide valuable lessons for Flutter teams. The evolution of Widgetbook showcases the importance of investing in tools that enhance collaboration and streamline workflows. By focusing on scalable design systems and shifting left to catch UI bugs early, teams can achieve greater efficiency and deliver exceptional products. As the Flutter landscape continues to evolve, embracing these principles will be crucial for success. You can catch the full conversation with Lucas Josefiak on [*Build to Succeed*](https://verygood.ventures/podcasts/lucas-josefiak-widgetbook-design-systems/) wherever you get your podcasts. Check out [Widgetbook](https://widgetbook.io/) and the interactive demo at [demo.widgetbook.io](https://demo.widgetbook.io/) to see the tool in action. ### Genkit for Flutter: Flows, Middleware, and Tools in a GenUI Shopping Assistant Source: https://verygood.ventures/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/ Most Flutter apps that integrate AI do it the same way: call an API, get text back, show it. It works — until you need retries on flaky calls, tools that query your own data, structured output you can trust without parsing hacks, or the ability to swap providers without a rewrite. [**Genkit**](https://firebase.google.com/docs/genkit) is Google's open-source AI framework for Dart. It sits between your app and the model API, giving you the infrastructure that production AI features need: - **Flows** — named, composable, observable units of AI work - **Middleware** — pluggable request processing (retry, logging, caching) - **Tools** — let the model call your business logic with validated schemas - **Structured output** — guaranteed typed JSON responses, no regex - **Provider-agnostic** — swap between Gemini, Claude, Ollama, or any supported provider by changing a plugin It runs anywhere Dart runs: in a Flutter app, on a backend server, or both. ## What We're Building In [Part 1](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/) we built a conversational shopping assistant using Flutter, [GenUI](https://pub.dev/packages/genui), and Firebase AI. The app renders interactive product cards, carousels, and filters — all driven by natural language. If you haven't read it, that post covers the GenUI architecture, the catalog system, and how AI responses become rendered widgets. Here, we take that same app and swap Firebase AI for Genkit. The UI layer stays untouched — we're only changing how the app talks to the model. But instead of stopping at a basic migration, we'll use Genkit's features to add things that would have been painful to build by hand. We'll run Genkit client-side to keep things simple — in Part 3 we'll move it to the backend where it really shines. The full source code is available on the [`genkit-refactor` branch of the repository](https://github.com/VGVentures/genui_shopping_assistant/tree/genkit-refactor) — follow along or jump ahead. Let's start with the one that matters most. ## **1. Flows: Genkit's Core Abstraction** Without Genkit, calling an AI model in Dart looks something like this: build a prompt, call `generate()`, handle the response, catch errors. The configuration — model name, tools, system prompt, retry logic — lives wherever you make the call. It works, but it doesn't scale. You can't trace it, you can't test it in isolation, and you can't reuse it without copy-pasting. A **flow** is Genkit's answer to this. Think of it as a function with superpowers: it has a name, typed inputs and outputs, and Genkit automatically traces its execution. You define a flow once, and you can call it from anywhere. Here's what our shopping assistant flow looks like: ```dart _shoppingAssistantFlow = _genkit.defineFlow, String, void, void>( name: 'shoppingAssistantFlow', fn: (messages, context) async { final response = await _genkit.generate( model: googleAI.gemini('gemini-2.5-flash'), config: GeminiOptions( thinkingConfig: ThinkingConfig( thinkingBudget: 0, includeThoughts: false, ), ), messages: messages, tools: [ _surfaceUpdateTool, _beginRenderingTool, _deleteSurfaceTool, ], maxTurns: 30, ); return response.text; }, ); ``` The type signature tells the story: `defineFlow, String, void, void>` means this flow takes a list of messages in and returns a string out. All the configuration — which model, which tools, how many turns — is encapsulated inside. The callsite becomes a single line: ```dart final text = await _shoppingAssistantFlow(messages); ``` This matters for three reasons: 1. the flow has a name (shoppingAssistantFlow), so Genkit's dev tooling can trace every execution — you can see exactly what the model received, what tools it called, and what it returned; 2. flows compose: you can call one flow from inside another, which we'll do later when we build a structured recommendation flow that feeds into this one; 3. a flow is just a typed function with a name, you can test it in isolation without spinning up your entire UI. ![Genkit Dev UI showing the shoppingAssistantFlow trace and execution](/assets/images/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/body-0.avif) ![Genkit Dev UI trace detail showing model requests and tool calls](/assets/images/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/body-1.avif) > **Try it yourself:** Install the Genkit CLI (`npm install -g genkit-cli`), create a standalone Dart script that defines your flows (see `tool/dev_ui.dart` in the repo), and run `genkit start -- dart run tool/dev_ui.dart`. The Dev UI opens at `http://localhost:4000`, where you can run flows, inspect traces, and debug tool calls — all without your Flutter app running. See the [Genkit developer tools docs](https://firebase.google.com/docs/genkit/devtools) for more. ## **2. Middleware: Retry Logic You Don't Have to Write** AI API calls fail. If you've worked with any LLM provider — Gemini, OpenAI, Claude — you've experienced it: rate limits hit, services go temporarily unavailable, and a transient error shouldn't crash your user's session. The standard fix — a retry loop with exponential backoff — is maybe 30 lines of boilerplate you'll write identically in every project. Genkit ships a `retry` middleware that plugs directly into any `generate()` call via the `use` parameter. First, register `RetryPlugin` when initializing Genkit: ```dart _genkit = Genkit(plugins: [googleAI(apiKey: apiKey), RetryPlugin()]); ``` Then use it in any `generate()` call: ```dart final response = await _genkit.generate( model: googleAI.gemini('gemini-2.5-flash'), messages: messages, tools: [ /* ... */ ], use: [ retry( maxRetries: 3, initialDelayMs: 500, maxDelayMs: 5000, backoffFactor: 2, statuses: [ StatusCodes.UNAVAILABLE, StatusCodes.RESOURCE_EXHAUSTED, StatusCodes.DEADLINE_EXCEEDED, ], ), ], ); ``` Three retries, exponential backoff starting at 500ms, capped at 5 seconds, and only for the status codes that represent transient failures. The `use` parameter takes a list, so you can stack multiple middleware — logging, caching, retry — in whatever order makes sense. ## **3. Custom Tools: Giving the Model Access to Your Business Logic** Our shopping assistant already has UI tools — `surfaceUpdate`, `beginRendering`, `deleteSurface` — that let the model build interactive interfaces (see [Part 1](https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/) for how those work). But what if the model needs to *find* the right products before it can display them? You need to give it access to your data. In Genkit, you do that with **tools**. A tool is a function the model can call during generation. You define it with a name, a description (so the model knows when to use it), an input schema (so the model knows what parameters to pass), and an implementation. Genkit handles the rest — validating inputs, invoking the function, and feeding the result back to the model. Here's a `searchProducts` tool backed by a hardcoded inventory (in a real app, this would hit your database or API): ```dart _searchProductsTool = _genkit.defineTool, String>( name: 'searchProducts', description: 'Searches the product inventory by keyword query. Returns ' 'matching products with name, price, description, category, ' 'brand, and rating. Use this to find products before rendering ' 'them with surfaceUpdate/beginRendering.', inputSchema: SchemanticType.from>( jsonSchema: { 'type': 'object', 'properties': { 'query': { 'type': 'string', 'description': 'Search query — matches product name, ' 'description, category, or brand.', }, 'category': { 'type': 'string', 'description': 'Optional category filter.', }, 'maxResults': { 'type': 'integer', 'description': 'Maximum results to return. Defaults to 5.', }, }, 'required': ['query'], }, parse: (json) => json as Map, ), fn: (input, _) async { final results = searchProducts( query: input['query'] as String, category: input['category'] as String?, maxResults: (input['maxResults'] as num?)?.toInt() ?? 5, ); return jsonEncode(results.map((p) => p.toJson()).toList()); }, ); ``` With this tool registered alongside the UI tools, the model gains a two-step workflow. When a user asks "show me running shoes," the model first calls `searchProducts` to find matching products from the inventory, then calls `surfaceUpdate` and `beginRendering` to display them as interactive product cards. The model doesn't just format data you hand it — it retrieves the right data first, then decides how to present it. ![Shopping assistant using searchProducts tool to find and display product cards](/assets/images/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/body-2.avif) Because tools are defined through Genkit's `defineTool`, they automatically participate in the tool-calling loop, show up in traces, and have validated input schemas. You write the business logic; Genkit handles the plumbing. ## **4. Structured Output: Typed Responses Without Parsing** When a user asks "what should I buy?", you want structured recommendations you can program against. Genkit's `outputSchema` parameter on `generate()` constrains the model to return JSON conforming to a schema you define. We built a dedicated flow for this: ```dart _quickRecommendationFlow = _genkit.defineFlow>, void, void>( name: 'quickRecommendationFlow', outputSchema: SchemanticType.from>>( jsonSchema: { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'productName': { 'type': 'string' }, 'reason': { 'type': 'string' }, 'category': { 'type': 'string' }, 'priceRange': { 'type': 'string' }, }, 'required': ['productName', 'reason', 'category'], }, }, parse: (json) => (json as List).cast>(), ), fn: (userQuery, context) async { final response = await _genkit.generate>>( model: googleAI.gemini('gemini-2.5-flash'), messages: [ /* system + user messages */ ], outputSchema: /* same schema */, ); return response.output!; }, ); ``` The flow's output type is `List>` — each map has a `productName`, `reason`, `category`, and optional `priceRange`. When the main flow detects a recommendation query, it calls this flow first to get structured data, then injects those recommendations back into the conversation for the UI rendering tools to display as product cards: ```dart final recommendations = await _quickRecommendationFlow(userText); messages.add(genkit.Message( role: genkit.Role.user, content: [ genkit.TextPart( text: 'Here are structured product recommendations I got. ' 'Please search for these products and display them as a ' 'ProductCarousel...\n\n${jsonEncode(recommendations)}', ), ], )); final text = await _shoppingAssistantFlow(messages); ``` This is flow composition in action: one flow extracts structured data, another renders it as UI. The schema guarantees the shape of the response — no parsing, no prompt engineering to coerce JSON output. ![Structured product recommendations rendered as a carousel via flow composition](/assets/images/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/body-3.avif) ## **5. Context: Letting the Model Know What the User Wants** A shopping assistant that forgets your cart between messages isn't much of an assistant. Genkit's context system lets you thread state through the entire generation pipeline — tools, middleware, and flows all have access to shared context. We built a `ShoppingContext` class that tracks cart items and user preferences: ```dart class ShoppingContext { final List _cartItems = []; final Map _preferences = {}; bool addToCart(String productName, double price) { if (_cartItems.any((item) => item.productName == productName)) { return false; } _cartItems.add(CartItem(productName: productName, price: price)); return true; } void setPreference(String key, String value) { _preferences[key] = value; } String toPromptSummary() { final buf = StringBuffer(); if (_cartItems.isNotEmpty) { buf.writeln('## Current Cart'); for (final item in _cartItems) { buf.writeln('- ${item.productName} (\$${item.price})'); } buf.writeln('Cart total: \$${cartTotal}'); } // ... preferences section ... return buf.toString(); } bool isInCart(String productName) => _cartItems.any((item) => item.productName == productName); } ``` Before each generation, incoming messages are scanned for cart events and preference signals (like "my budget is under $100"). The context is injected into the system prompt via `toPromptSummary()`, and the `searchProducts` tool annotates results with cart status: ```dart final json = results.map((p) { final map = p.toJson(); if (_shoppingContext.isInCart(p.name)) { map['inCart'] = true; } return map; }).toList(); ``` The model sees what's in the cart and can say "you already have those running shoes — want to see accessories instead?" The user's stated preferences influence recommendations. The pattern is straightforward: scan incoming messages for state changes, maintain it in a class, and inject it into the system prompt and tool handlers. Genkit doesn't prescribe how you manage conversation state — it gives you the pipeline hooks (system messages, tool context, middleware) to wire it in wherever it makes sense. ![Shopping assistant recognizing cart items and user preferences via context](/assets/images/blog/genkit-for-flutter-flows-middleware-and-tools-in-a-genui-shopping-assistant/body-4.avif) ## **6. What We Gained** We started with a Firebase AI migration that called `generate()` with tools — functionally identical to what we had before. After five focused changes, we have: - **Flows** that name and compose AI operations, making them observable and testable - **Middleware** that handles transient failures without custom retry loops - **Custom tools** that give the model access to business logic, not just UI rendering - **Structured output** that guarantees typed JSON responses without parsing hacks - **Context propagation** that threads user state through the entire pipeline None of these required swapping models or rewriting the UI layer. Genkit sits between your application code and the model API, adding the infrastructure that production AI apps need. The Flutter GenUI integration stays exactly the same — the `ContentGenerator` interface doesn't care whether you're using Firebase AI or Genkit behind it. One thing to note: in this blog, everything runs client-side — the Genkit instance, API calls, and tools all live in the Flutter web app. That's great for prototyping, but in production you'd want your API key and AI logic on a backend. Genkit is designed for this: you can run flows on a Dart server and call them from Flutter via Genkit's `RemoteAction` client. **Stay tuned for Part 3**, where we'll move Genkit to the backend and show how the same flows, tools, and middleware work server-side. If you're building AI features in Flutter and you've outgrown raw API calls, [Genkit](https://pub.dev/packages/genkit) is worth evaluating. Pair it with [GenUI](https://pub.dev/packages/genui) for model-driven UIs, and you have a solid foundation for production AI in Flutter. ### How I Built 5 Flutter Apps in 30 Days Using AI-Assisted Development and VGV Wingspan Source: https://verygood.ventures/blog/flutter-ai-development-wingspan-workflow/ ![Very Yummy Coffee multi-platform Flutter app mockup showing mobile, kiosk, POS, kitchen display, and menu board apps](/assets/images/blog/flutter-ai-development-wingspan-workflow/main.jpeg) The entire kiosk app — eight screens, full ordering flow, tests, and review — took one day. 95% of the code was generated correctly on the first shot. If you've seen [the talk I gave](https://verygood.ventures/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/) about Very Yummy Coffee's multi-device architecture, this is the part I didn't cover: how it actually got built that fast. In that talk, I showed [Very Yummy Coffee](https://github.com/VGVentures/very_yummy_coffee): five Flutter apps across five operating systems, all sharing a single codebase. A mobile ordering app, a self-service kiosk, a point-of-sale terminal, a kitchen display system, and a menu board. What I didn't spend much time on was _how_ I actually built all of that in a month, by myself. That's what this post is about. The short version: it happened because of a combination of Flutter's architecture, VGV's engineering standards, and an AI-assisted workflow called [VGV Wingspan](https://verygood.ventures/blog/vgv-wingspan-agentic-engineering-workflow/) that made each step of development feed directly into the next. ## The Workflow: Brainstorm, Plan, Build Wingspan is our AI-assisted engineering plugin for [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview). Here's the quick version. Wingspan organizes development into four phases: **brainstorm**, **plan**, **build**, and **review**. During brainstorming, it explores what you're trying to build through collaborative dialogue — asking structured questions, proposing approaches with tradeoffs, and capturing decisions in a document. During planning, it turns that brainstorm into a detailed implementation plan by researching your existing codebase, identifying patterns, and flagging gaps. During building, it executes the plan, writes tests, and runs five quality review agents in parallel before shipping a PR. The key insight is that each phase makes the next one dramatically better. A good brainstorm means the plan asks precise questions instead of vague ones. A good plan means the build phase already knows exactly which files to touch, which APIs to call, and which patterns to follow. It's compounding context, not starting from scratch each time. If you're familiar with the growing conversation around spec-driven development — writing structured specifications before generating code — Wingspan takes the same idea further. The brainstorm and plan phases don't just produce a spec; they produce a spec grounded in your actual codebase, your actual patterns, and your actual gaps. You can see the [brainstorm and plan documents](https://github.com/VGVentures/very_yummy_coffee/tree/main/docs) Wingspan generated throughout the project in the repository. ![Wingspan brainstorm plan build workflow diagram showing compounding context between AI development phases](/assets/images/blog/flutter-ai-development-wingspan-workflow/wingspan-workflow.avif) ## How Flutter's Architecture Enables Reliable AI Code Generation The [Very Yummy Coffee codebase](https://github.com/VGVentures/very_yummy_coffee) is, frankly, [boring](https://verygood.ventures/blog/boring-code-part-1/) — and that's the point. Repositories expose streams of data coming from the server and methods for sending events back. The `MenuRepository` returns a stream of menu groups. The `OrderRepository` returns a stream of the current order and exposes methods like `addItemToCurrentOrder()` and `submitCurrentOrder()`. Every app subscribes to the same streams, and every mutation goes through the same methods. State management is [Bloc](https://bloclibrary.dev) everywhere. Most blocs are 20-40 lines of code. They subscribe to a repository stream with `emit.forEach`, map data into state objects, and that's it. `MenuGroupsBloc` subscribes to `menuRepository.getMenuGroups()`. `CartBloc` subscribes to `orderRepository.currentOrderStream`. The pattern is so consistent you could almost template it. Then there's the shared UI. A `very_yummy_coffee_ui` package defines colors, typography, spacing, and reusable widgets like `OrderCard`, `StatusBadge`, and `ModifierGroupSelector`. When an app needs to display an order or a modifier picker, it composes existing components rather than building from scratch. Building five apps from a single Flutter monorepo across five operating systems is non-trivial — but it becomes possible when the architecture is this predictable. (If you want to go deeper on how we structure Flutter projects, check out our post on [Very Good Flutter Architecture](https://verygood.ventures/blog/very-good-flutter-architecture/).) The agent doesn't need to figure out _how_ to build something — it just needs to figure out _what_ to build. The discovery phase becomes reliable. It finds shared UI components, recognizes the state management pattern, and sees simple repository interfaces to work with. ![Flutter layered architecture diagram showing data, repository, Bloc, and shared UI package layers for AI code generation](/assets/images/blog/flutter-ai-development-wingspan-workflow/vgv-layered-architecture.avif) ## Engineering Standards as Agent Context At VGV, we've spent years distilling our engineering approach into what we call [Very Good Engineering](https://engineering.verygood.ventures/) — over 20 technical articles covering everything from layered architecture and state management to testing, theming, code style, and security. The core philosophy boils down to four qualities: consistent, flexible, approachable, and testable. In practice, that means strong opinions on things like how to structure layers (data, repository, business logic, presentation), how to name tests (natural sentences, grouped by entity and method), and how to handle state (Bloc with explicit events, never Cubit).

Engineering Docs

The standards behind the workflow

Where those four qualities turn into specific rules, with the reasoning behind each one.

Read our engineering philosophy

Before I started building Very Yummy Coffee, I converted those standards into markdown files that live directly in the repo under `ai-coding/standards/`. There are around 25 of them, organized by topic: architecture, CI/CD, code style, error handling, internationalization, state management, testing, UI, and general practices. When the AI agent writes code, it reads these files for context — so it knows that tests should use private mocks per file, that widgets should be found by type rather than hardcoded keys, that routes should use `context.go('/path')` with hardcoded strings, and that colors should come from design tokens instead of raw `Colors.green`. We've since packaged this idea up more formally as the [VGV AI Flutter Plugin](https://verygood.ventures/blog/very-good-ai-flutter-plugin/) — an open-source Claude Code plugin that embeds these battle-tested Flutter and Dart best practices directly into AI-assisted workflows. It covers accessibility, testing, design patterns, and more. For this project, I was working with an early version of the same concept: documented standards that the agent could read and follow. The result was that I didn't spend time or tokens correcting basic mistakes. The agent already knew how to write VGV-style Flutter code from the start. And during the build phase, Wingspan's five review agents — covering VGV standards, code simplicity, test quality, architecture, and PR readiness — enforced those same standards automatically. If something drifted, it got caught before I ever saw it. ## Pencil Filled the Design Gap For this project, I didn't have a designer available. I've tried designing things myself, but I'm no professional and it would have taken months to design all five apps. [Pencil](https://pencil.dev) filled that gap. If you haven't seen it, Pencil is a design tool that lives directly inside VS Code or Cursor — a canvas with layers, editable properties, and direct manipulation, embedded in your IDE instead of a separate app. Designs are stored as `.pen` files, which are JSON-based and version-controlled right alongside your code. You can even copy-paste frames from your existing design files and Pencil will preserve the layers, auto-layout settings, and styles. Here's what made Pencil particularly valuable for this project: it exposes an [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that AI agents can interact with programmatically. That distinction matters. When Wingspan's build agent reads a Pencil frame, it's not interpreting a screenshot or guessing at a visual — it's reading a structured data format that maps directly to layout properties. That means the agent could look at the kiosk's cart screen design, see the exact spacing, component hierarchy, and color tokens, and build toward that specification with real data rather than vibes. Pencil is still early — it's currently free during early access — and the rough edges show occasionally. But for this project, having designs version-controlled alongside code and readable by an AI agent was more useful than a polished tool that lived in a separate tab. ![Pencil design tool embedded in IDE showing Flutter kiosk app design alongside Dart code for AI-assisted development](/assets/images/blog/flutter-ai-development-wingspan-workflow/ai-design.avif) ## Putting It All Together The kiosk app took **one day to complete in a single PR** — and each piece described above contributed directly to that. Here's how. **Design gave the agent something concrete to build toward.** A Pencil design file provided agent-discoverable information about what each screen should look like — layouts, components, color usage. Any MCP-connected design tool would have worked the same way. Because the design and the code were accessible in the same environment, the agent could cross-reference one against the other. When it built the cart page, it could check whether the layout matched the design. When it saw a modifier row in the design, it knew to look for the corresponding component in the shared UI package. **Brainstorming and planning discovered the existing pieces.** This is the part that surprised me most. During the planning phase, Wingspan crawled the existing codebase and found all the repository methods, shared widgets, and patterns it would need. The questions it surfaced weren't high-level architecture questions — they were specific and actionable. At one point, it flagged: _"Your design shows a modifier row on the cart page, but the modifier data isn't included on the line item object."_ That's the kind of catch that would normally surface during code review or QA, not during planning. **Predictable architecture made discovery reliable.** Because every repository exposes streams, every bloc subscribes with `emit.forEach`, and every shared widget lives in the same package, the agent knew where to look and what to expect. It found `MenuGroupsBloc` in the mobile app, recognized it was a 32-line `emit.forEach` wrapper, and knew the kiosk version would be structurally identical. No guessing, no hallucinating APIs that don't exist. **Review agents kept the code on track.** After the build phase completed, Wingspan's review agents checked the output against VGV's engineering standards. Architecture violations, missing tests, unnecessary complexity — all caught automatically. Tests aren't just a downstream QA step here; they're the mechanism by which the agent self-corrects. The code that landed in the PR followed the same conventions as the rest of the monorepo. ![Convergence diagram showing design, architecture, standards, and review agents flowing into one-day kiosk app delivery with Wingspan](/assets/images/blog/flutter-ai-development-wingspan-workflow/putting-it-all-together.avif) ## What I Learned The lesson here isn't "AI writes code fast." It's that the _combination_ of good architecture, documented standards, visual design, and a structured agentic coding workflow produces results that none of those things achieve alone. It's driven by what's already worked for years, not some new fancy technique. Flutter's code sharing gave the agent a rich set of existing components and patterns to draw from. VGV's engineering standards eliminated an entire class of back-and-forth corrections. Pencil gave the agent a visual target. And Wingspan's brainstorm-plan-build pipeline ensured that by the time code was being written, the agent already understood the full context — what to build, which pieces already existed, and which conventions to follow. I've heard the criticism that AI agents generate slop — low-quality, generic code that you spend more time fixing than writing yourself. I think at this point, that's user error, not a tool problem. Agents generate slop when they don't have structure to work with: no architecture to follow, no standards to reference, no existing patterns to match. Give an agent a blank canvas and a vague prompt and yes, you'll get slop. Give it a predictable architecture, documented conventions, a design spec, and a phased workflow that builds context before writing code — and you get a kiosk app in a day. AI alone — without architecture, standards, or a phased workflow — would have meant most of my time reviewing and fixing generated code instead of shipping features. The architecture and standards didn't just help the AI — they made AI assistance _practical_ at this scale. If you're building Flutter apps and thinking about how AI fits into your workflow, start with the foundations: predictable architecture, documented conventions, and shared packages. Those are force multipliers whether you're coding by hand or working with an agent. Add structured AI tooling on top of that, and the ceiling for a single developer is higher than most teams assume. **Want to try this approach?** The [VGV AI Flutter Plugin](https://verygood.ventures/blog/very-good-ai-flutter-plugin/) embeds VGV's engineering standards directly into Claude Code. The [Very Good Engineering](https://engineering.verygood.ventures/) site documents the conventions behind it. And [VGV Wingspan](https://verygood.ventures/blog/flutter-ai-development-wingspan-workflow/) ties it all together with a brainstorm-plan-build workflow. All three are available for your team to use today. That's [VGV's AI Flutter Engineering ecosystem](https://verygood.ventures/resources/flutter-ai-engineering/), ready to ship! ### VGV Wingspan: An Agentic Engineering Workflow for Claude Code Source: https://verygood.ventures/blog/vgv-wingspan-agentic-engineering-workflow/ ![Wingspan is VGV's open-source agentic workflow for Claude Code — covering brainstorm, plan, build, and review phases to ship better Flutter apps faster.](/assets/images/blog/wingspan-agentic-engineering-workflow/main.jpeg) Handing a complex feature to an AI coding agent is the easy part. Getting the result back in a state you can actually ship is where things get difficult. The agent wrote the code, but did it follow your architecture conventions? Did it catch the edge cases? Did it even start from a well-formed plan? These are questions we have been wrestling with at VGV as we build with [Claude Code](https://code.claude.com/docs/en/overview) every day. AI agents are powerful, but without structured workflow guardrails, the output drifts. The architecture gets inconsistent. Tests get skipped. Reviews happen too late to matter. Wingspan is our answer to that gap. It is an open-source [agentic engineering workflow](https://verygood.ventures/resources/agentic-ai-systems/) that structures every AI-assisted coding session into four deliberate phases: brainstorm, plan, build, and review. Instead of hoping the agent produces something shippable, Wingspan makes sure it follows a disciplined software development lifecycle from the first prompt to the final commit. We are releasing Wingspan today in alpha (v0.0.1). It is open source, it is built for Claude Code first, and it encodes the engineering practices we use every day at VGV. You can explore the full codebase on GitHub: [Wingspan on GitHub](https://github.com/VeryGoodOpenSource/vgv-wingspan). ![Wingspan launch hero image — VGV's agentic SDLC tool for AI-assisted engineering workflows, shown as geometric wing structures formed from luminous particle streams](/assets/images/blog/wingspan-agentic-engineering-workflow/image1.avif) ## What Is An Agentic SDLC? A traditional software development lifecycle (SDLC) has well-defined stages: requirements, design, implementation, testing, and review. When AI agents enter the picture, those stages tend to collapse into a single "just generate the code" step. The planning disappears. The review becomes an afterthought. The architecture emerges by accident rather than by design. An agentic SDLC restores that structure. It treats the AI agent not as a replacement for the engineering process, but as a participant within it. Each phase of the lifecycle has a clear purpose, clear inputs, and clear outputs. The agent operates within those boundaries rather than inventing its own. This is closely related to what some teams call spec-driven development: defining clear specifications before the agent writes a single line of code. Wingspan operationalizes that principle by making each phase a concrete step the agent must complete. ## The Four Workflow Phases Wingspan organizes every engineering session into four phases, each invoked as a [Claude Code slash command](https://code.claude.com/docs/en/slash-commands): ![Wingspan agentic SDLC workflow — four phases of AI-assisted engineering: brainstorm, plan, build, and review connected in an iterative cycle](/assets/images/blog/wingspan-agentic-engineering-workflow/image2.avif) ### `/brainstorm` The brainstorm phase is where you and the agent explore the problem space together. Before any code is written, `/brainstorm` helps surface requirements, identify edge cases, and clarify scope. The agent asks questions, proposes approaches, and documents the constraints that will guide every subsequent phase. ### `/plan` Once the problem is well understood, `/plan` produces a structured implementation plan. This is the spec-driven development step: the agent generates a concrete plan covering architecture decisions, file structure, dependencies, and a sequenced list of tasks. Nothing gets built until the plan is reviewed and confirmed. ### `/build` With a plan in hand, `/build` executes the implementation. The agent writes code that follows the plan, respects the architecture decisions made in the planning phase, and adheres to project conventions. Because the plan exists as a reference, the agent stays on track rather than inventing its own path. ### `/review` After the build is complete, `/review` evaluates the result. The agent checks the implementation against the original plan, flags deviations, verifies test coverage, and surfaces potential issues that a standard code review might miss. The review phase can loop back to brainstorm if significant gaps are found, creating an iterative cycle. ![Wingspan running in a developer terminal via Claude Code — the /brainstorm command initiating an AI-assisted engineering workflow session](/assets/images/blog/wingspan-agentic-engineering-workflow/image5.avif) ## Why Claude Code First? We chose [Claude Code](https://www.anthropic.com/product/claude-code) as Wingspan's first platform because of its native support for [skills and slash commands](https://code.claude.com/docs/en/slash-commands), its strong reasoning capabilities, and its ability to operate as an agentic coding partner rather than just a completion engine. Claude Code's architecture made it possible to build a structured, multi-phase workflow that feels natural inside a developer's terminal. Support for additional platforms is on our roadmap, but Claude Code's skill system gave us the right foundation to ship something useful now. ## End-to-End Flutter Engineering with the VGV AI Flutter Plugin Wingspan covers the workflow. The [VGV AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin) covers the conventions. When you use Wingspan together with the [VGV AI Flutter Plugin](https://verygood.ventures/blog/very-good-ai-flutter-plugin/), you get end-to-end coverage of Flutter engineering workflows that follow VGV's established practices. The plugin provides Claude Code with skills for accessibility, testing, Bloc state management, layered architecture, and more. Wingspan's four-phase workflow ensures those skills are applied at the right time, in the right order. > The result: the agent does not just generate Flutter code. **It generates Flutter code that follows a consistent architecture, includes meaningful tests, and respects the conventions your team has agreed on.** ![End-to-end Flutter engineering stack — Wingspan AI workflows, VGV AI Flutter Plugin, and Flutter app layers combined for scalable AI-assisted development following VGV best practices](/assets/images/blog/wingspan-agentic-engineering-workflow/image3.avif) ## Beyond Flutter: All Tech Stacks Supported While Flutter is our home turf, Wingspan is not limited to it. ***The workflow phases are stack-agnostic by design.*** Teams working with web, native, or embedded systems can use the same brainstorm, plan, build, and review loop to bring structure to their agentic coding sessions. ![Wingspan multi-platform support — AI-assisted engineering workflows for Flutter, web, native, and embedded systems beyond a single tech stack](/assets/images/blog/wingspan-agentic-engineering-workflow/image4.avif) The Flutter-specific depth comes from the VGV AI Flutter Plugin integration. For other stacks, Wingspan provides the workflow structure while you bring your own conventions and tooling. ## Getting Started Wingspan is open source and available now in alpha. To get started: 1. Visit the [Wingspan repository on GitHub](https://github.com/VeryGoodOpenSource/vgv-wingspan). 2. Follow the installation instructions in the README to add Wingspan's skills to your Claude Code environment. 3. Open a project in Claude Code and try `/brainstorm` to start your first structured engineering session. This is an alpha release (v0.0.1). We are actively iterating on the workflow based on how we use it internally and on feedback from the community. If you find issues or have ideas, open an issue on the repo. We want to hear from you. ## What's Next Wingspan is one piece of a broader investment we are making in AI-native engineering at VGV. We are also building internal tooling, Very Good AI Assistant, that applies agentic workflows to large, complex engineering challenges: native-to-Flutter code migrations, comprehensive code assessments, and Figma-to-code patterns at scale. We will have more to share on that in a future post. For now, we are focused on making Wingspan's core workflow as solid as possible. We are sharing it early because the value is already real — even in alpha. Give Wingspan a try. Star the repo. [Open an issue](https://github.com/VeryGoodOpenSource/vgv-wingspan). And if your team is tackling agentic engineering challenges that go beyond what open-source tooling can solve, [we would love to talk](https://verygood.ventures/our-services/agentic-ai-solutions/). ### Fluttercon USA 2026: Flutter@Scale Enterprise Track Source: https://verygood.ventures/blog/fluttercon-usa-2026-flutter-at-scale-enterprise-track/ ![Flutter@Scale Enterprise Track at Fluttercon USA 2026](/assets/images/blog/fluttercon-usa-2026-flutter-at-scale-enterprise-track/flutterconusa-flutteratscale.webp) Flutter adoption at large companies doesn't look like Flutter adoption at startups. The challenges are different. The stakes are higher. That's why we're launching **Flutter@Scale** — a dedicated enterprise content track at [Fluttercon USA](https://flutterconusa.dev/) (July 16-17, Orlando), curated by Very Good Ventures. It's built specifically for engineering leaders and practitioners who are running Flutter in production at scale, and who have hard-won lessons worth sharing. > "Having followed Flutter since its early days, it's been remarkable to see it grow from an innovative cross-platform toolkit into a proven enterprise platform. Its evolution has positioned it as a trusted choice for organizations building high-quality, scalable applications at scale. We're thrilled to be working with David and the VGV team to showcase Flutter production at scale." > > — **Greg Fawson**, Fluttercon USA organizer ## Why This Track Exists At VGV, we've spent years [building and scaling Flutter applications for companies like Google, Toyota, Universal Studios, American Airlines, Betterment, and many others](https://verygood.ventures/blog/top-companies-using-flutter/). That work has taught us something clear: teams that succeed at Enterprise scale face a different class of problems across platforms, teams, and business units. ![Enterprise challenges with Flutter at scale](/assets/images/blog/fluttercon-usa-2026-flutter-at-scale-enterprise-track/enterprise-challenges.webp) Problems like: - How do you manage a monorepo with dozens or hundreds of contributors? - How do you keep performance tight when your app serves millions of users? - How do you integrate AI tooling into an engineering workflow without breaking what already works? - How do you make the case to leadership for deeper Flutter investment and then actually deliver on that promise? - What breaks when you have millions of lines of code and what are techniques to keep teams moving forward? - What does AI and agentic workflows mean for enterprise Flutter teams? These aren't beginner questions. They're the questions that determine whether Flutter becomes a strategic advantage or just another technology investment that stalled. Flutter@Scale exists to put those questions — and the people who've answered them — on stage. ## What You'll Hear We've organized the track around the layered reality of enterprise Flutter: the technical problems that drive the work, the operational patterns that enable scale, and the leadership decisions that transform tools into strategy. Every session comes from a practitioner who's done the work. ![Flutter@Scale track sessions at Fluttercon USA 2026](/assets/images/blog/fluttercon-usa-2026-flutter-at-scale-enterprise-track/track-sessions.webp) On the technical side, expect sessions on **large codebase management** ([modularization, monorepos, architecture at scale](https://verygood.ventures/blog/very-good-flutter-architecture/)), **performance optimization** (fixing jank, memory leaks, and slow startup in production), **deployment and DevOps** (release cadence, automated testing, parallel builds), **observability and monitoring**, and **design systems** including [Server-Driven UI and GenUI](https://verygood.ventures/resources/genui/). Of course, AI integration will get substantial attention too, but what's actually working — not hype. Sessions on **AI-powered development** and **GenAI in workflows** will tackle practical applications and how to use [generative AI for coding, testing, and code review at scale](https://verygood.ventures/blog/very-good-ai-flutter-plugin/). ## Speaking Formats We've structured the track around formats that let practitioners teach at the depth that enterprise challenges demand: - **Lightning Talks (20 min):** A single idea or demo, delivered with focus - **Full Sessions (40 min):** Deep dives with code walkthroughs or case studies - **Panel Discussions (40-60 min):** Multiple experts unpacking trends and challenges together - **Office Hours:** Scheduled open Q&A with experts for personalized guidance ## Benefits for Participating Companies Companies who speak at Flutter@Scale get more than a stage and free passes for speakers. Participants can have a presence in the **Flutter@Scale Pavilion** — a branded communal space on the conference floor for networking, recruiting, and demos. Speaking companies also can plan **designated Office Hours** to engage directly with the broader Flutter community for deeper networking and recruiting at the pavilion. Participating companies receive **25% off any Fluttercon sponsorship package** and access to **team ticket discounts** to bring their engineering teams. ![Flutter@Scale Pavilion at Fluttercon USA 2026](/assets/images/blog/fluttercon-usa-2026-flutter-at-scale-enterprise-track/pavilion.webp) ## Submit a Talk The Call for Papers is open now through **April 30**. If your team is building, scaling, or operating Flutter in production, we want to hear from you. [Submit your talk on Sessionize](https://sessionize.com/fluttercon-usa-2026) and select the **Flutter@Scale** track during submission. ## Get Involved Getting involved is straightforward. Depending on your interest — speaking, team attendance, or partnership — here's where to start: - **Speaking and participation:** [hello@verygood.ventures](mailto:hello@verygood.ventures) - **Sponsorship and team registration:** [e.frau@droidcon.de](mailto:e.frau@droidcon.de) - **Sponsorship and booth packages:** [flutterconusa.dev/sponsordeck](https://www.flutterconusa.dev/sponsordeck) Flutter is driving the enterprise. Flutter@Scale is where the people doing that work come together to share what they've learned. We'll see you in Orlando. ### VGV at Google Cloud Next 2026: Bringing LLM-driven UIs to Life With Flutter's GenUI SDK Source: https://verygood.ventures/blog/vgv-at-google-cloud-next-2026/ ![Very Good Ventures at Google Cloud Next 2026 showcasing Flutter's GenUI SDK](/assets/images/blog/vgv-at-google-cloud-next-2026/main.jpg) ## We're Heading Back to Las Vegas Very Good Ventures (VGV) is proud to return to [Google Cloud Next '26](https://www.googlecloudevents.com/next-vegas), on April 22–24 at Mandalay Bay Convention Center. This year, we're showing up with something that's been years in the making—and we think it changes everything about how digital products are built and experienced. Come find us in the Agentic Mobile & Web demo zone, where we'll be running live demos of Flutter's [GenUI SDK](https://github.com/flutter/genui/tree/main/packages/genui): the technology that makes interfaces adapt, in real time, to who's using them. ## The Next Era of Multi-platform is Generative For the past several years, the conversation around multi-platform development has centered on one big idea: build once, deploy everywhere. At VGV, we've lived that idea—from theme park kiosks and sports apps to enterprise booking tools and financial services dashboards, and we've seen firsthand how much it changes what's possible for product teams. But the next frontier isn't just about running on every device. It's about adapting to every user. That's the shift that Google's GenUI SDK for Flutter makes possible. Instead of building fixed screens that every user sees the same way, GenUI dynamically assembles interfaces at runtime—selecting brand-safe visual components like cards, sliders, dropdowns, and maps based on the specific user's intent, context, and behavior. The interface becomes a living thing. It responds. It anticipates. It guides. This isn't a prototype. At GCN '26, [you can see it running live](https://verygood.ventures/events/google-cloud-next-2026/)! ## What to Expect at our Demo You'll find the VGV team in the Agentic Mobile & Web demo zone across all three days of the event. Here's what you'll see: **Flutter's GenUI SDK in action.** Watch a single codebase render adaptive, generative interfaces across mobile & web, each experience personalized to the user in real time. **Financial services tailored to your situation:** Our demo walks every individual through a series of questions to understand how it would work for your product. **The A2UI protocol at work.** Under the hood, the LLM (Gemini) never "paints" pixels or generates raw code. Instead, it acts as an orchestrator, selecting components from a curated, brand-safe catalog that are formally assembled and rendered by the Flutter GenUI SDK. The result: AI-generated UI that is predictable, on-brand, and compliant. **A conversation about your product.** Whether you're a Flutter team already looking to integrate GenUI, or a product leader on a React or native stack curious whether this applies to you—it does, and we'd love to talk through your specific situation. Bring your questions, your constraints, and your platform challenges. ## Why This Moment Matters At last year's Google Cloud Next, one theme dominated every conversation: AI is no longer a talking point—it's moving fast and breaking real ground. Generative AI showed real impact across document processing, real-time agentic interfaces, and developer tooling. But there was a gap. The most powerful AI systems were still largely text-based. You'd type a prompt, get a response in text, and be left to manually figure out your next step. That interaction model works. It's just not the most human-friendly one. GenUI closes that gap. It's the shift from bots that talk to bots that show, adapt, and respond visually. Rather than asking users to endlessly refine text prompts, the agent predicts what's needed next and generates the right visual components to collect it — making task completion faster, more intuitive, and more on-brand. At VGV, we've always found the most interesting work at the edges — before the playbook exists. We were building Flutter apps in production when the framework was still finding its footing. GenUI is that moment again. We're not here with a polished, off-the-shelf answer. We're experimenting, iterating, and making something genuinely new more viable — and we want to bring you along for it. At VGV, we've been building in this space in partnership with Google, and we believe Gen UI will define the next era of UX for intelligent digital products. ## A Demo Built for Google Cloud Next Our GCN '26 demo isn't an off-the-shelf walkthrough. We've built a purpose-made GenUI experience that shows what adaptive, agentic UI actually looks like in practice — not in a slide deck, but running live on Chromebook, Android, and iOS simultaneously. The same underlying GenUI logic renders context-aware interfaces across every device. The experience adapts to the user. The interface generates itself. If you've been thinking about what AI means for your product's UX, this is the live proof of concept you've been waiting for. ## Come Find Us Live Flutter GenUI SDK demo—adaptive, generative UI across mobile, web, and every device on April 22–24, at the Agentic Mobile & Web demo zone, Mandalay Bay Convention Center, Las Vegas. If you're attending Google Cloud Next '26, add us to your list. If you want to make sure you connect with a specific member of the VGV team, [reach out ahead of time here](https://verygood.ventures/contact-us/). [Learn more about GenUI](https://verygood.ventures/resources/genui/) and [connect with us at GCN '26](https://verygood.ventures/resources/genui/)! We'll see you in Vegas 🦄 ### Routing Best Practices in Flutter Source: https://verygood.ventures/blog/routing-best-practices-in-flutter/ Every Flutter app beyond a single screen needs routing. Get it wrong and you inherit tangled navigation logic, broken deep links, and state that vanishes when users press the back button. Get it right and navigation fades into the background — reliable, testable, invisible. This guide covers the patterns that scale: declarative route configuration with `go_router`, authentication guards, nested navigation, bottom bars, tab bars, navigator key management, type-safe routes, and deep linking. ## Why go_router Flutter ships with `Navigator` and `Navigator 2.0`. The first handles simple push/pop stacks. The second exposes the full Router API — powerful, but verbose. Writing a custom `RouterDelegate` and `RouteInformationParser` by hand demands significant boilerplate for features most apps need out of the box. `go_router` sits on top of `Navigator 2.0` and handles that boilerplate. It gives you: - **Declarative route configuration** — define your entire route tree in one place - **URL-based navigation** — every screen maps to a path - **Deep linking** — works on web, iOS, and Android without extra setup - **Redirection** — centralized auth guards and conditional routing - **Nested navigation** — bottom nav bars and tabbed interfaces that preserve state Add it to your project: ```yaml dependencies: go_router: ^14.0.0 ``` ## Configure Routes Declaratively Define your route tree as a single, readable structure. Each `GoRoute` maps a URL path to a widget builder: ```dart final router = GoRouter( initialLocation: '/', routes: [ GoRoute( path: '/', builder: (context, state) => const HomeScreen(), routes: [ GoRoute( path: 'products', builder: (context, state) => const ProductsScreen(), routes: [ GoRoute( path: ':id', builder: (context, state) { final id = state.pathParameters['id']!; return ProductDetailScreen(productId: id); }, ), ], ), ], ), ], ); ``` Three rules keep this tree maintainable: 1. **Nest routes to reflect hierarchy.** A product detail screen lives under `/products/:id`, not as a top-level route. The URL structure mirrors the UI structure. 2. **Keep builders thin.** Extract widget construction into the screen itself. The builder's job is to read parameters and pass them — nothing more. 3. **Define the tree in one file.** Scattering route definitions across the codebase makes it difficult to reason about navigation flow. A single `router.dart` file acts as a map of your entire app. ## Navigate with go, push, and replace `go_router` offers three navigation methods. Each serves a distinct purpose: ```dart // Replaces the entire navigation stack. Use for top-level navigation. context.go('/products'); // Pushes onto the stack. Use when the user should be able to pop back. context.push('/products/42'); // Replaces the current screen without adding to the stack. context.pushReplacement('/login'); ``` **Use `go` for declarative navigation.** It rebuilds the stack to match the target path. Navigating to `/products/42` with `go` creates a stack of `[Home, Products, ProductDetail]` — every ancestor route appears automatically. **Use `push` for imperative additions.** It adds a single screen on top of the current stack. This suits flows where users must return to the exact screen they came from, such as opening a modal or a detail view from a search result. **Use `pushReplacement` for one-off transitions.** A login screen that transitions to a home screen should replace, not push — the user should not navigate back to the login form. ## Guard Routes with Redirects Authentication checks belong in a redirect, not scattered across individual screens. `go_router` evaluates the `redirect` callback on every navigation event: ```dart final router = GoRouter( redirect: (context, state) { final isLoggedIn = AuthService.of(context).isLoggedIn; final isLoggingIn = state.matchedLocation == '/login'; if (!isLoggedIn && !isLoggingIn) return '/login'; if (isLoggedIn && isLoggingIn) return '/'; return null; // No redirect needed. }, routes: [ /* ... */ ], ); ``` This pattern centralizes access control. Every route passes through the same check. No screen needs to know whether the user is authenticated — the router handles it before the screen builds. Two guidelines for redirects: 1. **Return `null` to allow navigation.** Any non-null return triggers a redirect to that path. 2. **Guard against redirect loops.** Always check whether the user is already heading to the redirect target. Without the `isLoggingIn` check above, an unauthenticated user triggers an infinite loop: `/login` redirects to `/login` redirects to `/login`. For route-level guards, place a `redirect` on the individual `GoRoute`: ```dart GoRoute( path: '/admin', redirect: (context, state) { final isAdmin = AuthService.of(context).isAdmin; if (!isAdmin) return '/'; return null; }, builder: (context, state) => const AdminScreen(), ), ``` ## Understand Navigator Keys Before building any nested navigation, understand how `go_router` uses navigator keys. Every navigator in your app — root and nested — needs a unique `GlobalKey`. These keys determine where a route renders. ```dart final rootNavigatorKey = GlobalKey(debugLabel: 'root'); final shellNavigatorKey = GlobalKey(debugLabel: 'shell'); final feedNavigatorKey = GlobalKey(debugLabel: 'feed'); final searchNavigatorKey = GlobalKey(debugLabel: 'search'); final profileNavigatorKey = GlobalKey(debugLabel: 'profile'); ``` Declare all keys in your `router.dart` file, at the top, before the router definition. Name each key with a `debugLabel` — it appears in error messages and Flutter DevTools, and saves you from guessing which navigator threw an exception. Three rules govern navigator keys: 1. **The root key goes on `GoRouter`.** This is the top-level navigator that owns the entire screen. Every route renders here unless directed elsewhere. ```dart final router = GoRouter( navigatorKey: rootNavigatorKey, routes: [ /* ... */ ], ); ``` 2. **Shell keys go on `ShellRoute` or `StatefulShellBranch`.** Each shell or branch creates a nested navigator. Assign it a key so child routes can reference it — or escape it. 3. **`parentNavigatorKey` controls where a route renders.** By default, a route renders in the nearest enclosing navigator. Set `parentNavigatorKey` to override this. Point it at the root key to break out of a shell; point it at a specific branch key to render within that branch. ```dart // Renders inside the shell (default behavior). GoRoute( path: 'settings', builder: (context, state) => const SettingsScreen(), ), // Renders on the root navigator, outside the shell. GoRoute( path: 'checkout', parentNavigatorKey: rootNavigatorKey, builder: (context, state) => const CheckoutScreen(), ), ``` ### Common Navigator Key Mistakes **Reusing the same key for two navigators.** Each key must be unique. Sharing a key between a `ShellRoute` and a `StatefulShellBranch` triggers a runtime assertion. **Forgetting to pass the root key to `GoRouter`.** Without it, you cannot reference the root navigator from nested routes. The `parentNavigatorKey` on a child route has nothing to point at. **Creating keys inside `build` methods.** A `GlobalKey` created during `build` gets recreated on every frame. Declare keys as top-level variables or static fields — they must persist across rebuilds. ## Set Up Bottom Navigation Bottom navigation bars are the most common shell pattern. The bar persists across tabs; the content area swaps. Here is the complete setup, from route definition to widget. ![Bottom navigation demo](/assets/images/blog/routing-best-practices-in-flutter/bottom-nav-demo.gif) ### With ShellRoute (Stateless) Use `ShellRoute` when your tabs hold simple, read-only content that can rebuild without consequence — a news feed, a settings menu, a static list: ```dart final rootNavigatorKey = GlobalKey(debugLabel: 'root'); final router = GoRouter( navigatorKey: rootNavigatorKey, initialLocation: '/feed', routes: [ ShellRoute( builder: (context, state, child) { return ScaffoldWithBottomNav(child: child); }, routes: [ GoRoute( path: '/feed', builder: (context, state) => const FeedScreen(), routes: [ GoRoute( path: ':postId', parentNavigatorKey: rootNavigatorKey, builder: (context, state) => PostDetailScreen( postId: state.pathParameters['postId']!, ), ), ], ), GoRoute( path: '/search', builder: (context, state) => const SearchScreen(), ), GoRoute( path: '/profile', builder: (context, state) => const ProfileScreen(), ), ], ), ], ); ``` The `ScaffoldWithBottomNav` widget reads the current location to highlight the active tab: ```dart class ScaffoldWithBottomNav extends StatelessWidget { const ScaffoldWithBottomNav({required this.child, super.key}); final Widget child; @override Widget build(BuildContext context) { return Scaffold( body: child, bottomNavigationBar: NavigationBar( selectedIndex: _calculateSelectedIndex(context), onDestinationSelected: (index) => _onTap(context, index), destinations: const [ NavigationDestination(icon: Icon(Icons.home), label: 'Feed'), NavigationDestination(icon: Icon(Icons.search), label: 'Search'), NavigationDestination(icon: Icon(Icons.person), label: 'Profile'), ], ), ); } int _calculateSelectedIndex(BuildContext context) { final location = GoRouterState.of(context).matchedLocation; if (location.startsWith('/feed')) return 0; if (location.startsWith('/search')) return 1; if (location.startsWith('/profile')) return 2; return 0; } void _onTap(BuildContext context, int index) { switch (index) { case 0: context.go('/feed'); break; case 1: context.go('/search'); break; case 2: context.go('/profile'); break; } } } ``` > **Note:** Tab switching uses `context.go`, not `context.push`. `go` resets the navigation stack for that path. `push` would stack screens on top of each other, and the back button would cycle through every tab the user visited. ### With StatefulShellRoute (Stateful) Use `StatefulShellRoute` when tabs contain scroll positions, form input, or in-progress state that must survive tab switches. Each branch maintains its own navigator and widget tree: ```dart final rootNavigatorKey = GlobalKey(debugLabel: 'root'); final router = GoRouter( navigatorKey: rootNavigatorKey, initialLocation: '/feed', routes: [ StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return ScaffoldWithStatefulNav(navigationShell: navigationShell); }, branches: [ StatefulShellBranch( navigatorKey: GlobalKey(debugLabel: 'feed'), routes: [ GoRoute( path: '/feed', builder: (context, state) => const FeedScreen(), routes: [ GoRoute( path: 'details/:id', builder: (context, state) => FeedDetailScreen( id: state.pathParameters['id']!, ), ), ], ), ], ), StatefulShellBranch( navigatorKey: GlobalKey(debugLabel: 'search'), routes: [ GoRoute( path: '/search', builder: (context, state) => const SearchScreen(), ), ], ), StatefulShellBranch( navigatorKey: GlobalKey(debugLabel: 'profile'), routes: [ GoRoute( path: '/profile', builder: (context, state) => const ProfileScreen(), ), ], ), ], ), ], ); ``` The scaffold uses `StatefulNavigationShell` instead of a raw `child`. This shell exposes `goBranch` for tab switching and `currentIndex` for highlighting: ```dart class ScaffoldWithStatefulNav extends StatelessWidget { const ScaffoldWithStatefulNav({ required this.navigationShell, super.key, }); final StatefulNavigationShell navigationShell; @override Widget build(BuildContext context) { return Scaffold( body: navigationShell, bottomNavigationBar: NavigationBar( selectedIndex: navigationShell.currentIndex, onDestinationSelected: (index) { navigationShell.goBranch( index, // Navigate to the initial location of the branch // if the user taps the active tab again. initialLocation: index == navigationShell.currentIndex, ); }, destinations: const [ NavigationDestination(icon: Icon(Icons.home), label: 'Feed'), NavigationDestination(icon: Icon(Icons.search), label: 'Search'), NavigationDestination(icon: Icon(Icons.person), label: 'Profile'), ], ), ); } } ``` The `initialLocation` parameter on `goBranch` handles a common UX pattern: tapping the already-active tab resets it to the root screen. A user deep inside `/feed/details/42` taps the feed tab and returns to `/feed`. Without this flag, the tap does nothing. ### When to Choose Which | **Scenario** | **Use** | | --- | --- | | Tabs hold static or easily rebuilt content | `ShellRoute` | | Tabs contain scroll positions, forms, or media playback | `StatefulShellRoute` | | You need independent back stacks per tab (like Instagram) | `StatefulShellRoute` | | Your app has only two or three simple screens | `ShellRoute` | ## Set Up Tab Bar Navigation A `TabBar` inside an `AppBar` differs from bottom navigation in structure but follows the same routing principles. The tabs live inside a screen that is itself a route, and each tab panel maps to a sub-route. ### Tabs as Sub-Routes Define the tabbed screen as a `ShellRoute` whose builder provides the `TabController` and `TabBar`. Each tab panel is a child route: ```dart final tabNavigatorKey = GlobalKey(debugLabel: 'tabs'); ShellRoute( navigatorKey: tabNavigatorKey, builder: (context, state, child) { return DashboardScreen(child: child); }, routes: [ GoRoute( path: '/dashboard/overview', builder: (context, state) => const OverviewTab(), ), GoRoute( path: '/dashboard/analytics', builder: (context, state) => const AnalyticsTab(), ), GoRoute( path: '/dashboard/reports', builder: (context, state) => const ReportsTab(), ), ], ), ``` The `DashboardScreen` manages the `TabBar` and maps tab indices to routes: ```dart class DashboardScreen extends StatelessWidget { const DashboardScreen({required this.child, super.key}); final Widget child; @override Widget build(BuildContext context) { return DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( title: const Text('Dashboard'), bottom: TabBar( onTap: (index) => _onTabTapped(context, index), tabs: const [ Tab(text: 'Overview'), Tab(text: 'Analytics'), Tab(text: 'Reports'), ], ), ), body: child, ), ); } void _onTabTapped(BuildContext context, int index) { switch (index) { case 0: context.go('/dashboard/overview'); break; case 1: context.go('/dashboard/analytics'); break; case 2: context.go('/dashboard/reports'); break; } } } ``` ### Tabs with Preserved State For tabs that must retain state — a chart the user has zoomed, a form partially filled — wrap the tab routes in a `StatefulShellRoute` nested inside the parent shell: ```dart StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return DashboardScreen(navigationShell: navigationShell); }, branches: [ StatefulShellBranch( routes: [ GoRoute( path: '/dashboard/overview', builder: (context, state) => const OverviewTab(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/dashboard/analytics', builder: (context, state) => const AnalyticsTab(), routes: [ GoRoute( path: ':metricId', builder: (context, state) => MetricDetailScreen( metricId: state.pathParameters['metricId']!, ), ), ], ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/dashboard/reports', builder: (context, state) => const ReportsTab(), ), ], ), ], ), ``` The user drills into `/dashboard/analytics/conversion-rate`, switches to the Reports tab, then switches back — the metric detail screen is still there, scroll position intact. ## Nest Shells Inside Shells Real apps combine bottom navigation with tab bars, sidebars with detail panels, or drawers with nested tab views. `go_router` supports this through shell nesting. ### Rules for Nesting Shells 1. **Each shell must own a distinct navigator.** Assign separate `GlobalKey`s. Two shells sharing a key will conflict. 2. **The outermost shell controls persistence.** If the outer shell is a `StatefulShellRoute`, inner branches survive tab switches. If it is a plain `ShellRoute`, they rebuild. 3. **Full-screen routes sit outside all shells.** Place routes like onboarding, login, or media viewers as siblings of the outermost shell, not inside it. Point them at `rootNavigatorKey`. 4. **Limit nesting depth to two.** Three or more nested shells become difficult to debug. If your navigation requires a third level, reconsider whether the UX itself is too complex. Consider an app with a bottom navigation bar and a dashboard tab that itself contains a `TabBar`: ```dart final rootNavigatorKey = GlobalKey(debugLabel: 'root'); final router = GoRouter( navigatorKey: rootNavigatorKey, initialLocation: '/home', routes: [ StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return MainScaffold(navigationShell: navigationShell); }, branches: [ // Tab 1: Home (simple screen) StatefulShellBranch( routes: [ GoRoute( path: '/home', builder: (context, state) => const HomeScreen(), ), ], ), // Tab 2: Dashboard (contains its own TabBar) StatefulShellBranch( routes: [ ShellRoute( builder: (context, state, child) { return DashboardScreen(child: child); }, routes: [ GoRoute( path: '/dashboard/overview', builder: (context, state) => const OverviewTab(), ), GoRoute( path: '/dashboard/analytics', builder: (context, state) => const AnalyticsTab(), ), ], ), ], ), // Tab 3: Settings (simple screen) StatefulShellBranch( routes: [ GoRoute( path: '/settings', builder: (context, state) => const SettingsScreen(), ), ], ), ], ), // Full-screen routes that cover everything GoRoute( path: '/onboarding', parentNavigatorKey: rootNavigatorKey, builder: (context, state) => const OnboardingScreen(), ), ], ); ``` The outer `StatefulShellRoute` manages the bottom bar. The inner `ShellRoute` manages the dashboard's `TabBar`. Each layer owns its own navigator. The user's position in the dashboard tabs survives switching to the Home tab and back, because the outer shell preserves the branch state. ## Pass Data Between Routes Routes often need data beyond path parameters. `go_router` provides three mechanisms, each suited to a different scenario. ### Path Parameters Use when the value **identifies the resource** and must survive deep links, browser refreshes, and URL sharing. **Scenario:** A push notification links to a specific order. The user taps it, the OS opens `https://myapp.com/orders/98712`, and the app navigates straight to that order — even if it was killed in the background. The URL alone carries enough information to reconstruct the screen. ```dart GoRoute( path: '/orders/:orderId', builder: (context, state) { final orderId = state.pathParameters['orderId']!; return OrderDetailScreen(orderId: orderId); }, ), // From a notification handler: context.go('/orders/98712'); ``` The same URL works when a support agent pastes it into a chat, when a user bookmarks it on web, or when the browser reloads the page. Path parameters are the source of truth for *which* resource the screen displays. **Another scenario:** A social app where tapping a username anywhere — in a comment, a like notification, a mention — opens that profile: ```dart GoRoute( path: '/users/:username', builder: (context, state) { final username = state.pathParameters['username']!; return ProfileScreen(username: username); }, ), // From a mention in a post body: context.go('/users/dash_flutter'); ``` The URL `/users/dash_flutter` is shareable, indexable by search engines on Flutter web, and works as a deep link from an email or SMS. ### Query Parameters Use when the value **modifies how a screen behaves** but is optional — the screen renders without it, and sharing the URL preserves the customization. **Scenario:** An e-commerce app with a product listing screen. Users filter by category, price range, and sort order. These filters do not identify a specific product — they shape which products appear. A colleague sends the link `https://myapp.com/products?category=shoes&sort=price_asc&min=50` and the recipient sees the same filtered view: ```dart GoRoute( path: '/products', builder: (context, state) { final category = state.uri.queryParameters['category']; final sort = state.uri.queryParameters['sort'] ?? 'relevance'; final minPrice = state.uri.queryParameters['min']; return ProductListScreen( category: category, sort: sort, minPrice: minPrice != null ? double.tryParse(minPrice) : null, ); }, ), // Navigate with filters applied: context.go('/products?category=shoes&sort=price_asc&min=50'); // Navigate without filters — still works: context.go('/products'); ``` Remove every query parameter and the screen still loads — it shows all products with default sorting. That optionality is what distinguishes query parameters from path parameters. **Another scenario:** A search screen with pagination. The user searches, scrolls to page 3, and shares the URL. The recipient lands on the same page of results: ```dart GoRoute( path: '/search', builder: (context, state) { final query = state.uri.queryParameters['q'] ?? ''; final page = int.tryParse( state.uri.queryParameters['page'] ?? '', ) ?? 1; return SearchScreen(query: query, page: page); }, ), // Navigate: context.go('/search?q=go_router+deep+linking&page=3'); ``` ### Extra Use when you need to pass **data that cannot serialize to a URL** — an in-memory object, a file handle, raw bytes, or a complex model that already exists in the widget tree. **Scenario: Multi-step form wizard.** The user fills out a shipping address on screen one and taps "Continue." Screen two needs the validated `ShippingAddress` object to display a confirmation and calculate costs. No server call has happened yet — this data exists only in memory: ```dart GoRoute( path: '/checkout/confirm', builder: (context, state) { final address = state.extra! as ShippingAddress; return ConfirmationScreen(address: address); }, ), // After the user fills the address form: context.push('/checkout/confirm', extra: validatedAddress); ``` This works because the user reached the confirmation screen through in-app navigation. They did not type the URL into a browser. They will not share this link. The data's lifetime matches the navigation flow. **Scenario: Local photo editing.** The user picks a photo from their device gallery. The edit screen needs the raw `Uint8List` image bytes to render filters and crop tools. There is no server, no ID, no URL-friendly representation of this data: ```dart GoRoute( path: '/editor', builder: (context, state) { final imageBytes = state.extra! as Uint8List; return PhotoEditorScreen(imageBytes: imageBytes); }, ), // After picking a photo: final bytes = await picker.pickImage(); if (bytes != null) { context.push('/editor', extra: bytes); } ``` No path parameter or query string can carry raw bytes. `extra` is the only option. **Scenario: Returning a selection.** A checkout screen opens an "address picker" screen. The user selects an address, and the picker pops back with the chosen `Address` object. The picker pushes the result back through `extra` and `pop`: ```dart // In the address picker, when the user taps an address: context.pop(selectedAddress); // In the checkout screen, capturing the result: final address = await context.push
('/addresses/pick'); if (address != null) { setState(() => _selectedAddress = address); } ``` > **Tip:** Treat `extra` as a last resort. Data passed via `extra` does not survive browser refreshes, deep links, or URL sharing. It vanishes when the user copies the URL or hits F5. If you rely on `extra`, provide a fallback: fetch the data from a repository when `extra` is null. ### When to Use Which | **Question** | **Path param** | **Query param** | **Extra** | | --- | --- | --- | --- | | Does it identify *which* resource the screen shows? | Yes | | | | Should the URL work as a deep link or bookmark? | Yes | Yes | | | Is it optional — the screen works without it? | | Yes | | | Is it a filter, sort, or pagination value? | | Yes | | | Is it an in-memory object with no URL representation? | | | Yes | | Does it only exist during a single navigation flow? | | | Yes | | Must it survive a browser refresh? | Yes | Yes | | ## Manage Transition Animations `go_router` uses platform-default transitions. To customize them, use `pageBuilder` instead of `builder` and return a `CustomTransitionPage`: ```dart GoRoute( path: '/details/:id', pageBuilder: (context, state) { return CustomTransitionPage( key: state.pageKey, child: DetailScreen(id: state.pathParameters['id']!), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition(opacity: animation, child: child); }, ); }, ), ``` Three guidelines for transitions: 1. **Use `state.pageKey` as the page key.** This allows `go_router` to distinguish between pages and trigger the correct animation. Omitting it causes stale pages to linger in the navigator. 2. **Match transitions to the navigation pattern.** Forward navigation (push) suits slide-in. Lateral navigation (tab switch) suits fade or none. Modal presentation suits slide-up. 3. **Disable transitions for tab switches.** Within a bottom bar or tab bar, instant swaps feel correct. Animation between tabs feels sluggish: ```dart pageBuilder: (context, state) { return const NoTransitionPage(child: FeedScreen()); }, ``` ## Refresh Routes Reactively Authentication state, locale, or theme can change while the app runs. When they do, `go_router` must re-evaluate its redirects. Pass a `Listenable` to the `refreshListenable` parameter: ```dart final authNotifier = AuthNotifier(); // extends ChangeNotifier final router = GoRouter( refreshListenable: authNotifier, redirect: (context, state) { if (!authNotifier.isLoggedIn) return '/login'; return null; }, routes: [ /* ... */ ], ); ``` When `authNotifier` calls `notifyListeners()`, the router re-runs its `redirect` callback. A user whose session expires navigates to `/login` automatically — no manual check required. **Combine multiple listenables** when several sources can trigger a re-evaluation: ```dart final refreshNotifier = Listenable.merge([ authNotifier, localeNotifier, onboardingNotifier, ]); final router = GoRouter( refreshListenable: refreshNotifier, redirect: (context, state) { if (!authNotifier.isLoggedIn) return '/login'; if (!onboardingNotifier.isComplete) return '/onboarding'; return null; }, routes: [ /* ... */ ], ); ``` ## Test Your Routes Routing logic deserves tests. A broken redirect or a miswired navigator key surfaces only at runtime — often in production. Write tests that exercise the router directly. ### Test Redirects ```dart test('redirects unauthenticated users to login', () { final authNotifier = AuthNotifier()..setLoggedIn(false); final router = GoRouter( refreshListenable: authNotifier, redirect: (context, state) { if (!authNotifier.isLoggedIn) return '/login'; return null; }, routes: [ GoRoute(path: '/', builder: (_, __) => const HomeScreen()), GoRoute(path: '/login', builder: (_, __) => const LoginScreen()), ], ); router.go('/'); expect(router.state.matchedLocation, '/login'); }); ``` ### Test Parameter Extraction ```dart test('extracts product ID from path', () { String? capturedId; final router = GoRouter( routes: [ GoRoute( path: '/products/:id', builder: (context, state) { capturedId = state.pathParameters['id']; return const Placeholder(); }, ), ], ); router.go('/products/42'); expect(capturedId, '42'); }); ``` ### Test Shell Navigation Verify that tapping a tab navigates to the correct location and that the shell persists: ```dart testWidgets('bottom nav switches tabs', (tester) async { await tester.pumpWidget(MaterialApp.router(routerConfig: router)); await tester.pumpAndSettle(); // Verify initial tab expect(find.byType(FeedScreen), findsOneWidget); // Tap search tab await tester.tap(find.text('Search')); await tester.pumpAndSettle(); expect(find.byType(SearchScreen), findsOneWidget); // Bottom nav still visible expect(find.byType(NavigationBar), findsOneWidget); }); ``` ## Use Type-Safe Routes String-based paths are prone to typos and refactoring hazards. `go_router` supports code-generated, type-safe routes via the `go_router_builder` package: ```yaml dependencies: go_router: ^14.0.0 dev_dependencies: go_router_builder: ^2.7.0 build_runner: ^2.4.0 ``` Define routes as classes annotated with `@TypedGoRoute`: ```dart part 'routes.g.dart'; @TypedGoRoute( path: '/', routes: [ TypedGoRoute( path: 'products', routes: [ TypedGoRoute(path: ':id'), ], ), ], ) class HomeRoute extends GoRouteData { const HomeRoute(); @override Widget build(BuildContext context, GoRouterState state) { return const HomeScreen(); } } class ProductsRoute extends GoRouteData { const ProductsRoute(); @override Widget build(BuildContext context, GoRouterState state) { return const ProductsScreen(); } } class ProductDetailRoute extends GoRouteData { const ProductDetailRoute({required this.id}); final String id; @override Widget build(BuildContext context, GoRouterState state) { return ProductDetailScreen(productId: id); } } ``` Run `dart run build_runner build` to generate the routing code. Navigate with type-checked constructors instead of raw strings: ```dart // Type-safe — the compiler catches errors. const ProductDetailRoute(id: '42').go(context); // String-based — typos compile fine and fail at runtime. context.go('/products/42'); ``` Type-safe routes eliminate an entire category of bugs. Path parameters become constructor arguments. Change a parameter name and the compiler flags every call site. ## Handle Deep Links `go_router` handles deep links by default. Any URL that matches a defined route resolves automatically — on web, via Android App Links, or via iOS Universal Links. For platform-specific deep link setup, configure your `AndroidManifest.xml` and `apple-app-site-association` file to route your domain's URLs to your app. Once the OS delivers the URL, `go_router` parses it and navigates to the matching route. Test deep links during development: ```bash # Android adb shell am start -a android.intent.action.VIEW -d "https://example.com/products/42" # iOS xcrun simctl openurl booted "https://example.com/products/42" ``` Two practices for robust deep linking: 1. **Validate parameters in builders.** A deep link may contain an invalid product ID. Handle missing or malformed data gracefully — show an error screen rather than crashing. 2. **Match your route structure to your URL structure.** Deep links work because URLs map directly to routes. Break this mapping and deep links break with it. ## Handle Errors Gracefully When a user navigates to a route that does not exist — through a stale deep link, a mistyped URL, or a broken in-app link — show a helpful error screen: ```dart final router = GoRouter( errorBuilder: (context, state) { return ErrorScreen(error: state.error); }, routes: [ /* ... */ ], ); ``` The error screen should offer a clear path back: a button that navigates to the home screen, a search bar, or a relevant suggestion. A blank page or an unhandled exception teaches the user nothing. ## Checklist Before shipping your routing layer, verify these points: - All routes and navigator keys defined in a single file - Every navigator key has a unique `debugLabel` - Navigator keys declared as top-level variables, not inside `build` - `redirect` handles authentication at the router level - `refreshListenable` re-evaluates redirects when auth state changes - Redirect callbacks guard against infinite loops - `ShellRoute` or `StatefulShellRoute` wraps persistent navigation UI - `StatefulShellRoute` used where tab state must survive switches - Full-screen overlays use `parentNavigatorKey` to escape the shell - Nested shells limited to two levels - Tab switches use `go`, not `push` - `goBranch` with `initialLocation` resets tabs on re-tap - Path parameters used for resource identifiers - Query parameters used for optional filters and sort orders - `extra` used sparingly, with a fallback fetch strategy - Type-safe routes replace string-based paths - `pageBuilder` with `state.pageKey` used for custom transitions - Deep links tested on Android, iOS, and web - `errorBuilder` provides a graceful fallback for unknown routes - Navigation methods (`go`, `push`, `pushReplacement`) chosen deliberately - Redirect, parameter, and shell navigation covered by tests Routing is infrastructure. Build it once with clear conventions, and every feature you ship afterward navigates without friction. ### Using Maestro MCP with Claude for Mobile UI Test Automation Source: https://verygood.ventures/blog/maestro-mcp-claude-mobile-ui-test-automation/ Mobile QA has always been a discipline that demands precision: juggling simulators and real devices, iOS and Android quirks, [flaky selectors and ever-changing UI hierarchies](https://verygood.ventures/blog/elevating-flutter-test-reports-with-allure/) — all while keeping pace with a dev team that ships fast. The traditional answer has been to write more automation, maintain bigger YAML files, and hope the test suite doesn't collapse the moment someone renames a button. We recently set up Maestro MCP on a project and spent time with the workflow. By connecting Maestro — one of the most widely adopted mobile UI testing frameworks — to an AI coding assistant like Claude through the Model Context Protocol, QA engineers and Test Engineerins gain an AI-assisted mobile testing workflow directly embedded in their existing tools. This post covers what the integration looks like in practice, what worked well, and where the limits are. --- ## What Is the Model Context Protocol (MCP)? The [Model Context Protocol (MCP) is an open standard](https://modelcontextprotocol.io/introduction), [introduced by Anthropic in November 2024](https://www.anthropic.com/news/model-context-protocol), that creates a uniform interface for connecting large language models to external tools, data sources, and services. Instead of every tool requiring a bespoke integration with every AI model, MCP defines a single protocol that any compliant client (like Claude) can use to talk to any compliant server (like Maestro). The architecture is straightforward: * **MCP Servers** expose tools and resources through a standardized interface * **MCP Clients** (AI assistants) consume those tools via the protocol * **Transport Layer** handles communication over stdio, HTTP with SSE, or WebSocket ![Model Context Protocol architecture — MCP Server, Transport Layer (stdio, HTTP SSE, WebSocket), and MCP Client diagram showing bidirectional communication](/assets/images/blog/maestro-mcp-claude-mobile-ui-test-automation/mcparch.avif) The practical implication: Maestro doesn't need to build a custom Claude plugin, and Claude doesn't need a bespoke Maestro SDK. They speak the same language out of the box. --- ## What Maestro MCP Actually Does for QA Teams Once you configure Maestro as an MCP server and connect it to Claude, your AI assistant gains direct access to the full Maestro CLI — the same commands you'd run in your terminal, now driven by natural language. Here's what that unlocks: ### 1. Write Test Flows in Plain Language Instead of hand-authoring YAML, you can describe what you want in natural language and Claude generates a valid Maestro flow. *"Create a flow that opens the app, taps the login button, enters credentials, submits the form, and verifies the home screen appears."* Claude produces a properly structured YAML flow tied to [Maestro's YAML flow syntax](https://docs.maestro.dev/getting-started/writing-your-first-flow) and command reference. Here's what that output looks like in practice: ```yaml appId: com.example.myapp env: BASE_URL: "https://your-app-url.com" LOGIN_EMAIL: "user@example.com" LOGIN_PASSWORD: "your-password" --- # Navigate to Login - openLink: "${BASE_URL}/login" - waitForAnimationToEnd - assertVisible: id: "login-page" # Log in - tapOn: id: "login-email-input" - inputText: "${LOGIN_EMAIL}" - tapOn: id: "login-password-input" - inputText: "${LOGIN_PASSWORD}" - hideKeyboard - tapOn: id: "login-submit-btn" - waitForAnimationToEnd - assertVisible: id: "feed-page" ``` This test flow generation capability is particularly valuable when [onboarding new team members unfamiliar with Maestro's DSL](https://verygood.ventures/blog/robot-testing-in-flutter/), or when rapidly scaffolding test coverage for a new feature. ### 2. Automatically Debug Failing Tests This is where the practical value surfaces. When a test fails with a vague "Element not found" error, instead of manually digging through the view hierarchy, you can describe the failure to Claude and have it analyze the issue and suggest a fix — immediately. Claude can inspect the nested view hierarchy of the current screen in CSV format, cross-reference it with the failing flow, and propose the corrected selector or interaction. Debugging that previously required manually tracing through a hierarchy and iterating on selectors collapses into a short back-and-forth conversation. That reduction in per-failure overhead compounds quickly across a growing test suite. ![Maestro MCP automated test debugging — Claude AI analyzing a failing mobile UI test and correcting the element selector from a live device view hierarchy](/assets/images/blog/maestro-mcp-claude-mobile-ui-test-automation/aidebug.avif) ### 3. Control Emulators and Real Devices Interactively Maestro MCP gives Claude the ability to directly interact with your running emulators and simulators — tapping elements, inputting text, pressing back, validating UI state — all through natural language. In exploratory testing, you can describe a scenario conversationally and Claude executes it live. ### 4. Validate Flow Syntax Before Running Before committing a new test flow, Claude can validate the YAML syntax against Maestro's spec. This catches malformed flows before they hit CI, reducing pipeline failures caused by test authoring errors rather than actual bugs. ### 5. Access Built-in Testing Knowledge Maestro MCP bundles the YAML flow syntax guide, command reference, and mobile testing best practices for debugging and CI/CD setup. Claude draws on this context when generating and reviewing flows, keeping output idiomatic and production-ready. --- ## See It in Action The demo below shows a Maestro test flow running against a live device — tapping through the app, validating UI state, and completing the full sequence end to end.
--- ## How to Set Up Maestro MCP with Claude **Step 1:** Install the Maestro CLI if you haven't already. This is a hard requirement — the MCP server is exposed through the CLI, so nothing works without it. You can follow the official installation guide at [docs.maestro.dev](https://docs.maestro.dev). **Step 2:** Once the CLI is installed, run `maestro mcp` from your terminal to start the MCP server. No separate installation is needed for the server itself — it comes bundled with the CLI. **Step 3:** Add the MCP configuration to your IDE or AI client. For Claude Desktop, edit: * macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` * Windows: `%APPDATA%\Claude\claude_desktop_config.json` **Step 4:** Point the config at your Maestro CLI executable. If Maestro is on your PATH, the setup is a few lines of JSON. If not, use the full path to the executable. Once configured, Claude has full access to Maestro's toolset. ![Maestro MCP setup — terminal running maestro mcp command and Claude Desktop JSON configuration file for connecting Maestro to Claude](/assets/images/blog/maestro-mcp-claude-mobile-ui-test-automation/setupconf.avif) --- ## What This Isn't It's worth being clear about the limits. Maestro MCP isn't a replacement for thoughtful test strategy. Claude won't know which user flows matter most to your business, or where your app's riskiest edge cases live — that expertise still belongs to your QA team. What it does is remove friction from the execution layer, so the time you do spend on test automation goes toward decisions that actually require human judgment. There are also cases where natural language test generation produces flows that are syntactically valid but miss business logic — a login flow that checks for the home screen but doesn't verify the right user session, for example. The AI handles mechanics well; domain reasoning still needs a human in the loop. --- ## Where This Fits For QA engineers and SDETs, the value of Maestro MCP shows up in a few specific places. Generating a test flow from a user story description is faster than authoring it manually. Teams can [keep pace with feature development without sacrificing test depth](https://verygood.ventures/blog/road-to-100-test-coverage/). AI-assisted triage of "element not found" and selector drift issues cuts the feedback loop on failing tests. And natural language authoring means more people on the team can [contribute to test coverage](https://verygood.ventures/blog/guide-to-flutter-testing/) without needing deep Maestro expertise — lowering the floor without lowering the standard. Worth noting: Maestro MCP operates cross-platform against compiled APK and IPA files with no app instrumentation required. If you've seen other MCP-based mobile testing tools that attach to a specific framework's debug session, the distinction matters. Maestro works regardless of whether your app is built in Flutter, React Native, or native iOS/Android. --- ## The Honest Take Maestro MCP changes how QA engineers interact with their test tooling. By bridging Claude's language understanding with Maestro's mobile automation capabilities, it replaces the manual workflow for test authoring, debugging, and device interaction with a conversational one. If your team runs mobile UI test automation on iOS or Android and uses — or is evaluating — Maestro, set up the MCP integration. Configuration takes under an hour. Whether the workflow change sticks depends on how your team actually debugs failing tests — that's the part worth evaluating. --- *The Maestro MCP documentation at [docs.maestro.dev](https://docs.maestro.dev/get-started/maestro-mcp) covers the full setup guide and tool reference.* ### Flutter Gives You a Video Player. Here's Why That's Not Enough for a Real Feed. Source: https://verygood.ventures/blog/video-playback-flutter-feed/ Flutter ships with a VideoPlayer widget. You can drop it in a PageView, hook up a controller, and have something playing in under an hour. It looks like progress. Then you test on a budget Android phone, swipe through twenty videos, and the app crashes without an error message. The gap between "it plays a video" and "it feels like TikTok" is enormous, and it's not because Flutter is lacking. It's because smooth video feed playback is a resource management problem disguised as a UI problem. When we built [diVine](https://divine.video/), we had to learn this the hard way. Every solution we reached changed how we thought about the one before it. **Here's what we learned, and the architectural shift that made everything click.** ## The Flutter Video Ecosystem Has a Gap Flutter's [video_player](https://pub.dev/packages/video_player) package wraps AVPlayer on iOS and ExoPlayer on Android. It works. But each controller is tied to a single URL. To switch videos, you dispose the controller and create a new one. For a product detail page with one embedded clip, that's fine. For a feed where users swipe through dozens of videos per session, the constant create-dispose cycle becomes a bottleneck that compounds with every scroll. The packages built on top of [video_player](https://pub.dev/packages/video_player) inherit this constraint. [chewie](https://pub.dev/packages/chewie) adds a richer UI layer (progress bars, fullscreen toggle) but doesn't solve the reusability problem underneath. [better_player](https://pub.dev/packages/better_player) is in maintenance mode. [awesome_better_player](https://github.com/nateshmbhat/awesome_video_player) has been discontinued entirely. We looked for a production-grade reusable player in Flutter and didn't find one. [media_kit](https://pub.dev/packages/media_kit), which wraps libmpv, comes closest. It allows calling player.open() with a new URL on an existing instance. The native player survives the switch; only the media source changes. It also leverages libmpv's hardware-accelerated decoding pipeline, which reduces CPU load when you're running multiple streams concurrently. This single capability, player reuse, turned out to be the foundation for everything else we built. Without it, pooling is impossible. And without pooling, you're fighting platform limits with every scroll event. ![Diagram comparing the create-play-dispose cycle of traditional video controllers with the reusable player model where the same player opens different URLs](/assets/images/blog/video-playback-flutter-feed/player-reuse.png) ## The Core Insight: Players Are Not Widgets Here's the mental shift that changed our architecture: stop treating video players as disposable UI components and start treating them as a shared, managed resource. Every native video player instance holds GPU textures, codec contexts, and network buffers. Android devices typically support 5 to 16 simultaneous hardware decoders, depending on the chipset. Exceed that limit and the app doesn't throw an error. It just dies. The naive approach (one player per visible video, created on mount, disposed on unmount) breaks down as soon as the user scrolls at any reasonable speed. The fix is to decouple players from widgets entirely. We built a **player pool**: a fixed-size set of reusable players, keyed by URL, with least-recently-used eviction. Three design decisions make it work. ![Diagram of a player pool with fixed size, showing how players are keyed by URL and how the least recently used player is evicted and reassigned](/assets/images/blog/video-playback-flutter-feed/player-pool.png) **First, key players by URL, not by widget index.** When someone scrolls back to a video they already watched, the pool returns the existing player instantly, buffer intact. No rebuffering. This is a subtle UX win that users feel even if they can't name it. **Second, evict the least recently used.** When the pool is full and a new video needs a player, dispose the one the user interacted with longest ago. This naturally keeps "nearby" players alive while freeing resources from content the user has moved past. **Third, fire eviction callbacks synchronously.** When the pool reclaims a player, the UI needs to know before Flutter renders the next frame. Synchronous callbacks let the widget swap in a placeholder image before the native texture is destroyed. Without this, you get a single frame of garbage pixels. Small detail, big difference. ## Mute, Don't Pause This was the single biggest UX win in Divine, and it's counterintuitive. When a native video player pauses and then resumes, most implementations need to re-establish the network buffer. That takes 200 to 800 milliseconds. In a feed where users swipe rapidly, the delay compounds into a sluggish experience they feel but can't articulate. They just know the app feels slow. The pause itself is the problem. So instead of pausing adjacent videos, keep them playing silently at volume zero. The buffer stays hot because the player never stops. When the user swipes, the transition is simple: mute the old video (it keeps playing, invisible and silent), seek the new video to start, and set its volume to full. ![Diagram comparing pause-based playback with rebuffering delay versus mute-based playback where the buffer stays hot, showing a preload window of 1 behind and 2 ahead](/assets/images/blog/video-playback-flutter-feed/mute-vs-pause.png) A **preload window** defines how many videos get this treatment: we use 2 ahead and 1 behind the current index. That means 3 to 4 simultaneous streams at any given time. The bandwidth cost is real, but for short content under 30 seconds it's acceptable. And once a video has been cached to disk, the muted playback costs zero bandwidth; it's reading from a local file path. ## The Async Gap That Makes Your App Feel Unfinished Traditional cache libraries check a database, check the filesystem, and return a path. All async. This sounds fine until you realize it creates a visible "loading" flash between cached videos as the player either starts buffering from the network while the cache check runs or blocks the load entirely. In a feed where milliseconds determine whether the experience feels polished or broken, that async gap is unacceptable. We built a **synchronous cache manifest** instead: an in-memory Map\ of cached URLs to local file paths, populated at app startup. Every subsequent cache lookup is a synchronous map access. The business logic replaces network URLs with file:// paths before the player ever sees them. No async gap. No wasted network requests. No loading flash. ![Diagram comparing async cache lookup with database and filesystem checks causing a loading flash, versus synchronous map lookup that returns a file path instantly](/assets/images/blog/video-playback-flutter-feed/sync-cache.png) For background pre-caching, two constraints keep things stable. First, limit to one concurrent download at a time. More parallelism competes with the foreground stream for bandwidth, causing the currently-playing video to stutter. Second, deduplicate requests so multiple calls for the same URL piggyback on a single in-flight download. ## Scroll Performance Is a State Architecture Problem If you model video state as a single object for the whole feed, every state change (video #3 finishes buffering, video #7 starts playing) triggers a rebuild of every video widget. During fast scrolling, this means dropped frames and visible jank. The users who scroll fastest are often your most engaged. Losing frames for them means losing the audience that matters most. The solution is per-item state isolation. Give each video index its own ValueNotifier, created lazily. Each video widget subscribes only to its own notifier via ValueListenableBuilder. When video #3's state changes, only widget #3 rebuilds. Everything else stays untouched. ![Diagram comparing whole-feed FeedState rebuild where one change rebuilds all widgets, versus per-item ValueNotifier rebuild where one change only rebuilds the affected widget](/assets/images/blog/video-playback-flutter-feed/per-item-state.png) One subtlety worth noting: when the pool evicts a player that was previously assigned to a given index, you need an identity check on the eviction callback. Without it, a stale callback from a previously-released index can trigger incorrect state updates in the wrong widget. ## Platform Divergence Will Surprise You Code that works perfectly on one platform can crash the other. This isn't theoretical; it happened to us repeatedly. **Android MediaCodec limits** are the most common source of silent crashes. Every hardware decoder slot is a scarce resource, and exceeding the device-specific limit produces no error message. Budget Android devices add another layer: many choke on H.264 High Profile content entirely. Your CDN needs to serve an HLS variant encoded with Baseline Profile as a fallback, because that's the only reliable way to handle the long tail of Android hardware without dropping users. **iOS codec gaps** are simpler but still surprising. Native AVPlayer does not support WebM or VP9. If your content pipeline produces VP9, iOS users see a blank frame. Ensure your transcoding pipeline always includes an H.264 or HEVC variant. These aren't edge cases. They're the default experience for a meaningful percentage of your users. ## Lifecycle Bugs Are the Hardest to Reproduce When the user backgrounds the app, video playback should stop. Getting this wrong causes audio leaks: video sound playing while the app is invisible. Getting it almost right causes subtler bugs that only surface in specific sequences (background, switch tabs, return, swipe) and are nearly impossible to reproduce in testing. Three rules kept our lifecycle clean. **Don't force-resume on foreground.** When the app returns from background, don't blindly call play(). Signal that the app is foregrounded and let the visibility-based logic decide what should play. This prevents the wrong video from resuming after a tab switch. **Clear stale state on background.** When pausing for background, clear any cached visibility tracking. Stale state is the root cause of most "wrong video plays" bugs. **Order matters.** Set the foreground flag to false before pausing videos. The reverse order creates a race condition where the visibility logic sees the app as still active and tries to resume playback during the pause sequence. ## Quality, Format, and the Infrastructure You'll Need Not all users have the same network conditions. Serving a single quality level means either buffering endlessly on slow connections or under-serving fast ones. In Divine, we measure actual throughput from video load times (file size divided by time to first frame) rather than running synthetic speed tests. A rolling average maps to quality tiers, and the tier drives which resolution the player requests. The hard part isn't the client logic. It's the infrastructure. Your server or CDN must transcode videos into multiple resolutions and serve them on demand. Without that pipeline, adaptive quality is a dead end on the client side. One format consideration worth calling out: for short content under 30 seconds, MP4 delivers faster first-frame latency than HLS. HLS's adaptive bitrate shines when you need resilience across varying conditions, and in a feed architecture with pooling and preloading, it provides better overall stability. The choice depends on your content length and architecture. ## Testing Without a Device Video playback code depends on native players with real GPU textures and platform-specific codecs. You can't run these in unit tests. But the logic around pooling, preloading, eviction, and lifecycle management is complex enough that shipping without tests is reckless. > The answer: mock the native player behind a controllable boundary. Create test doubles with StreamController instances to simulate buffering completion, playback state changes, and position updates. This lets you thoroughly test scenarios like LRU eviction order, audio leak prevention, and UI safety on eviction, all without touching native code. The boundary between "things that need a real player" and "things that need a state machine" is exactly where your abstraction layer should live. If you can't test a behavior without a physical device, you've drawn that boundary in the wrong place. ## The Pattern Behind the Patterns Step back from the individual solutions and a larger principle emerges. Every technique we've described (player reuse, fixed-size pooling, silent playback, synchronous caching, per-item state isolation, lifecycle management) follows from one insight: treat native resources as a shared, managed pool rather than disposable per-widget allocations. The player pool is the load-bearing abstraction. Get it right, and the rest of the architecture has room to breathe. Get it wrong, and you're fighting platform limits with every scroll event. Building Divine forced us to confront every one of these constraints in production. The patterns that emerged aren't theoretical. They're the result of iterating against real devices, real network conditions, and real users swiping through hundreds of videos a day. If you're building a video-heavy Flutter app and the experience feels brittle, start here: stop thinking about players as widgets, and start thinking about them as a managed resource. The Flutter ecosystem still has a gap where a truly reusable, well-abstracted player package should be. Until that gap closes, these patterns are how you bridge it. ### 8 Years of Flutter Agency Firsts: The VGV Leadership Story Source: https://verygood.ventures/blog/flutter-agency-8-year-leadership-journey/ On April 1, 2018, we started something new — Very Good Ventures was officially incorporated to continue developing [the mobile app for the Broadway musical Hamilton](https://verygood.ventures/success-stories/hamilton/) and pursue new opportunities for to make a difference with emerging technology. Back then, cross-platform development was still synonymous with compromise. Nobody was talking about AI. And Google had yet to ship a stable release of Flutter. But *we* had shipped something stable — an app that set the standard for Broadway and grew to millions of installs. We bet on Flutter because our developers preferred it to the alternatives. To celebrate our birthday, I'm documenting our story focusing on [Flutter](https://flutter.dev/) community impact — how a three-person agency grew alongside a remarkable community into something we're proud we built together. ## Before VGV: The Hamilton App That Started Everything The story actually starts before VGV existed. In 2017, at our previous agency, Google approached us about a new way to build mobile apps — they called it "Flutter." At the time, we were exploring the opportunity to build the official [Hamilton: The Musical app](https://hamiltonmusical.com/us-tour/lottery/#app). The stars aligned and Hamilton decided to follow our recommendation to use Flutter so we could deliver a high-quality app to all users on a single codebase. Flutter was pre-alpha at the time and untested at commercial scale — a significant technical risk by any measure. But we did it anyway, built it in three months, collaborated closely with Google, and [watched it take off](https://mashable.com/article/official-hamilton-app) — millions of installs, hundreds of thousands of monthly active users. It became the [first large-scale commercial Flutter app built outside of Google](https://verygood.ventures/success-stories/hamilton/), and Hamilton remains a VGV client to this day. Google featured the Hamilton app on the [Google Developer Blog](https://developers.googleblog.com/en/hamilton-app-takes-the-stage/) and cited it across [multiple Flutter announcements](https://developers.googleblog.com/ready-for-production-apps-flutter-beta-3/) as proof that Flutter was ready for production. The HamApp changed the trajectory of VGV, Flutter, and the community that has grown around this technology. From there, showing the world what was possible became our defining work. ![Hamilton musical app built with Flutter — VGV's first commercial Flutter app before the framework reached 1.0](/assets/images/blog/flutter-agency-8-year-leadership-journey/hamilton-musical-flutter-app.webp) ## 2018–2019: Founding a Flutter Company, Built on Flutter Firsts At the start VGV was only three people: David DeRemer, Al Lardizabal, and Kevin Gray ([RIP, we miss you](https://verygood.ventures/blog/remembering-our-friend-and-teammate-kevin-gray/)). We enjoyed working with Flutter, and we had built something special. We got to know the Flutter team at Google through our Hamilton work, and we started pushing the framework to do more to accomplish our needs. This kicked off a collaboration with Google and the community that continues to this day: build things nobody had built before and share tools, content, and open-source software that would help show the way to other teams. At **Flutter Live 2018** in London, Google asked us to build [Flutter Slides](https://verygood.ventures/success-stories/flutter-slides/) — a full presentation tool running natively on macOS. It was the first public demo of a Flutter desktop application running on macOS. The reveal was masterful showmanship. At a pivotal moment, Tim Sneath minimized the window the keynote was running in and showed the audience that the presentation they'd been watching (on an IMAX screen no less) was actually a live Flutter app running on macOS the entire time. The energy in the room was electric. I'll never forget the gasps and applause. ![Flutter Slides presentation app running natively on macOS — first public Flutter desktop demo at Flutter Live 2018](/assets/images/blog/flutter-agency-8-year-leadership-journey/flutter-slides-macos-desktop-app.webp) At **Google I/O 2019**, we partnered with The New York Times and Google to build [KENKEN](https://verygood.ventures/success-stories/new-york-times/) — the first significant public demo of Flutter as a web application. Martin Rybak, one of our engineering leaders, [built the first prototype in 48 hours](https://medium.com/flutter-nyc/under-the-hood-with-flutter-for-web-bc0d5ce1c11e) to prove it was even possible (it took a lot longer than that to polish it!). KENKEN was showcased on the Google I/O Developer Keynote stage, showing Flutter's ability to deliver on six platforms from a single codebase. ![KENKEN puzzle game built with Flutter for web in partnership with The New York Times — first major Flutter web demo at Google I/O 2019](/assets/images/blog/flutter-agency-8-year-leadership-journey/kenken-nyt-flutter-web-app.webp) By the end of 2019, we'd accumulated a series of Flutter firsts: first commercial app, first major desktop app, first commercial web app. But what mattered more than the firsts themselves was the pattern we'd established — when the Flutter ecosystem had a need that pushed the framework's boundaries, we leaned in to help solve it. That pattern would define our work for the years ahead. ## 2020–2021: Building the Foundation for Scale The pandemic hit. Hamilton shows paused and nobody knew what the future would bring. But we kept building. We were joined by Jorge Coca and Felix Angelov, who joined our team from BMW and started a new era. We developed [Very Good Start](https://www.youtube.com/watch?v=JVYXZ0f8-ac) — our proprietary Flutter starter kit — and held our first Open Source Week. We helped organize [Hack20](https://verygood.ventures/blog/hack20-the-worlds-largest-online-flutter-hackathon-goes-virtual/), the world's largest online Flutter hackathon, which drew 2,400+ participants who created 250 Flutter projects in a single event. We helped Google expand the community by moderating Discord groups supporting [free Flutter training](https://medium.com/flutter/learn-flutter-for-free-c9bc3b898c4d) that Google gifted to the world. In February 2021, we launched [**Very Good CLI**](https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/) — a command-line tool providing opinionated, production-ready project scaffolding from the first commit: multi-platform support, build flavors, internationalization, 100% test coverage, and CI/CD configured by default. It grew from a convenience into something much bigger — [Flutter's official architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations) now feature it as a recommended template. ![I/O Photo Booth virtual photo booth web app demonstrating Flutter camera capabilities at Google I/O 2021](/assets/images/blog/flutter-agency-8-year-leadership-journey/google-io-2021-photo-booth-flutter.webp) Also in 2021, we built the [**I/O Photo Booth**](https://verygood.ventures/success-stories/i-o-photo-booth/) for Google I/O — a virtual photo booth web app demonstrating Flutter's real-time camera capabilities and Firebase integration ([Flutter blog](https://medium.com/flutter/how-its-made-i-o-photo-booth-3b8355d35883) · [open source](https://github.com/flutter/photobooth)). We also completely rebuilt the Hamilton app to leverage the ecosystem's progress. When we started, there was no flutter_bloc, no official Firebase SDK, or any VGV tools. Our engineers also contributed directly to the framework itself — Google [credited VGV in the Flutter 2.5 release notes](https://medium.com/flutter/whats-new-in-flutter-2-5-6f080c3f3dc) for building camera_web support, one of many upstream contributions over the years. Our enterprise relationships expanded rapidly, demonstrating Flutter's capacity to serve organizations managing millions of customers. The team grew from roughly ten people to a real engineering organization. A flywheel was taking shape: help enterprise Flutter adopters prove the framework at scale, build showcases with Google that demonstrated Flutter's potential, and share what we learned with the community. Repeat. ## 2022: Series A and a Big Year for Flutter App Development 2022 was a major growth year. We [raised a Series A](https://verygood.ventures/blog/series-a/) from [Celesta Capital](https://www.celesta.vc/insights/very-good-ventures-why-we-invested) ([PR Newswire](https://www.prnewswire.com/news-releases/very-good-ventures-raises-3m-series-a-funding-from-celesta-capital-to-further-scale-rapidly-growing-flutter-practice-301548658.html)) to build on strong growth over the prior years. We brought on our first executive hires. And we delivered two new projects that expanded what people thought Flutter could do. ![I/O Pinball browser-based game built with Flutter and the Flame engine for Google I/O 2022](/assets/images/blog/flutter-agency-8-year-leadership-journey/google-io-2022-pinball-flutter-flame.webp) [**I/O Pinball**](https://verygood.ventures/success-stories/i-o-pinball/) for Google I/O 2022 was a browser-based pinball game built with the [Flame](https://flame-engine.org/) engine, pushing Flutter into game development territory ([Flutter blog](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d) · [open source](https://github.com/flutter/pinball)). It directly contributed to Google's announcement of the [Flutter Casual Games Toolkit](https://medium.com/flutter/announcing-the-flutter-casual-games-toolkit-c22e401d8fee) — and Flutter's [official Games Toolkit documentation](https://docs.flutter.dev/resources/games-toolkit) credits VGV for the project. [I/O Pinball](https://pinball.flutter.dev/) became a major success for Google's marketing team. [**Dart Frog**](https://verygood.ventures/blog/dart-frog/) came from the desire to extend Dart's elegance to the backend. Our team built an [open-source backend framework](https://github.com/dart-frog-dev/dart_frog) that would go on to power Google I/O showcase apps and reach 2,200+ GitHub stars before we transitioned it to community governance in 2025. With our clients, we helped prove Flutter's value across [financial services](https://verygood.ventures/success-stories/betterment/), [media](https://verygood.ventures/success-stories/marketwatch-stock-market-game/), [automotive](https://flutter.dev/showcase/toyota), and [consumer](https://verygood.ventures/success-stories/slickdeals-flutter/) industries — cutting development timelines in half, enabling single-codebase strategies, and training in-house teams on Flutter best practices. The question shifted from "can Flutter work for enterprise?" to "what's the best way to accelerate [Flutter's business value](https://verygood.ventures/whitepapers/business-value-of-flutter/)?" ## 2023: Acquisition, AI, and Global Scale 2023 was a year we bet big on Flutter. We [acquired CreateThrive](https://verygood.ventures/blog/vgv-acquires-createthrive/) — Latin America's leading Flutter development firm ([PR Newswire](https://www.prnewswire.com/news-releases/very-good-ventures-acquisition-of-createthrive-underscores-flutter-momentum-and-enterprise-adoption-301865279.html)) — bringing our team to 120+ engineers across the US, Latin America, and Europe. Tomas Piaggio and CreateThrive had been our partners for years, and making it official was one of the best decisions we've made. For the community and in partnership with Google, we built three major projects in a single year. ![Holobooth ML-powered experience integrating MediaPipe and TensorFlow.js at Flutter Forward 2023](/assets/images/blog/flutter-agency-8-year-leadership-journey/flutter-forward-holobooth-ml.webp) The [**Holobooth**](https://verygood.ventures/success-stories/flutter-forward-holobooth/) for the Flutter Forward event integrated machine learning with [MediaPipe](https://ai.google.dev/edge/mediapipe/framework) and [TensorFlow.js](https://www.tensorflow.org/js) — our first project incorporating ML into a Flutter app ([Flutter blog](https://medium.com/flutter/how-its-made-holobooth-6473f3d018dd) · [open source](https://github.com/flutter/holobooth)). ![I/O FLIP AI-powered card game using Google PaLM API played on the main stage at Google I/O 2023](/assets/images/blog/flutter-agency-8-year-leadership-journey/google-io-2023-flip-ai-card-game.webp) [**I/O FLIP**](https://verygood.ventures/blog/how-its-made-i-o-flip/), an AI-powered card game using Google's PaLM API and a Dart Frog backend, was played between keynotes on the main stage at Google I/O ([Flutter blog](https://medium.com/flutter/how-its-made-i-o-flip-da9d8184ef57) · [Google Developer Blog](https://developers.googleblog.com/how-its-made-io-flip-adds-a-twist-to-a-classic-card-game-with-generative-ai/) · [open source](https://github.com/flutter/io_flip)). Physical I/O FLIP cards were in the attendee swag bags. ![Ask Dash generative AI demo built with Flutter at the Google Cloud Applied AI Summit](/assets/images/blog/flutter-agency-8-year-leadership-journey/ask-dash-generative-ai-flutter.webp) [**Ask Dash**](https://verygood.ventures/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/) demonstrated Flutter as a front-end for GenAI at the Google Cloud Applied AI Summit ([Flutter blog](https://medium.com/flutter/how-we-built-it-ask-dash-a-generative-ai-flutter-application-79a836ced058)). ![Flutter News Toolkit open-source template built with Google to help news publishers launch mobile apps 80% faster](/assets/images/blog/flutter-agency-8-year-leadership-journey/flutter-news-toolkit-google.webp) Also at Flutter Forward, Google launched the [**Flutter News Toolkit**](https://verygood.ventures/success-stories/flutter-news-toolkit/) — an open-source template we co-built with Google as part of the [Google News Initiative](https://newsinitiative.withgoogle.com/resources/trainings/build-news-apps-flutter/) that helps news publishers launch mobile apps [80% faster](https://medium.com/flutter/announcing-the-flutter-news-toolkit-180a0d32c012). It's now referenced on [Flutter's official documentation](https://docs.flutter.dev/resources/news-toolkit). We released [**Dart Frog 1.0**](https://verygood.ventures/blog/dart-frog-1-0-release/), sent six speakers to Fluttercon Berlin (where we also delivered a keynote), and published hundreds of releases across 20+ open-source libraries. By the end of 2023, we'd built real momentum — a large and experienced Flutter engineering team, deep Google collaboration, a growing open-source portfolio, and enterprise work spanning every major industry vertical. ## 2024: Gemini, Engineering Practices, and Continued Partnership In 2024, we continued driving Flutter into the enterprise environment — expanding into aviation, theme parks, cruise lines, [luxury travel](https://verygood.ventures/success-stories/blade-braze-optimization-roadmap/), and food and beverage. Flutter's versatility was proving its impact well beyond its mobile origins, and we were helping enterprises discover new ways to succeed. ![I/O Crossword multiplayer puzzle powered by Gemini AI and compiled to WebAssembly for Google I/O 2024](/assets/images/blog/flutter-agency-8-year-leadership-journey/google-io-2024-crossword-gemini-flutter.webp) With AI entering the conversation, the [**I/O Crossword**](https://verygood.ventures/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/) for Google I/O 2024 represented a new level of ambition: a multiplayer crossword puzzle powered by Google's [Gemini AI](https://deepmind.google/technologies/gemini/), compiled to [WebAssembly](https://webassembly.org/) — a first for any VGV Google I/O project — backed by a Dart Frog server on [Cloud Run](https://cloud.google.com/run). It was hosted on [crossword.withgoogle.com](https://crossword.withgoogle.com/) and open-sourced for the community ([Flutter blog](https://medium.com/flutter/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai-2210e39b04b9) · [Google Developer Blog](https://developers.googleblog.com/en/how-we-built-the-io-crossword/) · [Firebase blog](https://firebase.blog/posts/2024/06/ai-powered-crossword-genkit/) · [blog.google](https://blog.google/technology/ai/google-io-crossword-puzzle-gemini-ai/) · [open source](https://github.com/VGVentures/io_crossword)). In September, we launched [**Very Good Engineering**](https://engineering.verygood.ventures/) — a dedicated site where we consolidated years of accumulated best practices into a public resource for the Flutter community. Architecture patterns, testing philosophy, conventions, documentation standards — all in one place. [Flutter's official architecture documentation](https://docs.flutter.dev/app-architecture/recommendations) integrates Very Good Engineering as a reference resource. But we didn't build it for recognition—we built it so other teams could benefit from what helped us ship well. The Flutter team [named VGV as a partner](https://medium.com/flutter/flutter-and-dart-at-google-cloud-next-b4921946d0d1) alongside Google Cloud and Firebase at Google Cloud Next 2024, where we sponsored and delivered sessions on building AI-powered apps with Flutter and Dart. At [Fluttercon USA 2024](https://verygood.ventures/blog/very-good-keynotes-sessions-at-fluttercon-usa-2024/), we delivered multiple talks, including a keynote, on performance tuning, Flutter WebAssembly, Dart Frog, and growing Flutter's commercial impact. We also co-presented a [Flutter migration case study](https://www.youtube.com/watch?v=pvpgl3mr6lM) on stage with a major fintech firm's VP of Architecture — the kind of thing that only happens when enterprise and the community share genuine trust. ## 2025: Community Stewardship and Industry Leadership 2025 was the year our work became visible far beyond the Flutter developer community. We announced a [strategic partnership with Trackhouse Racing](https://verygood.ventures/news/trackhouse-racing-partners-with-very-good-ventures-to-drive-digital-experience-innovation-in-motorsports/), building immersive multi-screen VIP race experiences for NASCAR — real-time telemetry, in-car video, live race tracking, and a localized edge/cloud server architecture designed for race-day network conditions where you can't rely on connectivity. ![VGV-sponsored No. 99 Trackhouse Racing Chevrolet at Pocono Raceway — first Flutter logo on a NASCAR race car](/assets/images/blog/flutter-agency-8-year-leadership-journey/vgv-trackhouse-nascar-flutter-logo.webp) *Photo courtesy of Daylon Barr Photography* And then… we put VGV and the Flutter logo on a race car! In June, we became [primary sponsor of Daniel Suárez's No. 99 Chevrolet](https://verygood.ventures/news/vgv-will-sponsor-trackhouse-no-99-chevrolet-at-pocono-raceway/) at Pocono Raceway ([Jayski](https://www.jayski.com/2025/06/02/very-good-ventures-sponsoring-daniel-suarez-at-pocono/)). We designed the full paint scheme ourselves, complete with Easter eggs built for Flutter developers: Flutter logos on the bumper and fenders, "Hot Reload!" next to the gas tank, and "Get Faster" above the jack points. It was the first time the Flutter logo appeared on a race car and was seen by race fans on national TV broadcasts and Amazon Prime. We [transitioned Dart Frog to community governance](https://verygood.ventures/blog/dart-frog-has-found-a-new-pond/) and [took over maintenance of the Flutter News Toolkit](https://verygood.ventures/blog/the-flutter-news-toolkit-finds-a-new-home-at-very-good-ventures/) from Google — two moves that reflect something we believe deeply: our role isn't just to build ecosystem infrastructure, it's to make sure the community has what it needs to carry things forward. Sometimes that means putting a project in the hands of the maintainers most invested in its future, and sometimes it means stepping up when Google needs a trusted steward. ## The Open-Source Tools That Shaped Flutter Development Throughout these years, we've also built a portfolio of open-source tools that became foundational to the entire Flutter ecosystem. We've always believed VGV's impact extends beyond the apps we ship—it's measured by the opportunities we create for others. [**Very Good CLI**](https://cli.vgv.dev) ([GitHub](https://github.com/VeryGoodOpenSource/very_good_cli) · [pub.dev](https://pub.dev/packages/very_good_cli)) enhances Flutter's default project scaffolding with opinionated, production-ready templates — multi-platform support, build flavors, internationalization, 100% test coverage, and CI/CD from the first commit. In February 2026, [we shipped Very Good CLI 1.0](https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/) — formally committing to a stable API surface, backward compatibility, and semantic versioning. The 1.0 release also introduced fail-fast testing, a built-in MCP server for AI-assisted workflows, and Windows CI support. Flutter's [official architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations) explicitly name it as a recommended application template, which speaks to the community's trust in the tool more than anything we could say about it ourselves. [**Very Good Analysis**](https://pub.dev/packages/very_good_analysis) enforces 188 lint rules — 86% of all available Dart lint rules — far exceeding Flutter's default linting package. It's become a strict-linting standard trusted by countless professional Flutter teams. **Very Good Workflows** provides reusable CI/CD pipelines tested across hundreds of projects, and **Very Good Coverage** enforces code coverage thresholds in CI — driving standards deeper — the thing that separates "we follow linting rules" from "we actually catch bugs before they ship." (Very Good Coverage was [recently deprecated](https://www.linkedin.com/posts/marcossevilla_weve-deprecated-very-good-coverage-ugcPost-7445054833151225856-9VIY) in favor of Very Good Workflows, which now handles coverage enforcement natively.) Over the years, we championed [BLoC](https://bloclibrary.dev/) as the enterprise standard for state management, adopting [flutter_bloc](https://verygood.ventures/blog/why-we-use-flutter-bloc/) across every client project and every Google showcase app we built. Felix Angelov [created it](https://medium.com/flutter-community/bloc-from-first-commit-to-flutter-favorite-bffd1fa64891), and while he was at VGV it got an enterprise proving ground to mature. The library now holds [Flutter Favorite](https://pub.dev/packages/flutter_bloc) status with [12,400+ GitHub stars](https://github.com/felangel/bloc) and 200,000+ dependent repositories. ![Very Good AI Flutter Plugin — open-source tool integrating Flutter best practices into AI-assisted development workflows](/assets/images/blog/flutter-agency-8-year-leadership-journey/very-good-ai-flutter-plugin.webp) Most recently, we released the [**VGV AI Flutter Plugin**](https://verygood.ventures/blog/very-good-ai-flutter-plugin/) — our first open-source venture into AI-assisted engineering tooling. The plugin integrates VGV's accumulated Flutter and Dart best practices directly into AI development tools, giving engineering teams contextual guidance on accessibility, performance, testing, and architecture without leaving their development environment. It's another Flutter first for VGV: the first [Claude Code](https://claude.com/product/claude-code) plugin purpose-built for Flutter development teams. The plugin represents the next evolution in how we help the community build—shifting from frameworks we ship to tools that amplify every engineer's impact. Today, a Flutter developer can scaffold a project, manage state, lint code, write tests, validate forms, enforce coverage, run CI, build a backend, and use AI — all using [VGV-created](https://github.com/VeryGoodOpenSource) or [VGV-associated](https://github.com/VGVentures) tooling. Valuable contributions that have accumulated over years of solving real problems and sharing the solutions. ## Engineering Practices Built for the Community We didn't just build and release tools, we also shared how we work — and it has made all the difference. Our four-layer pattern — Data, Domain/Repository, Business Logic, and Presentation — enforces strict separation of concerns. Flutter's [official architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations) reference "Very Good Engineering architecture documentation" as a recommended resource. Third-party developers mention "the VGV layered architecture" by name, and Google's own team [cited VGV projects as architectural references](https://developers.googleblog.com/en/how-we-built-purrfect-code/) for best practices when building new Flutter apps. That recognition belongs to the many engineers who refined these patterns across hundreds of projects. The practices we developed — 100% test coverage as discipline, build flavors as default, strict linting from day one — we were careful to share openly in [our Flutter engineering documentation](https://engineering.verygood.ventures/). The goal was never to dictate how Flutter teams should work; it was to offer a well-tested starting point. The fact that so many teams have adopted and adapted these practices speaks to the community's hunger for practical, battle-tested guidance.

Engineering Docs

What eight years of Flutter work settled on

The reasoning behind each rule, and the trade-offs we weighed before it became a default.

See the architecture we standardized on

## The Impact Eight years in, it's hard to believe it's been that long, yet also exciting how much we've accomplished. Built the **first commercial Flutter app** before Flutter reached 1.0. Built **Google's Flutter showcases for six consecutive years**. Created **open-source tools that became ecosystem standards**. Produced **at least ten Google Developer Experts**. Delivered **keynotes at every major Fluttercon**. Published **[eight articles on the official Flutter blog](https://medium.com/@vgv_team)** with our work referenced and mentioned across sixteen total articles. Referenced in **[Flutter's official documentation](https://docs.flutter.dev/app-architecture/recommendations)**. Along the way, we grew from three people to **125+ engineers** across several continents. We helped drive enterprise Flutter app development adoption across [financial services](https://verygood.ventures/industries/financial-services/), automotive, [travel](https://verygood.ventures/industries/travel-hospitality/), [media and entertainment](https://verygood.ventures/industries/entertainment/), and consumer industries. We didn't do any of this in a vacuum. Flutter's [community](https://flutter.dev/community) is one of the most generous and collaborative in software development, and every milestone on this list was shaped by that community's energy and feedback. We helped nurture the commercial and engineering ecosystem, and it nurtured us right back. ## It's All About The Team ![Very Good Ventures Flutter development team — 125+ engineers building enterprise Flutter applications](/assets/images/blog/flutter-agency-8-year-leadership-journey/vgv-flutter-development-team.webp) Every success on this list starts and ends with one thing — the team. We've had a wild ride of ups and downs, and we've accomplished a great deal. But what we're most proud of is the positive impact we've had on the community we helped build and on each other. Everything at VGV is entirely because of the individuals we've been fortunate enough to call teammates — passionate, smart, experienced, and generous people. Our team wants to push the boundaries of technology as much as they want to help their teammates and others in the community. Over the years, the VGV team just kept showing up, doing the work, and trying to make everything around us a little better than we found it. That's the VGV magic. We didn't set out to win or build a record. We set out to do very good work, and the record built itself. Happy birthday, VGV. Thank you to every unicorn, past and present, who made this possible. Thank you for making a difference and leaving the campground better than you found it. Here's to the next eight years. Enjoy the ride. --- *Very Good Ventures is a leading Flutter development company and [the top Flutter development company recommended on flutter.dev](https://flutter.dev/consultants). We partner with organizations to build, scale, and ship Flutter applications — backed by eight years of ecosystem leadership, enterprise delivery, and open-source contribution. [Learn more about hiring Flutter expertise from VGV →](https://verygood.ventures/contact-us/)* ### Your Business Doesn't Fit on a Phone - Neither Should Your Flutter App Source: https://verygood.ventures/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/ ![Multi-device Flutter coffee shop scenario — smartphone, menu board, and POS-style tablet sharing consistent blue-accent UI — Flutter multi-device architecture blog post](/assets/images/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/hero.avif) A modern coffee shop runs on many screens. On the guest side: a mobile app, a website, a self-order kiosk, drive-thru and in-store menu boards, and an order-ready display are all common these days. Behind the counter: a point-of-sale system and a kitchen display. Each application has its own platform expectations, hardware, and operating systems. Most businesses build one or two of these, then buy off-the-shelf products for the rest — separate teams, separate codebases, separate release cycles. It seems "good enough", but often the data lives in multiple systems, they don't look exactly the same, there are actions you can complete on some devices but not others, and the whole experience is "just fine". What if there were another way? I address this head on in my recent talk "Your Business Doesn't Fit on a Phone — Neither Should Your Flutter App". Here's the twist: this talk isn't really about the architecture. It's about a question I keep coming back to: **when technology makes you more efficient, what do you do with that efficiency?** Check out the full talk here: {% video url="https://www.youtube.com/watch?v=-S4KV1TjUG4" title="Your Business Doesn't Fit on a Phone — Neither Should Your Flutter App" %} ## The Coffee Shop That Started It All I've [written before](https://verygood.ventures/blog/my-journey-from-barista-to-google-developer-expert/) about my path from college barista to Google Developer Expert for Flutter and Dart — building a coffee shop app, becoming Director of Technology, eventually joining [Very Good Ventures](https://verygood.ventures) and working on mobile ordering for brands like Dutch Bros Coffee. What I saw over all those years was the same pattern: coffee shops kept adding more tech, and that tech made operations more efficient. Drinks got made faster. Lines moved quicker. Fewer mistakes. But here's the thing about efficiency — it's a gift, and you have to decide what to do with it. You can pocket the savings. Staff fewer people. Run leaner. Or you can reinvest it. Give your baristas more time to connect with customers. Keep the store cleaner. Make better drinks. The technology is neutral. The choice is yours. ## Five Apps, One Codebase For the talk, I built a project called [Very Yummy Coffee](https://github.com/VGVentures/very_yummy_coffee) — a full coffee shop tech stack designed to show what Flutter can do when you think beyond the phone. The monorepo has five apps: - **Mobile App** — customers browse the menu, customize drinks with modifiers, and place orders from their phone - **Kiosk App** — an in-store touchscreen with a horizontal category row and item grid for walk-up ordering - **POS App** — the cashier's iPad for taking orders and managing item availability - **KDS (Kitchen Display System)** — a tablet behind the counter with a three-column Kanban view: New, In Progress, Ready - **Menu Board App** — a read-only display on a big screen with a three-panel layout for featured items and categories Every app imports the same shared models, the same repositories, and the same UI components. Eight shared packages sit underneath those five apps, covering everything from the API client and connection management to shared menu features and a common UI library. Teams that ship multi-package Flutter repos often wire this up with [configurable CI workflows](https://verygood.ventures/blog/configuring-workflows-for-your-flutter-projects/) so each package stays testable and releasable. For day-to-day testing across nested packages, [Very Good CLI's recursive test flow](https://verygood.ventures/blog/flutter-tests-very-good-cli/) is built for exactly this shape of repo. These apps run on iOS, macOS, Android, and even Linux. Built traditionally, this would be multiple product teams with separate skills trying to make products that work together and look the same. With Flutter? Across the five apps, about 21% of all app code is shared. And that's before I've extracted the cart and checkout flows into their own packages. ![Five Flutter apps—mobile, kiosk, POS, kitchen display, menu board—feeding shared packages — Very Yummy Coffee monorepo](/assets/images/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/5-apps.avif) ## Every Device Just Knows The real magic of this setup is that every device is always aware of what's happening on every other device — and nobody had to build that per-app. A [Dart Frog](https://dartfrog.vgv.dev/) server holds state in memory and broadcasts updates over WebSocket topics like `menu`, `orders`, and `order:`. Each app opens a single WebSocket connection through a shared `WsRpcClient` that lives in the `api_client` package. That client multiplexes all of an app's subscriptions over one connection — the first subscriber to a topic opens it, the last one closes it, and everything auto-resubscribes on reconnect. _From [`shared/api_client/lib/src/ws_rpc_client.dart`](https://github.com/VGVentures/very_yummy_coffee/blob/main/shared/api_client/lib/src/ws_rpc_client.dart) in Very Yummy Coffee:_ ```dart /// Returns a broadcast stream of update payloads for [topic]. If already /// subscribed, returns the existing stream; otherwise opens the topic on the wire. Stream> subscribe(String topic) { if (_controllers.containsKey(topic)) { return _controllers[topic]!.stream; } _controllers[topic] = StreamController>.broadcast(); _ensureListening(); _connection.send(RpcSubscribeMessage(topic: topic).toMap()); return _controllers[topic]!.stream; } void sendAction(RpcAction action) { _connection.send( RpcActionClientMessage( action: action.actionName, payload: action.toPayloadMap(), ).toMap(), ); } ``` When apps need to mutate state, they send strongly typed actions. A sealed `RpcAction` class hierarchy defines every possible mutation: `CreateOrderAction`, `MarkOrderReadyAction`, `UpdateMenuItemAvailabilityAction`, and so on. Each action knows how to serialize itself, and the server knows how to deserialize it back into the same Dart class. There's JSON under the hood, but you never have to think about it — the same Dart classes are being converted on both sides of the wire. _From [`shared/very_yummy_coffee_models/lib/src/rpc/rpc_action.dart`](https://github.com/VGVentures/very_yummy_coffee/blob/main/shared/very_yummy_coffee_models/lib/src/rpc/rpc_action.dart):_ ```dart sealed class RpcAction { const RpcAction(); String get actionName; Map toPayloadMap(); } class UpdateMenuItemAvailabilityAction extends RpcAction { const UpdateMenuItemAvailabilityAction({ required this.itemId, required this.available, }); final String itemId; final bool available; @override String get actionName => 'updateMenuItemAvailability'; @override Map toPayloadMap() => { 'itemId': itemId, 'available': available, }; } ``` When the cashier toggled Pumpkin Spice Latte to unavailable during the demo, the POS sent an `UpdateMenuItemAvailabilityAction` over WebSocket. The server flipped a boolean and broadcast the updated menu to every subscriber. That's it. Every screen updated because every screen was already listening. ![Diagram of multiple devices receiving updates from one connection — WebSocket and Dart Frog — Flutter real-time retail apps](/assets/images/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/real-time-sync.avif) Even the "what happens when the connection drops?" experience is shared. An `AppBloc` in the `app_shell` package listens to the connection state from a shared `ConnectionRepository`. When the WebSocket disconnects, every app shows the same `ConnectingView` — a spinner with a message — and automatically redirects back when the connection recovers. Five apps, one connection experience. Nobody had to build or maintain that logic five times. ## One Brand, Every Screen When your business runs on multiple devices, visual consistency matters. A customer who orders on the mobile app and picks up at a kiosk should feel like they're interacting with the same brand. When those devices are built by separate teams on separate tech stacks, that's aspirational at best. When they all share a single UI package, it's automatic. The `very_yummy_coffee_ui` package defines the entire design system in one place. A `CoffeeTheme` sets up colors, typography, and component styles that every app imports. `AppColors` is a `ThemeExtension` with around 28 color tokens — primary, secondary, status variants, navigation accents — accessible anywhere via `context.colors`. `AppTypography` defines a type scale using IBM Plex Sans, from page titles down to captions, accessible via `context.typography`. On top of those tokens sit shared components. `OrderCard` renders an order's number, customer name, line item summaries, total, and status pill. `StatusBadge` is a simple pill with a label and color. `ModifierGroupSelector` handles single- and multi-select modifier choices with price deltas and a "(required)" badge when applicable. These components are intentionally domain-agnostic — they accept primitive parameters and callbacks, not domain models — so they slot into any app's layout without coupling. And because this is Flutter, those components render pixel-identically across iOS, macOS, Android, and Linux. The same `OrderCard` on the barista's KDS tablet looks exactly like the one on the cashier's iPad POS. No cross-platform rendering quirks, no "close enough." One definition, one result, everywhere. ![Matching order card UI on POS and kitchen tablets — shared Flutter design system and ThemeExtension — multi-device brand consistency](/assets/images/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app-recap/matching-order-card.avif) ## Shared Features: Icing on the Cake Sharing models, networking, and UI components is already a big win. But the real icing on the cake is sharing entire features — business logic and all. Shared menu code lives in packages like `menu_repository`, while each app wires up Blocs that fit its UI. `MenuGroupsBloc` streams menu groups for category navigation; `MenuItemsBloc` loads the items for a selected group. The content widgets that display that data include `MenuGroupList` for vertical layouts and `MenuGroupRow` for horizontal ones. The mobile app uses `MenuGroupList` — a vertical scrollable list of menu group cards, natural for a phone screen. The kiosk app uses `MenuGroupRow` — the same groups laid out horizontally, better suited for a large touchscreen. Different layouts, same Bloc underneath for a given concern. `MenuItemsBloc` doesn't know or care which widget is rendering its state. It combines menu group and item streams with `Rx.combineLatest2`, then emits the current group and items. _From [`applications/mobile_app/lib/menu_items/bloc/menu_items_bloc.dart`](https://github.com/VGVentures/very_yummy_coffee/blob/main/applications/mobile_app/lib/menu_items/bloc/menu_items_bloc.dart) (kiosk uses the same pattern):_ ```dart await emit.forEach( Rx.combineLatest2( _menuRepository.getMenuGroups(), _menuRepository.getMenuItems(_groupId), (groups, items) => (groups, items), ), onData: (data) { final (groups, menuItems) = data; final group = groups .where((MenuGroup g) => g.id == _groupId) .firstOrNull; return state.copyWith( status: MenuItemsStatus.success, group: group, menuItems: menuItems, ); }, onError: (_, _) => state.copyWith(status: MenuItemsStatus.failure), ); ``` This means when I add a new menu feature — say, filtering by dietary preference — I build it once in the shared repository or Bloc layer. Every app that composes the same `MenuRepository` and Blocs picks it up automatically. The mobile app gets it. The kiosk gets it. The POS gets it. No coordinating across teams, no "we'll port that feature next sprint." It's just there. ## It's the Same Story Flutter gave me efficiency by letting me share code across five apps spanning iOS, macOS, Android, and Linux. It handed me back time. And it left me with the same question: **what do you do with it?** I keep coming back to that fork in the road. You can pocket the savings — ship with a smaller team, cut costs, move on. Or you can reinvest — build something more ambitious, polish the experience, try the idea you didn't think you had time for. ## The Future Is Multi-Device At VGV, we've formalized this progression into what we call the [Multi-Device Maturity Model](https://verygood.ventures/resources/multi-device-maturity-model/). It's a framework for understanding where your business sits on the spectrum — from a single app on a single platform, all the way to a fully connected multi-device experience with shared data and consistent branding across every surface. Very Yummy Coffee is an exercise in pushing toward the far end of that spectrum. The lines between "this is an app" and "this is a window into your data" are razor thin right now. A customer starts an order on their phone, checks the status on a kiosk, and picks it up when the order-ready board says so. Each screen is just a different view into the same data model — the same orders, the same menu, the same inventory. This is where Flutter shines. Not just "write once, run on iOS and Android," but "build a cohesive experience across every screen your business touches." [Toyota is putting Flutter in car dashboards](https://flutter.dev/showcase/toyota). [Universal Destinations & Experiences](https://verygood.ventures/success-stories/amusement-park-flutter-apps/) extended mobile food and drink ordering into a Flutter-powered kiosk solution integrated with restaurant POS systems. [Companies across industries](https://verygood.ventures/blog/top-companies-using-flutter/) are discovering that a surface with a screen is a candidate, which is a different way of thinking about the scope of a "Flutter app." As Flutter makes it more practical to build for each of those surfaces, the question isn't whether your business will need more than a phone app. It's what you'll do when building for five screens and four operating systems takes the same effort that one screen and one operating system used to. That brings the story back to the opening: **when technology makes you more efficient, what do you do with that efficiency?** Everything above is one way to earn that efficiency—shared models, one multiplexed WebSocket client, shared UI, shared features—so every screen in the shop stays in lockstep. Here is the suggestion I give teams when we reach this point: treat the gains as **opportunity**, not only as a cost-saving. Where you have room, reinvest capacity into **innovation and craft**: flows that are more helpful, handoffs that stay seamless from phone to kiosk to pickup surface, and the small touches that make an experience feel delightful instead of merely functional. ### Building on Nostr: Real Engineering Challenges Source: https://verygood.ventures/blog/building-on-nostr-real-engineering-challenges/ > This is Part 2 of a two-part series. [Part 1](https://verygood.ventures/blog/what-is-nostr/) covers the protocol fundamentals — events, relays, NIPs, and the architecture that makes Nostr different. Nostr’s design is deceptively simple: one data type, dumb relays, smart clients, cryptographic identity. But that simplicity means every problem a traditional server would solve — authentication, data consistency, connection management, content moderation — now lands on the client. When our team joined the [diVine](https://divine.video/) project, we learned this quickly. The challenges we hit are not unique to video. They are structural, baked into what it means to build on a protocol where the server is deliberately dumb. ![Building Nostr Engineering Challenges Hero](/assets/images/blog/building-on-nostr/image5.avif) ## Managing WebSocket Connections Your client maintains simultaneous WebSocket connections to multiple independent relays — often five to fifteen at once. Because relays do not talk to each other and different users publish to different relays, connecting to multiple relays is not optional. **Connection lifecycle is your responsibility.** Each relay connection can drop independently. Your client needs reconnection logic with exponential backoff, health monitoring per connection, and graceful degradation. The practical approach is a dedicated connection manager layer between your app logic and raw WebSocket connections, owning lifecycle, subscription routing, and resource budgeting in one place. **Subscription management gets complex fast.** Loading a feed requires subscriptions across multiple relays for followed accounts, replies, reactions, and profile metadata — multiplied by every screen in your app. Without careful lifecycle management, relays will reject new subscriptions or drop your connection. Tying subscription cleanup to your navigation lifecycle helps. **Battery and bandwidth matter.** On mobile, dozens of concurrent WebSocket connections consume battery and data at measurable rates. Lazy connection is the most effective mitigation: only open a WebSocket when your app needs data from that relay, and close it when the relevant screen is no longer visible. **Local caching is load-bearing.** No server exists to go back to. Without local persistence, every app launch re-fetches the user’s entire social graph from scratch. Treat the local cache as your primary data source — a structured database with indexes on event kind, pubkey, and created_at. Serve the UI from cache on launch, then merge incoming relay data in the background using a stale-while-revalidate pattern. ![WebSocket Connection Management Complexity](/assets/images/blog/building-on-nostr/image4.avif) ## Relay Discovery and the Data Puzzle Managing connections is only half the problem. The harder question is: which relays should you connect to? Fetching a timeline on Nostr means retrieving the user’s follow list, finding each followed user’s relay preferences (kind 10002), connecting to those relays, subscribing, and merging results. This is what's called the [“outbox model”](https://github.com/nostr-protocol/nips/blob/master/65.md). The problem is that many users have never published relay preferences. When preferences are missing, your client must fall back to querying popular relays (slow, bandwidth-heavy) or showing nothing (worse). Most clients maintain default relays as a fallback, but the more you rely on defaults, the more your “decentralized” app resembles a centralized one. Prompting users to publish their own relay list early reduces this dependency over time. ![Relay Discovery Network Puzzle](/assets/images/blog/building-on-nostr/image3.avif) ### Relay Reliability Relays are independent operators with their own policies, retention periods, and rate limits. They can go offline without notice. Publish events to at least three relays for redundancy, confirm acceptance before showing the user a success state, and build relay health tracking (response times, error rates) into your connection manager so you can prioritize reliable relays and deprioritize those that consistently fail. ## Data Consistency Without a Server Without a single source of truth, your client may receive different data from different relays. One relay has a user’s updated profile; another serves the old version. One relay has all the replies in a thread; another has a subset. This is not a bug — it is the structural cost of decentralization. The local cache you built for performance now has a second job: reconciliation. Use created_at timestamps as the primary conflict resolution rule — when two relays return different versions of the same replaceable event, keep the later one. For threads and feeds, a time-bounded scatter-gather pattern works well: query all relevant relays in parallel, render the best available result after a short timeout, and continue merging late-arriving responses into the view. Design your cache to support upsert-on-newer logic natively and use subtle UI indicators to communicate ongoing sync without blocking the user. Your client is an eventually consistent system. If your background is mobile apps that consume REST APIs, this is a significant shift. ![Relay Data Consistency Reconciliation](/assets/images/blog/building-on-nostr/image2.avif) ## Key Management: Where Users Hit a Wall Nostr’s identity model is cryptographically elegant and practically painful. Every identity is a key pair — no email, no password reset, no support ticket. For engineers, the model is clean. For the users you are building for, it introduces friction at every stage. ![Key Management Onboarding Trade-off](/assets/images/blog/building-on-nostr/image6.avif) ### Onboarding Friction A new user’s first interaction: generate a cryptographic key pair, understand that their private key must be backed up, and store it securely. Compare that to “Sign in with Google.” Every client must decide how much to abstract. Auto-generate for fast onboarding and users may never back up their key; show the raw hex string and the funnel drops. One approach: auto-generate at first launch, then surface non-blocking backup prompts at natural pause points — after the first follow, the first post, a week of usage. These tradeoffs are invisible in the protocol spec but very real in the product budget. ### Cross-Platform Identity A user creates their identity on desktop and now wants to use your mobile app. The current options: copy-paste a hex string (insecure), scan a QR code (requires both devices in hand), or use an external signer app. Bunker-based remote signing, where a separate service holds the private key and signs events on the client’s behalf, remains the clearest path forward. Remote signing solutions are maturing but remain friction-heavy for mainstream users. ![Cross-Platform Identity Ecosystem Gap](/assets/images/blog/building-on-nostr/image1.avif) ## UX Patterns You Have to Invent Building on Nostr means designing interfaces for concepts that have no precedent in centralized apps. Users arrive expecting familiar patterns that do not exist — your UI must teach new ones. **Relay selection.** Most users should never think about relays. Pick sensible defaults on first launch and expose relay management as an advanced setting. That balance between abstraction and transparency is what every client solves differently. **Identity switching.** Some users operate multiple key pairs — a public persona, an anonymous account, a project identity. Switching means swapping the active key pair along with its cached state and relay configuration. Treating each identity as an isolated profile keeps the logic clean. **No delete button.** The protocol defines deletion request events (kind 5), but relays are not required to honor them. “Delete” means “ask relays to delete.” Label the action honestly to set the right expectation. **Content filtering without a server.** Every event arrives unfiltered. Your app decides what to show and what to hide. For consumer-facing apps, this means building a client-side moderation layer: keyword filters, mute lists, and community-curated block lists published as Nostr events. ## The Ecosystem Today Community-maintained libraries exist across most languages, and published packages cover the core functionality: key encoding, relay connections, event creation, and signing. The Dart/Flutter ecosystem is production-viable — we built diVine’s client layer on it. What you will not find yet is the depth of tooling around established platforms: standardized testing frameworks, comprehensive guides, or stable abstraction layers that insulate you from protocol changes. The protocol evolves fast. No stable LTS version exists — just community consensus about which NIPs are mature. Start with the core NIPs (NIP-01 for events, NIP-02 for follows, NIP-65 for relay lists) and add newer NIPs incrementally as your feature set demands. ![Nostr Developer Tooling Maturity Gap](/assets/images/blog/building-on-nostr/image7.avif) None of these challenges are unsolvable. Libraries exist, documentation is improving, and the community is active. But the protocol is young and evolving fast, which means the ground shifts under you — a NIP you build against today may change next month. Keeping pace with the protocol through resources like Nostrbook MCP can make a real difference (check out our [MCP blog](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/)). Budget for that pace, and the engineering is entirely manageable. ### Migrating VGV from Webflow to Astro: Building an AI-Native Website Source: https://verygood.ventures/blog/migrating-vgv-from-webflow-to-astro/ ![Illustration of the design feedback loop collapsing from days to minutes with AI](/assets/images/blog/migrating-vgv-from-webflow-to-astro/main.avif) We rebuilt [verygood.ventures](https://verygood.ventures/) from scratch. Not because our old site was broken, but because we needed a site that could move at the speed our team — and AI — now operates. For years, our website ran on [Webflow](https://webflow.com/). It looked great, but making updates was slow, required specialized Webflow knowledge, and locked our content behind a proprietary visual editor. Every change to a blog post or landing page required someone who knew the platform's quirks. For a company building cutting-edge software, our own website workflow felt stuck in the past. AI changed the calculus. When your development partner can write code, generate content, and ship changes from a single prompt, the last thing you want is a visual editor standing between your team and a published update. We needed full code custody — a codebase any engineer (or AI assistant) could modify directly. So we migrated to [Astro](https://astro.build/), a modern static site generator that outputs zero JavaScript by default, supports markdown-driven content, and deploys to a global edge network. The result: a faster site, a simpler workflow, and a foundation built for the AI-native era. This might be one of the fastest website migrations of this scale ever completed. The entire project — from first extraction to live site — took **less than two weeks**. The initial sprint from idea to a fully rebuilt site running on Cloudflare was roughly **5 days**. The remaining time was spent on design refinement, cross-browser QA, and launch readiness. For a 309-page site with 1,189 images and 12 third-party integrations, that pace would have been unthinkable without AI as a development partner. > Our clients rely on us to be leaders at the forefront of technology capabilities, and we have always focused on learning by adopting the latest tools and techniques ourselves first. This effort is one of many steps to make VGV more “AI Native” which enables us to operate more effectively while developing expertise we can share with our partners and community. > > **David DeRemer, CEO @ Very Good Ventures** ## The Game Plan: A 3-Phase Migration We structured the migration into three phases: 1. **Content extraction** — Pull every page, image, and metadata field out of Webflow and into structured markdown files. 2. **Site build** — Construct a production-ready Astro site matching the original design and SEO footprint. 3. **DNS cutover** — Switch traffic from Webflow to Cloudflare Pages with zero downtime and zero broken links. Each phase was designed to be independently verifiable: we could validate extraction completeness before building, and validate the build before going live. And throughout the entire process, [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) served as our development partner — writing extraction scripts, building components, and iterating on every detail. Claude also helped us plan and execute the Cloudflare deployment itself, providing detailed instructions for DNS configuration, Workers setup, caching rules, and the deployment pipeline — turning what could have been a nerve-wracking cutover into a well-documented, step-by-step process. ![Diagram showing the three migration phases: content extraction, site build, and DNS cutover](/assets/images/blog/migrating-vgv-from-webflow-to-astro/body-0.avif) ### Phase 1: Extracting Content from Webflow The first challenge was getting our content out. Webflow doesn't make bulk export easy, so we used a hybrid approach: automated scripts for CMS-managed collections and AI-assisted extraction for static pages. For the **204 blog posts**, 27 success stories, 26 podcast episodes, and other CMS collections, we wrote Node.js scripts using [Turndown](https://github.com/mixmark-io/turndown) and [Cheerio](https://cheerio.js.org/) to fetch each page's HTML and convert it to clean markdown with structured frontmatter. The Webflow API gave us access to CMS fields — titles, slugs, publish dates, tags, and author references — which we mapped directly into typed frontmatter schemas. For the **29 static pages** (homepage, services, industries, about, etc.), we developed a multi-pass extraction protocol: first capturing page structure and navigation, then body copy, then all internal and external links, and finally media assets. This systematic approach ensured nothing was lost in translation. Along the way, we downloaded **1,189 images** and mapped **291 URLs** from the original Webflow sitemap into a comprehensive manifest. ![Infographic showing extraction stats: 204 blog posts, 27 success stories, 1,189 images, 291 URLs mapped](/assets/images/blog/migrating-vgv-from-webflow-to-astro/body-1.avif) The extraction process also surfaced issues on the live Webflow site itself — broken links, missing metadata, content inconsistencies — that had gone unnoticed. Claude identified these on its own and fixed them as part of the migration, so the Astro site launched cleaner than the original. The gotchas were real: JavaScript-loaded media was invisible to our scrapers, some Webflow CDN assets returned 403 errors, and inline links occasionally got stripped during HTML-to-markdown conversion. Each issue required a targeted fix, but the systematic phase structure meant we caught problems early and verified everything before moving on. The initial extraction produced **301 typed markdown files** with complete frontmatter — the 291 URLs plus partials and reusable content sections that didn't have their own URLs. Critically, the extraction system we built wasn't throwaway tooling. As new blog posts and pages were published on the live Webflow site during the migration, we re-ran the same scripts to pull them in seamlessly. By launch, the site had grown to 309 pages — and adding each new one was trivial. ### Phase 2: Building the Astro Site With content in hand, we built the site in methodical batches, starting from the foundation and working up to full feature parity. **Tech stack:** [Astro 5](https://astro.build/), [Tailwind CSS v4](https://tailwindcss.com/) with semantic color tokens, [Cloudflare Pages](https://pages.cloudflare.com/) for hosting, and [bun](https://bun.sh/) as our JavaScript runtime. ![Tech stack diagram showing Astro, Tailwind CSS, Cloudflare Pages, and Bun](/assets/images/blog/migrating-vgv-from-webflow-to-astro/body-2.avif) **Tooling for accuracy:** A key challenge with AI-assisted development is ensuring the code targets the *latest* APIs, not patterns from stale training data. We solved this with [MCP](https://modelcontextprotocol.io/) servers and developer tooling: the astro-docs MCP server gave Claude real-time access to Astro's current APIs and configuration options, [Context7](https://context7.com/) provided up-to-date documentation for the full stack, and Anthropic's TypeScript LSP caught type errors as Claude wrote code. This combination meant Claude was always building against the latest features — not guessing based on older versions. **Content architecture:** We defined 7 content collections — blog, success stories, podcasts, whitepapers, webinars, pages, and partials — each with a [Zod](https://zod.dev/)-validated schema. This means every markdown file is type-checked at build time. A missing required field or an invalid date format fails the build immediately, not silently in production. **Component design:** We built 30+ reusable Astro components covering everything from hero sections and card grids to podcast players and HubSpot form embeds. For animations, we made an intentional choice: vanilla CSS and JavaScript instead of React islands. Our `FadeIn` and `StaggerChildren` animation components weigh roughly 300 bytes — compared to the ~45KB a React-based alternative would have added. **Integration wiring:** Claude detected the 12 third-party services embedded in our Webflow site automatically — Google Tag Manager, HubSpot (forms and tracking), Greenhouse (job listings), Iubenda (cookie consent), YouTube, and others. Rather than blindly porting old embed snippets, Claude reviewed each service's latest integration documentation and implemented them using current best practices. The result: integrations that weren't just migrated, but modernized. Custom remark plugins handle video embeds and audio players directly in markdown — authors write a simple syntax, and the build pipeline handles the rest. **Visual fidelity:** Throughout the build, we used Claude Code to compare the Astro output against the live Webflow site — catching visual discrepancies, missing content, and layout differences as we went. When something looked off, we could simply ask Claude to compare what it built with the original and fix the gap. This turned visual QA from a manual side-by-side exercise into a continuous, conversational process. **SEO preservation:** This was non-negotiable. Every page carries JSON-LD structured data (Organization, Article, BreadcrumbList schemas), canonical URLs, and verified meta descriptions. The auto-generated sitemap produces 307 URLs. We validated all of this programmatically before launch. The final build: **0 TypeScript errors**, **0 broken internal links**, and **309 pre-rendered pages** deployed to Cloudflare's global edge network. ### Phase 3: DNS Cutover The moment of truth. The time where all the traffic was routed to the new site. Moments like this tend to go horribly wrong... but not this time! In fact, since we launched the site already a few weeks ago, we've been focusing on further improvements and performance on our site and content creation, and never looked back. WebFlow is now legacy. Astro and Claude are VGV's present and future. ![Illustration comparing the old Webflow workflow with the new AI-native Astro workflow](/assets/images/blog/migrating-vgv-from-webflow-to-astro/body-3.avif) ## The AI-Native Workflow Here's the real shift. Our old Webflow workflow looked like this: 1. Write content in a Google Doc 2. Hand it to someone who knows Webflow 3. Wait for them to re-enter it, format it, and publish 4. Hope nothing breaks in the visual editor Our new workflow: 1. Write (or generate) a markdown file 2. Work with Claude Code to integrate and adjust design 3. Commit and push 4. Cloudflare deploys automatically Any team member — engineer, designer, marketer — can update the site by editing a markdown file. No Webflow expertise required. And with Claude Code, changes are literally a prompt away: "Add a new blog post about X" or "Update the homepage hero copy" produces a working commit in seconds. > The biggest shift isn't the technology, it's the operating model. When your entire team can ship changes through markdown and a conversation with AI, you eliminate the bottleneck of specialized platform knowledge. Engineers focus on architecture, designers iterate directly on the product, and content authors publish without waiting in a queue. For any company still tied to a proprietary CMS, the question isn't whether to make this move, it's how much velocity you're leaving on the table by waiting. > > **Jorge Coca, Head of Engineering @ Very Good Ventures** ### Designers in the Driver's Seat One of the most unexpected wins was how our designers used Claude Code to iterate on the site's design directly. Instead of the traditional cycle — designer creates a mockup, hands it to an engineer, waits for implementation, reviews, requests changes, repeat — our designers could describe what they wanted and refine it in real time. Spacing adjustments, typography tweaks, color token changes, component layout refinements — all of it happened in conversation with Claude Code, not in a ticket queue. Designers could see their changes reflected in code immediately, test them in a browser, and keep iterating until the result matched their vision. The design-to-implementation feedback loop collapsed from days to minutes. ![Illustration of the design feedback loop collapsing from days to minutes with AI](/assets/images/blog/migrating-vgv-from-webflow-to-astro/body-5.avif) This matters because it shifts who has agency over the final product. When designers can directly shape the output without needing to coordinate with engineers for every visual detail, the entire team moves faster — and the result is more faithful to the original design intent. > What’s changing isn’t just speed... how design and development work together. We’re moving from creating static outputs to orchestrating living systems, where design and development happen in tandem. With AI as a collaborator, designers can shape, test, and refine directly in the product, creating a more fluid workflow that benefits the entire team. > > **Joshua Wells, Commercial Strategy Designer @ VGV** ### A Learning System That Compounds Early in the migration, we introduced a simple but powerful practice: we had Claude Code maintain a markdown "lessons learned" file, recording every unique gotcha, edge case, or non-obvious solution it discovered along the way. Webflow CDN returning 403s on certain asset URLs? Documented, with the workaround. Inline links getting stripped during HTML-to-markdown conversion? Documented, with the fix. This file became a living reference that Claude consulted at the start of every session. When it encountered a similar situation later — a new batch of pages with the same CDN issue, for example — it already knew the solution. What would have been a repeated debugging cycle became an instant, automatic fix. The effect compounded over the course of the migration. One-off discoveries turned into reusable patterns. Edge cases that tripped us up in batch 1 were handled silently in batch 7. The learning system turned Claude Code from a capable tool into one that got measurably better at *our specific project* over time — a form of institutional knowledge that persisted across sessions. ## Performance Wins Moving from Webflow's runtime rendering to Astro's static output brought dramatic performance improvements: - **Zero JavaScript by default** — Astro only ships JS for components that explicitly need client-side interactivity - **309 pages pre-rendered at build time** — no server-side rendering, no hydration delays - **Cloudflare's global edge network** — static HTML served from the nearest data center worldwide - **Full version control** — every change tracked in git, every deploy reproducible Where Webflow injects its own runtime scripts, analytics wrappers, and interaction handlers on every page, our Astro site serves clean HTML and CSS. The difference in time-to-interactive is significant. ## QA at Scale with AI With 309 pages to validate across multiple browsers and viewports before launch, manual QA wasn't realistic. So we turned to Claude Code again — this time as an autonomous QA engineer. One of our engineers, whose primary expertise isn't web development, set up a Claude Code agent with a product requirements document describing what needed to be tested. From there, the agent worked autonomously. It wrote **22 Playwright test suites** covering URL crawls, SEO audits, visual regression, accessibility checks, navigation flows, and form submissions. It then ran over **1,500 tests** across Chromium, Firefox, and WebKit at mobile, tablet, and desktop viewports — capturing **374 screenshots** for visual comparison. ![QA automation results: 22 test suites, 1,500+ tests, 374 screenshots across 3 browsers](/assets/images/blog/migrating-vgv-from-webflow-to-astro/body-4.avif) The results were thorough: **12 issues** documented with severity ratings, the specific URL, browser, viewport, and screenshot evidence for each. Issues ranged from missing meta tags (something the engineer noted they wouldn't have thought to check themselves) to layout inconsistencies at specific breakpoints. But the process didn't stop at finding problems. A second Claude Code agent spun up on an isolated git worktree, triaged all 12 findings, and **fixed 6 of them** automatically — producing a clean pull request. The remaining 6 were correctly classified as out of scope: upstream HubSpot rendering issues, redirect rules that only work on the live Cloudflare environment, or changes too complex for an automated fix. The total human investment: about 30 minutes writing the PRD and configuring the setup. The "finder" agent ran for roughly 2 hours. The "solver" agent ran for 14 minutes. What would have taken days of manual cross-browser testing was handled overnight, and the engineer who ran it gained confidence in areas outside their core expertise. This is what AI-native QA looks like: not replacing human judgment, but extending it to a scale and thoroughness that wouldn't be practical otherwise. ## What's Next The migration is complete. All three phases — content extraction, site build, and DNS cutover — are done. Every URL from the original Webflow site now resolves to its Astro equivalent or a proper redirect, preserving SEO equity with zero broken links. But the migration itself was never really the point. The point is what comes after: a website that evolves at the speed of thought, where any team member can ship changes with confidence, and where AI isn't bolted on as an afterthought — it's woven into how we build. > In the digital agency world, it's all about creating timely content that's relevant to our clients and prospects as quickly as possible. Before this new website existed, we relied on creating custom PDFs for our clients, as that gave us full creative control. With this new experience, however, we can create custom briefing pages that showcase digital visions, trends, and insights—not just for our clients, but for our prospects as well—and we can do it almost instantly. This enables us to produce content-rich pages, each personalized to a client and tailored specifically to their business and technical situation. > > Additionally, in our old setup, our process was weighed down by red tape. We had to coordinate with our craftspeople, extract their insights, and translate those into market-ready thought pieces. With this new setup, we can train our craftspeople to use Claude directly, effectively giving each of them a megaphone to share their thought leadership. This dramatically accelerates the pace at which we can share trends and technical insights with our audiences. > > **Nate Wootten, Chief Strategist Officer @ VGV** Now that the foundation is in place, we're exploring what else becomes possible: richer interactive content, deeper personalization, and continued experimentation with AI-driven workflows across design, development, and QA. If your team is considering a similar move away from a proprietary CMS, our advice is simple: start with content extraction, validate obsessively, and build incrementally. The tools are ready. The workflows are proven. And the performance gains speak for themselves. We're excited about what's next — and we'll keep sharing what we learn along the way. ### Very Good CLI MCP Server: AI-Powered Flutter Development Source: https://verygood.ventures/blog/very-good-cli-mcp-server-flutter-ai-tools/ ![Very Good CLI MCP server terminal displaying the very_good mcp command connecting to Claude Desktop, Cursor, and VS Code — Model Context Protocol Flutter CLI integration](/assets/images/blog/very-good-cli-mcp-server-flutter-ai-tools/image1.avif) You can ask Claude to help you scaffold a Flutter app. You can ask it to review your test coverage. What you could not do until now is have your AI assistant actually run the scaffolding, execute the tests, or check the licenses. That gap is what [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) closes, and it is why we added an MCP server to [Very Good CLI](https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/). The Very Good CLI MCP server exposes real development workflows to AI agents and editors: project creation from templates built on [our documented Flutter conventions](https://engineering.verygood.ventures/), test execution with coverage enforcement, dependency management, and [license compliance for Dart and Flutter](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/). This is not "here is MCP in theory." This is what MCP unlocks in a serious Flutter project. > **Note:** The MCP server is an experimental feature. We are shipping it early because the value is already real, with the caveat that MCP itself is still evolving. Expect changes as the protocol matures. ## A Quick Primer on MCP AI agents can already run commands on your machine. Tools like Claude Code, Cursor, and Windsurf have shell access — they can guess the right CLI invocation, execute it, parse the output, and keep going. If you ask an AI agent to scaffold a Flutter app, it can absolutely try running `very_good create flutter_app my_app` in a shell. Sometimes it gets it right. Sometimes it hallucinates a flag that does not exist, forgets a required argument, or misinterprets the output. [MCP](https://modelcontextprotocol.io/introduction) does not give AI agents a capability they lacked. It gives them a **deterministic** way to use that capability. Instead of composing a shell command from memory (or imagination), the agent calls a structured tool with a well-defined schema: typed parameters, documented options, and predictable output. The difference is reliability. The protocol itself is straightforward. An MCP server advertises a set of tools over standard I/O using [JSON-RPC 2.0](https://www.jsonrpc.org/specification). An MCP client (your AI editor) discovers those tools, and the AI model decides when and how to call them based on what you ask for. No proprietary SDK, no vendor lock-in, no platform-specific glue code. One server works with every compatible client. ### Prompt-Based Execution vs. MCP: A Comparison To make this concrete, here is the same task — running tests with 100% coverage — done both ways: **Via prompt (shell execution):** You ask: *"Run tests with 100% coverage enforcement."* The agent guesses the command, perhaps `very_good test --coverage --min-coverage 100`, executes it in a shell, and parses the raw terminal output as unstructured text. It works when the agent remembers the exact flags. When it does not — wrong flag name, missing argument, deprecated syntax — you get a confusing error that the agent may or may not recover from. **Via MCP (structured tool call):** You ask the same thing. The agent discovers the `test` tool from the MCP server, sees that it accepts `coverage: true` and `min_coverage: "100"` as typed parameters, and calls it directly. The server validates the input, runs the command, and returns structured output. No guessing involved. | | Prompt-based (shell) | MCP (structured tool) | | ------------------- | --------------------------------------------------------- | --------------------------------------------------------------- | | **Discovery** | Agent must know or guess the CLI syntax | Agent discovers available tools and their schemas automatically | | **Reliability** | Non-deterministic — depends on the model's training data | Deterministic — validated parameters, documented options | | **Error handling** | Agent parses raw terminal output, may misinterpret errors | Server returns structured results the agent can reason about | | **Maintainability** | CLI changes break the agent's assumptions silently | Schema updates are surfaced to the agent at discovery time | | **Security** | Arbitrary shell execution with broad permissions | Scoped tool calls with defined inputs and outputs | Both approaches get the job done when everything goes right. The difference shows up when things go wrong — and in production Flutter projects, things go wrong often enough that determinism matters. ### What This Looks Like in Practice For Flutter and Dart developers, MCP turns best-effort shell calls into reliable tool invocations: * You ask your AI assistant to scaffold a new app. Instead of guessing the `very_good create` syntax, it discovers the `create` tool, sees the available templates and parameters, and calls it with the right arguments. The project scaffolds correctly on the first try. * You ask it to run your test suite with 100% coverage. It calls the `test` tool with typed parameters — no risk of a wrong flag name or a missing dash. It parses the structured output and tells you which files fell short. * You ask it to check license compliance across a monorepo before opening a PR. It calls `packages_check_licenses` with the right scope, gets back a structured result, and flags exactly what needs attention. The pattern is the same every time: you describe what you want, and the AI assistant executes it through a well-defined tool interface rather than improvising a shell command. ### Why We Think This Matters We have spent years building CLI tools that make Flutter development faster and more reliable. MCP lets us put those same tools directly in the hands of AI assistants, which changes the developer experience in two concrete ways. First, it collapses the feedback loop. Instead of switching between your editor and terminal, copying commands, waiting for output, and then interpreting results, you stay in one place. The AI assistant handles the round trip. Second, it makes the tools composable in ways they were not before. An AI agent can chain multiple tool calls together — scaffold a project, install dependencies, run tests, check licenses — in a single interaction, without you orchestrating each step manually. ![MCP feedback loop showing an AI chat conversation triggering Flutter test execution and returning results — Very Good CLI MCP server workflow](/assets/images/blog/very-good-cli-mcp-server-flutter-ai-tools/image2.avif) ## How Very Good CLI MCP Complements the Official Dart MCP Server The official [Dart MCP Server](https://docs.flutter.dev/ai/mcp-server) provides general-purpose Dart and Flutter tooling for AI agents. The Very Good CLI MCP server builds on top of that foundation and adds capabilities specific to professional Flutter development: VGV-curated project templates (including Flutter apps, packages, plugins, Flame games, Dart CLIs, and documentation sites), fail-fast test execution with coverage enforcement and golden-file updates, recursive dependency resolution across monorepos, and automated license compliance checks. If the official server gives your AI agent basic Flutter literacy, the Very Good CLI MCP server gives it a production engineering workflow. ## Prerequisites Before starting the MCP server, make sure you have Very Good CLI v1.0.0 or later installed. See the [Very Good CLI documentation](https://cli.vgv.dev/docs/overview) for installation instructions. ## Quick Start To start the MCP server, run: ```sh # Listens on stdio using JSON-RPC 2.0 very_good mcp ``` > **Experimental:** This command relies on the [Dart MCP Server](https://docs.flutter.dev/ai/mcp-server), which is experimental and may change without notice. Test thoroughly before integrating into production workflows. ## Client Configuration The Very Good CLI MCP server works with all major AI-powered editors and assistants. The table below summarizes supported clients, and the configuration snippets that follow show the exact setup for each one. | Client | Config File | Transport | | ------------------------ | ---------------------------- | --------- | | Claude Desktop | `claude_desktop_config.json` | stdio | | Claude Code | `.claude/settings.json` | stdio | | Cursor | `.cursor/mcp.json` | stdio | | VS Code / GitHub Copilot | `.vscode/mcp.json` | stdio | | Windsurf | `~/.windsurf/mcp.json` | stdio | ![Diagram showing Claude Desktop, Claude Code, Cursor, VS Code, and Windsurf connecting to the Very Good CLI MCP server — MCP client configuration for Flutter development tools](/assets/images/blog/very-good-cli-mcp-server-flutter-ai-tools/image4.avif) **Claude Desktop** (`claude_desktop_config.json`): ```json { "mcpServers": { "very_good_cli": { "command": "very_good", "args": ["mcp"] } } } ``` **Claude Code** (`.claude/settings.json`): ```json { "mcpServers": { "very_good_cli": { "command": "very_good", "args": ["mcp"] } } } ``` **Cursor** (`.cursor/mcp.json`): ```json { "mcpServers": { "very_good_cli": { "command": "very_good", "args": ["mcp"] } } } ``` **VS Code / GitHub Copilot** (`.vscode/mcp.json`): ```json { "servers": { "very_good_cli": { "command": "very_good", "args": ["mcp"], "type": "stdio" } } } ``` **Windsurf** (`~/.windsurf/mcp.json`): ```json { "mcpServers": { "very_good_cli": { "command": "very_good", "args": ["mcp"] } } } ``` ## Available MCP Tools The Very Good CLI MCP server exposes four tools that cover the core Flutter development lifecycle: scaffold a project, run tests, manage dependencies, and verify license compliance. Each tool is available to any MCP-compatible AI client configured above. ![Very Good CLI MCP tools overview showing create, test, packages_get, and packages_check_licenses commands for Dart and Flutter project automation](/assets/images/blog/very-good-cli-mcp-server-flutter-ai-tools/image3.avif) ### `create` — Scaffold New Flutter and Dart Projects Generate a new Dart or Flutter project from a curated template. Your AI assistant can scaffold a production-ready Flutter app, package, plugin, Flame game, Dart CLI, or documentation site without you leaving the chat interface. ```json { "tool": "create", "arguments": { "subcommand": "flutter_app", "name": "my_app", "description": "A Very Good Project created by Very Good CLI.", "org_name": "com.example.verygoodcore", "output_directory": "./", "application_id": "com.example.my_app", "platforms": "android,ios,web,macos,linux,windows", "publishable": true, "executable-name": "my_cli", "template": "core" } } ``` `subcommand` and `name` are required. Choose one subcommand per invocation: `flutter_app`, `flutter_package`, `flutter_plugin`, `flame_game`, `dart_cli`, `dart_package`, or `docs_site`. All other parameters are optional. | Parameter | Applicable Subcommands | | ----------------- | ------------------------------------------------------------------ | | `platforms` | `flutter_plugin` (all platforms), `flame_game` (android, ios only) | | `publishable` | `flutter_package`, `dart_package` | | `executable-name` | `dart_cli` | | `template` | `flutter_app` (`core` or `wear`) | ### `test` — Run AI-Assisted Flutter Tests Run your full test suite with [test coverage and optimization workflows](https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/) from within your AI editor. Your AI agent can execute tests across a monorepo, enforce minimum coverage thresholds, and report results back in the conversation. ```json { "tool": "test", "arguments": { "directory": "./my_app", "dart": false, "coverage": true, "recursive": true, "optimization": true, "concurrency": "4", "min_coverage": "100", "tags": "unit", "exclude_tags": "integration", "exclude_coverage": "**/*.g.dart", "update_goldens": false, "force_ansi": false, "platform": "chrome | vm | android | ios", "dart-define": "foo=bar", "dart-define-from-file": "config.json", "test_randomize_ordering_seed": "random" } } ``` All parameters are optional. Note: when `optimization` is not specified, `--no-optimization` is applied by default. ### `packages_get` — Manage Dependencies with AI Install or update Dart and Flutter package dependencies across your project. When working in a monorepo, set `recursive` to `true` and let your AI agent resolve dependencies for every package in one pass. ```json { "tool": "packages_get", "arguments": { "directory": "./my_app", "recursive": true, "ignore": "package1,package2" } } ``` All parameters are optional. ### `packages_check_licenses` — Automate License Compliance Verify that all package licenses in your project meet your compliance requirements. This is especially useful in enterprise Flutter projects where license auditing is part of the CI pipeline, and your AI agent can flag non-compliant dependencies before they reach a pull request. ```json { "tool": "packages_check_licenses", "arguments": { "directory": "./my_app", "licenses": true } } ``` All parameters are optional. `licenses` defaults to `true`. ## Putting It All Together: A Workflow Example Here is what a real MCP-powered development session looks like. You open Claude Code (or Cursor, or VS Code with Copilot) and type: > "Create a new Flutter app called `budget_tracker` using the core template, then run all tests with 100% coverage enforcement." The AI agent calls the `create` tool to scaffold the project from VGV's opinionated template, then immediately calls the `test` tool with `coverage: true` and `min_coverage: "100"`. You get a fully scaffolded Flutter app and a test report without touching the terminal yourself. That is the gap the Very Good CLI MCP server closes: your AI assistant can reason about what to do and actually do it. ## Common Pitfalls If you run into issues setting up the MCP server, check these common problems first: * **CLI not found:** Make sure `very_good` is on your system PATH. Run `very_good --version` in your terminal to confirm it is accessible. * **Wrong CLI version:** The MCP server requires Very Good CLI v1.0.0 or later. Run `very_good update` to get the latest version. * **Client not connecting:** Double-check the configuration file path for your editor. Each client expects its MCP config in a specific location (see the table in the Client Configuration section above). * **Stale server state:** If tools stop responding, restart the MCP server by restarting your AI editor or re-running `very_good mcp`. ## What Comes Next We built the Very Good CLI MCP server because we believe the best developer tools disappear into the workflow. When your AI assistant can scaffold, test, and verify compliance without you switching contexts, the tool has done its job. This is one step in that direction, and we are continuing to build on it. To get started, install [Very Good CLI](https://cli.vgv.dev/docs/overview) and run `very_good mcp`. For more on MCP servers relevant to Dart and Flutter development, see our companion post: [7 MCP Servers Every Dart and Flutter Developer Should Know](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/). ### The Last Mile Gap: How Generative UI Drives New Account Openings in Retail Banking Source: https://verygood.ventures/blog/genui-last-mile-gap-banking-account-openings/ Ask any room of banking executives how many have increased their AI investment over the past two years. Nearly every hand goes up. Now ask how many can say their customers are significantly more satisfied with their digital experience than they were two years ago. Most hands drop. That gap — between what banks are spending on AI and what customers are actually experiencing — is what we call the **Last Mile Problem**. And it's costing the industry more than most leaders realize. ## The Investment Paradox The scale of AI investment in financial services is staggering. ![The investment paradox: AI investment in banking is growing at 32% CAGR, projected to reach $143.6B by 2030, with McKinsey estimating $200–340B in annual value unlock and banks spending an average of $22M per year on GenAI](/assets/images/blog/genui-last-mile-gap-banking-account-openings/investment-paradox.avif) [McKinsey estimates](https://www.mckinsey.com/industries/financial-services/our-insights/capturing-the-full-value-of-generative-ai-in-banking) generative AI could unlock **$200 to $340 billion annually** for global banking. The average bank is already spending **[$22 million per year](https://www.bain.com/insights/ai-in-financial-services-survey-shows-productivity-gains-across-the-board/)** on GenAI initiatives, a figure growing at a 32% CAGR. And yet: [MIT's 2025 State of AI in Business report](https://fortune.com/2025/08/18/mit-report-95-percent-generative-ai-pilots-at-companies-failing-cfo/) found that 95% of enterprise AI pilots fail to deliver ROI. [J.D. Power's 2025 Banking Mobile App Satisfaction Study](https://www.jdpower.com/business/press-releases/2025-us-banking-and-credit-card-mobile-app-satisfaction-studies) tells a very specific story about why. Virtual assistant usage in banking apps has declined — not stagnated, declined — from 33% to 30%. Customer satisfaction scores dropped from 691 to 687 out of 1,000. J.D. Power's own explanation: customers now use ChatGPT daily. When they come back to your app, the experience gap is visible. The money is flowing into Customer Data Platforms, AI Operations infrastructure, and data lakes. The intelligence being generated is real. But when a customer opens your app, they still see a static menu, a list of tabs, and a generic product page that bears no relationship to who they are or what they actually need. ## What Your AI Knows. What Your Customer Sees. This is the Last Mile Problem. ![The Last Mile Problem: The intelligence never reaches the interface. What your AI knows: balances, cash-flow patterns, life events, risk profile, product usage, intent. What the customer still sees: static dashboards, generic menus, personalization that stops at "Hello, Customer."](/assets/images/blog/genui-last-mile-gap-banking-account-openings/last-mile-problem.avif) Consider the data advantage banks already hold. Your systems know each customer's balances and cash-flow patterns. Transaction history and merchant categories. Life events — a new job, a recent move, a new baby. Risk profile and credit signals. Intent signals from browsing behavior. But when a customer opens your mobile app, they see: a static dashboard, segmentation-driven offers that could apply to anyone, a generic menu of categories, and feature help buried behind three taps. ![GenUI: The Experience Opportunity in Financial Services — projected value created by AI (McKinsey) is rising, but declining customer satisfaction (J.D. Power) is moving in the opposite direction. GenUI closes that gap.](/assets/images/blog/genui-last-mile-gap-banking-account-openings/two-lines-gap.avif) AI investment is projected to unlock hundreds of billions in value. Customer satisfaction is moving in the opposite direction. GenUI is the bridge. Accenture research shows personalization drives a **10–20% revenue lift** in financial services. We know it works. Most institutions simply can't capture it at the screen level. The intelligence never reaches the interface. Now here's what closing that gap looks like in practice — the same bank, the same app, completely different experiences built in real time for two different customers:
Same bank. Same app. Completely different home screens, assembled in real time based on each person's profile, goals, and life situation. This is what closing the Last Mile looks like. ## Finding the Highest-Impact Starting Point Understanding that GenUI can personalize the interface is one thing. Knowing *where to start* is another. Most institutions have dozens of potential use cases on a whiteboard somewhere. Competing priorities, legacy constraints, and a board asking for ROI proof make the prioritization question real. Rather than guessing, VGV used our **Opportunity Map** framework — a proprietary methodology built on [Anthony Ulwick's Jobs to Be Done](https://strategyn.com/jobs-to-be-done/) research. ### The Research We conducted in-depth interviews with banking customers who were actively considering opening a new account but had not yet progressed. The goal was to uncover the *jobs* these customers needed to complete in order to feel confident moving forward. Three jobs emerged consistently: - **Understand what I would actually gain** — not marketing copy, but real clarity about how a specific account fits my specific financial situation - **Compare options without getting lost** — customers felt overwhelmed trying to evaluate products, benefits, and tradeoffs across multiple offerings - **Complete the process without friction** — every extra step, every re-entry of information, every form that doesn't auto-populate is a reason to quit These are not new insights. They have been sitting in your NPS data for years. What's new is what we can now do about them. ### The Opportunity Map We plotted each job against two inputs: how important it is to customers, and how satisfied they currently are with how their bank supports it. Where importance is high and satisfaction is low — that gap is the opportunity score. ![The CX Opportunity Map: Importance, Opportunity, and Satisfaction scores plotted across all jobs in the account-opening journey — with the biggest gap clearly visible at "Understand what I'd gain by switching" in the first phase](/assets/images/blog/genui-last-mile-gap-banking-account-openings/opportunity-map.avif)

Look at the far left of the map.

The biggest opportunity across the entire account-opening journey sits right at the start: helping customers understand what they'd actually gain by opening a new account in the first place — and making that value personal to their specific life situation. High importance. Low satisfaction. This is where the gap is largest, and where GenUI can move the needle fastest.

This job ranked as highly important across all customer segments — yet existing digital experiences left it nearly unserved. Customers wanted clarity and relevance. Instead, they were met with generic product descriptions and marketing language that could apply to anyone. [The average account-opening abandonment rate has doubled to 67% for some banks](https://www.mx.com/blog/account-opening-stats/). [Abandonment exceeds 50%](https://www.mx.com/blog/account-opening-stats/) when the process takes more than three to five minutes. Customers aren't abandoning because the application is too hard. They're abandoning before they even reach it — because no one has convinced them the account is right for them. ## GenUI in Action: Guiding a Customer to Account Opening What does GenUI look like at this specific moment in the journey? The demo below shows a bank using LLM-generated UI elements to guide a customer through exactly this job.
Notice what the bank is doing here. Rather than dropping the customer into a product catalog, the LLM first guides them through a series of savings scenarios — asking about their goal, their timeline, and what tradeoffs matter to them. Only once those scenarios are personalized to the customer's actual life situation does the system surface an account recommendation. The AI isn't presenting options; it's building understanding first, then translating that understanding into a specific, justified recommendation. This is the key distinction between GenUI and traditional personalization. It is interpreting intent — "I want to start saving for my daughter's college fund" — and assembling a flow-based experience in real time: a context bar that updates as the conversation progresses, visual UI components that surface precisely when the customer needs them, and a comparison surface that appears exactly when the customer faces a complex decision. The bank controls what the AI can do — the approved components, the compliance rules, the brand guardrails. The AI decides how to assemble those components, for this customer, in this moment.

GenUI is the experience layer where your AI investment finally becomes visible to customers.

Learn more about Generative UI at VGV
### What Game Jams Teach You About Building Products Source: https://verygood.ventures/blog/what-game-jams-teach-you-about-building-products/ Most scope problems don't announce themselves. You design a system that makes sense on paper, start building, and then somewhere in the middle (usually closer to the deadline than you'd like) it becomes clear that what you planned and what you can actually deliver aren't the same thing. That's exactly where my brother [Morgan Hunt](https://x.com/mrgnhnt96_dev) and I found ourselves on day three of the [Flame Game Jam 2026](https://itch.io/jam/flame-game-jam-2026). The [Flame Game Jam 2026](https://itch.io/jam/flame-game-jam-2026) is Blue Fire's annual game jam for Flame, the open-source 2D game engine that runs on Flutter. This year's ten-day edition featured one revealed theme and one required diversifier—a constraint or implementation challenge chosen from a list. This was my first time competing as a duo; in previous years, I'd entered solo. Morgan owned the technical foundation (game logic, sector calculations, and the procedural generator), while I took frontend development and UI. When the theme dropped as "Big Brother," we began brainstorming. We ran through the obvious directions (surveillance thrillers, dystopian aesthetics) and landed on something more interesting: a social commentary on AI dependency, wrapped in mechanics inspired by [Plague Inc.](https://en.wikipedia.org/wiki/Plague_Inc.) You play as an LLM. Your goal is to spread AI dependency across the globe before an anti-AI organization shuts you down. We called it *[Suppressed Intelligence](https://github.com/Huntaylor/suppressed_intelligence)*. Our planned game design would include four sector stats, three upgrade trees, a procedurally generated news report pushing pro- or anti-AI headlines every 15 seconds (our chosen diversifier), and a resistance organization that mobilized once you'd made enough of an impact. For the visual identity, I landed on a Windows 95 aesthetic. ## The Moment You Have to Choose By day three, we still lacked a playable build and were running out of time. The mechanics existed in pieces, but the pieces weren't connected into anything a player could experience. This is a moment every team faces in time-boxed work. The call you make in that moment determines whether you ship something people can actually engage with, or something that technically exists. We narrowed to a single upgrade tree (AI Infiltration) and removed two of the four sector stats. The core gameplay loop stayed intact: players pop AI bubbles to infiltrate connected regions while managing the news headline carousel. The game we shipped was smaller than the game we designed, and it was better for it. ## What We Should Have Aligned on Earlier We'd gone into the game jam with a general idea of what each upgrade tree would do, but not the specifics. When it came time to actually build the upgrade system, Morgan and I had to stop and define things that should have been established before coding began. This cost us time we didn't have. > The lesson isn't that you need exhaustive specs before you build. It's that the communication between system pieces needs to be agreed on before anyone starts building them in parallel. Like the variables, the interactions, and the cause-and-effect relationships. In a 10-day sprint, two days of misaligned assumptions are a significant tax. In a longer product cycle, the same dynamic compounds. ## Playtesting With 24 Hours Left With a playable game and less than a day on the clock, we sent our build to friends and family who had offered to test it. The brief was simple: what worked, what confused you, what didn't work. We got back three clear priorities: 1. Players didn't know how to play. A tutorial wasn't optional; it was the minimum viable experience. 2. The upgrade button blended into the background. Several testers hadn't discovered it until well into the game. 3. The difficulty was too high. Everyone reported losing. With a handful of bug reports alongside these, we now had three improvements that needed to be done during the last day. I built the tutorial as a staged system: a UI panel that slides in and pauses the game at key moments rather than front-loading everything at once. It triggers at game start, when the player selects their launch sector, and when AI Dependency hits 15%. The goal was to deliver context when it was relevant, not dump it all on the player before they'd had a chance to understand what they were doing. Morgan tackled the difficulty curve. When playtesters came back after the fixes, the verdict was clear: significant improvement over the previous version. We submitted with 8 minutes and 23 seconds to spare. ## What Time-Boxed Work Actually Teaches You A game jam is an extreme version of something product teams navigate constantly. Building something real under real constraints, with ambiguous information, and a deadline you can't negotiate. Three things I've learned from this experience: **Scope discipline is a skill, not a compromise.** The teams that ship well under constraint aren't the ones who do less; they're the ones who identify the core loop early and build toward it. Everything else is additive, and additive features earn their way in based on what the core loop still needs. **Pre-build alignment has a cost when it's skipped.** Variables that touch multiple systems, and interactions between components built by different people, need to be defined before anyone starts building. This isn't process overhead; it's the minimum coordination that prevents expensive rework later. **Testing with real users, even briefly, changes what you build.** We made better decisions in the final day because we got feedback from real people. The tutorial design, the button positioning, the difficulty tuning: these didn't feel important until our testers brought attention to them. The game we shipped wasn't the game we designed, but it was more concise and intentional. That's the trade-off at the center of every constrained build: not what we *can add*, but what *is needed* to work. ### Zero to Rust in Three Weeks: How We Built a Real-Time NASCAR Pipeline with Claude Code Source: https://verygood.ventures/blog/zero-to-rust-in-three-weeks/ In February 2026, I sat in the Trackhouse Racing pit box at Daytona watching a data processing pipeline we'd been building fall further behind with every lap. The data arrived in real time. Our processing didn't. Results showed up 16 to 24 hours after the checkered flag. Three weeks later, we had a completely rewritten pipeline in Rust, a language I'd never touched, processing the same workload in real time during the race. This weekend at Darlington, it runs live. This is the story of how we got there with AI, and why the hardest part wasn't writing the code. ## The Problem Trackhouse Racing needed complex, computationally intensive processing of NASCAR telemetry data in real time, during the race. The scale is hard to overstate. 22,000+ messages per second across dozens of data points, feeding a processing pipeline that needs to run 50 times per second across 36 cars. The results need to go to three places at once: LiveKit for real-time visualization in Flutter apps, Azure Blob Storage for analytics, and Databricks for the data science team. And it all needs to run for 3.5-hour races without losing data. Our Python prototype got the processing right. The math was correct. The data arrived on time. But Python's computational overhead meant each processing tick took more than 200ms at a data rate that demanded sub-20ms. Every tick that ran long pushed the next one further behind. Over a 3.5-hour race, the backlog snowballed. Results that should have been available in milliseconds took days. Everything worked. It just wasn't fast enough. We needed orders of magnitude more performance to keep pace with the data. ## Why Rust Rust kept bouncing around in my head as an alternative. Fast, popular for server-side work, increasingly the go-to choice for teams that needed serious performance. It seemed like it could be the right fit. The catch: I'd never written a line of it. And with NASCAR racing every week, every week without a reliable pipeline was another race where the team didn't have the real-time data they needed to improve on-track performance. We needed to move fast without cutting corners. At VGV, we've been investing heavily in how AI tooling fits into our engineering practice. Not as a shortcut, but as an accelerator grounded in taste and trust. Taste to know what the right architecture for the experience looks like. Trust to make sure it actually works in production. This was exactly the kind of challenge that approach was built for. I would use Claude Code, Anthropic's AI coding assistant, as my primary implementation partner. I would drive the architecture, make the decisions, own the outcome. Claude would write the Rust. ## What AI-Assisted Engineering Actually Looks Like The popular narrative around AI coding tools is simple. Describe what you want, get code back, ship it. That narrative is wrong. Or at least, it leaves out the part that actually matters: the gap between AI-generated output and production-grade software. Here's what actually happened over three weeks. ### Week 1: Understanding the Problem and Planning the Migration Before writing any Rust, we needed to understand what we were building and why. The product goal was straightforward. Give the Trackhouse team real-time access to processed telemetry during the race, not hours after. That meant the pipeline had to keep pace with a 50Hz data stream across 36 cars, publish to three different outputs simultaneously, and run stable for 3.5 hours without manual intervention. I started by pointing Claude at the existing Python codebase. Not to rewrite it line by line, but to deeply analyze it. Claude produced detailed assessments of the architecture, identified where the performance bottlenecks lived, mapped out every data transformation and output path, and generated migration plans that laid out what could port directly and what needed to be rethought from scratch. These reports became the blueprint for the entire project. From there we designed the Rust workspace together. But the key decisions weren't about code. They were about architecture. How do we handle backpressure when Azure uploads are slow but LiveKit needs to maintain a smooth framerate? What happens when one output falls behind? How do we guarantee data durability without stalling the real-time stream? These aren't questions an AI answers well on its own. They require understanding operational constraints. NASCAR's data stream has specific timing characteristics. A single dropped frame matters less than a pipeline stall. I made those calls. Claude implemented them. ### Week 2: Stress Testing and Hitting Walls The first Rust pipeline was based directly on the Python architecture. A monolithic process that ingested data, ran the processing, and published to all outputs in one place. The performance gains were immediate. 200-400ms per tick in Python down to 2-4ms in Rust. A massive improvement. But the system started showing cracks under real race conditions. We ran replays of full races and watched everything. Memory usage, disk throughput, message latency, consumer lag. Monitoring every 10 minutes, just observing. Two problems emerged. The first was speed. The core processing could keep pace with the data stream, but the outputs couldn't always keep up. Azure Blob uploads take time. Databricks publishes over gRPC with acknowledgment latency. When those slowed down, they back-pressured the processing, and the whole pipeline stalled. The second was data loss. We needed durable storage of every tick for analytics and data science. Losing data wasn't acceptable. But the act of durably storing everything was exactly what was dragging the system down. The Databricks publisher would fall behind, its memory would grow as it buffered unsent records, and eventually it would crash and take the entire pipeline down with it, including the real-time stream the team was actually watching. This was the core tension and the critical insight that shaped everything that followed. The processing was fast enough. The real-time stream was fast enough. But we couldn't guarantee durable capture of every data point without risking the system that was supposed to deliver it. In a monolithic architecture, the requirement to store everything conflicted with the requirement to never stall. ### Week 3: Re-Architecture and Rapid Iteration Week 3 was about tearing apart the monolith and rebuilding it as independent pieces. We designed a NATS JetStream fan-out architecture. 9 independent binaries, each responsible for exactly one thing. The processing engine reads from one stream and writes to another. LiveKit, Azure, and Databricks each have their own consumer binary. If one crashes, the others keep running. This design came from a conversation, not a prompt. I described what we'd observed during stress testing, and we iterated on solutions until we landed on the fan-out model. Claude wrote the integration code, the consumer reconnection logic, the compression and retention configuration. I reviewed every architectural choice. What followed was a rapid cycle of deploy, test, observe, iterate. We'd run a full race replay, monitor it, find an issue, fix it, redeploy, and test again. In a single 48-hour stretch we shipped six patch releases, each one informed by what we'd observed in the previous test. This is where Claude's value went beyond writing code. Every issue we found required deep analysis. Memory growing in a third-party SDK. A consumer silently dying without logging an error. A visual artifact that only appeared at specific points on the track. These weren't problems you solve by reading the docs. Each one required tracing through multiple layers of the system, testing hypotheses, ruling out dead ends, crunching hundreds of millions of telemetry records to find patterns, and sometimes discovering that the root cause was something completely different from what the symptoms suggested. A person doing that analysis manually would take days per issue. Claude could do it in minutes, try multiple pathways simultaneously, and present findings I could evaluate and act on. That speed of analysis is what made it possible to ship those releases in two days instead of six weeks. The fixes ranged from one-line changes to completely restructuring how data was packaged and sent. One issue was caused by a third-party SDK that couldn't handle large payloads efficiently. The solution was breaking data into much smaller chunks, which dropped memory usage from gigabytes to under 100MB. Another bug had been present since the original Python codebase and nobody had noticed. The ability to rapidly and deeply analyze complex problems, weigh solutions, and iterate is what turned a fragile prototype into a production system. ## The Pipeline Today The data flows through six stages, each running as its own independent binary. The ingest bridge connects to NASCAR's NATS feed and filters it into a local JetStream server. The processing engine picks up every tick and runs the computation, producing output for 36 cars across thousands of data points. From there, the LiveKit publisher packs results into a compact binary frame and pushes them via WebRTC at 50Hz to a multi-platform Flutter ecosystem of web, mobile, and desktop apps sharing a common real-time transport layer. The Azure Blob writer saves Parquet files for analytics. The Databricks publisher streams records in real time via gRPC. ![Pipeline architecture diagram showing the JetStream fan-out: NASCAR data flows through a Rust bridge into JetStream, then fans out to ZeroBus, a Transformer, and Azure consumers. The Transformer writes to a second JetStream stream that fans out again to ZeroBus, a LiveKit Publisher, and Azure, with the Publisher feeding a Flutter Visualizer.](/assets/images/blog/zero-to-rust-in-three-weeks/architecture.avif) Every stage is independent. The processing engine handles each tick in 0.04-0.08 milliseconds, down from 200-400ms in Python. The pipeline ingests 22,000+ messages per second and ran a full 3.5-hour race processing over 227 million messages with zero restarts and minimal drift between the live data and the slowest consumer. If the Databricks publisher falls behind, it catches up without affecting the real-time LiveKit stream. If any process crashes, systemd restarts it and the durable consumer resumes where it left off. ## What I Learned About Working With AI ### It's not about the code generation The most valuable thing Claude did wasn't writing Rust. It was being a collaborator who could hold the entire system in context. The message formats, the processing math, the upload retry logic, the binary frame protocol. It helped me reason about how changes in one place affected everything else. When I said "the Databricks consumer is crashing after 60 seconds," Claude didn't just suggest increasing the memory limit. We traced the growth through the SDK's buffer, measured the payload sizes, calculated what the theoretical maximum should have been, found the gap between theory and reality, and identified that the data packaging was the real problem. The fix wasn't tuning a config. It was rethinking how we structured the data. ### The human still drives Every architectural decision was a human decision informed by operational experience. I know what happens at a NASCAR pit box when the network drops. I know that losing 10 minutes of durable uploads is worse than a 2-second visual stall. Claude doesn't have that context unless I provide it. The AI accelerated the implementation of those decisions by orders of magnitude. But it didn't make them. ### Testing is non-negotiable We ran the pipeline through a full 3.5-hour race replay before declaring it ready. We monitored memory, disk, CPU, message throughput, and consumer lag every 10 minutes. We found and fixed 6 bugs in production-like conditions over two days of testing. AI makes it tempting to move fast. The discipline is in not shipping until you've validated at scale. ### Unfamiliar languages aren't the barrier they used to be I went from zero Rust experience to a production pipeline processing 22,000 messages per second in three weeks. Not because Rust is easy, but because the AI handled the syntax and idioms while I focused on architecture and correctness. I still don't write Rust fluently by hand. I don't need to. I understand the system because I've read and reviewed thousands of lines of it. The AI wrote the first draft. I own what shipped. ## The Bigger Picture This project isn't a story about AI replacing engineers. It's a story about what happens when you combine AI speed with the taste to make the right decisions and the trust to ensure they hold up under real conditions. Three weeks with an unfamiliar language. A pipeline that processes millions of data points in real time during a NASCAR race. That's not magic. That's taste and trust, accelerated by a new kind of tool. The pipeline runs live at Darlington this weekend. The team will have real-time data during the race for the first time. That's what matters. --- *Very Good Ventures helps teams build production-grade software at AI speed, with the taste to know what's right and the trust to make sure it works. If you're interested in how this approach could accelerate your team, [reach out](https://verygood.ventures/contact-us/).* ### VGV AI Flutter Plugin: Battle-Tested AI Best Practices for Dart & Flutter Development Source: https://verygood.ventures/blog/very-good-ai-flutter-plugin/ ![Sleek laptop displaying Flutter code with VGV Blue geometric accents — representing VGV AI Flutter Plugin's sophisticated engineering approach](/assets/images/blog/very-good-ai-flutter-plugin/image1.avif) Today, we're happy to announce the first alpha release of [VGV AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin), our collection of Flutter and Dart best practices for the AI-assisted engineering era. We built the [VGV AI Flutter Plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin) because we've seen firsthand that best practices are only useful if they're accessible in the moment you need them. This is our collection of battle-tested best practices for Flutter and Dart, now embedded directly into Claude Code to help your team apply proven approaches without losing context or productivity. The plugin is built on the foundation of our [Very Good Engineering collection of best practices](https://verygood.ventures/blog/introducing-very-good-engineering/), which has been proven in production across large consumer applications and diverse Flutter teams. These aren't theoretical guidelines—they're patterns and principles extracted from real-world work at scale. ## Why Battle-Tested Best Practices Matter Developing Flutter applications at scale requires more than individual skill—it requires consistency, clarity, and judgment that compounds across teams. The patterns we've codified in this plugin solve the real problems we encountered while building consumer-grade applications used by millions of people. We've learned what works when a codebase grows, when teams distribute across time zones, and when code quality determines user experience. That experience—combined with [Claude Code's ability to understand context and intent](https://claude.com/product/claude-code)—creates something new: AI-assisted development that doesn't just accelerate typing, but enforces judgment. ## How It Works The plugin brings five core engineering skills directly into your development workflow: - **Accessibility**: Ensure your Flutter widgets meet WCAG standards and are usable by everyone. - **Performance**: Identify rendering bottlenecks and optimize widget composition. - **Documentation**: Generate clear, intention-driven code comments and docs. - **Testing**: Write comprehensive tests that improve code reliability and provide feedback loops for AI agents. - **Design Patterns**: Apply proven architectural patterns for state management, widget composition, and code organization. ![Five interconnected skill nodes (accessibility, performance, documentation, testing, design patterns) in VGV Blue — showcasing VGV AI Flutter Plugin's comprehensive engineering capabilities](/assets/images/blog/very-good-ai-flutter-plugin/image2.avif) These skills work together as a system. Strong [design patterns](https://verygood.ventures/blog/very-good-flutter-architecture/) make code readable for AI agents. Clear [documentation](https://verygood.ventures/blog/exploring-accessibility-and-digital-inclusion-with-flutter/) provides context for intelligent suggestions. Comprehensive testing creates feedback loops that improve both code quality and AI-assisted development workflows. ### An Example: The Accessibility Skill in Action Accessibility is where best practices and quality code converge. When you build with semantic widgets, clear information hierarchies, and WCAG compliance in mind, you're not just making your app usable—you're making your code clearer, more maintainable, and more compatible with AI-assisted development. The accessibility skill in the plugin reviews your widget structure, checks color contrast ratios, validates keyboard navigation, and suggests improvements in context as you develop. Here's what this looks like in practice: ![Split-view code review interface showing Dart widget code with Claude Code accessibility feedback and suggestions — representing VGV AI Flutter Plugin's accessibility skill enhancement](/assets/images/blog/very-good-ai-flutter-plugin/a11y.png) ## Our Approach: Claude Code First This marks VGV's first open-source venture into [AI-assisted engineering tooling](https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/), and we're leading with Claude Code because we've found it best honors our philosophy of contextual, thoughtful development. You don't lose your train of thought switching between tools. Claude Code understands your code's intent and can apply best practices intelligently, not mechanically. Support for additional AI development platforms is on our roadmap. We're already building integrations with other AI-first development environments, and we'll share updates as those efforts progress. ![Platform expansion roadmap from Claude Code to Antigravity to Codex — showing VGV AI Flutter Plugin's multi-platform growth strategy](/assets/images/blog/very-good-ai-flutter-plugin/image3.avif) ## Why This Matters for Your Teams The ecosystem is establishing new norms around AI-assisted development. Tools are proliferating, but best practices for *how to work with those tools* are still being defined. Our plugin positions your team ahead of that curve—you're not just using Claude Code, you're using it with judgment built in. This is especially valuable for large teams. When best practices are codified in tooling, they scale without constant reminders or code reviews. When new team members join, they inherit your team's standards automatically. When you're thinking through a complex architectural decision, your AI assistant isn't just speeding up implementation—it's guiding you toward decisions that have worked at scale. ## How to Get Started The VGV AI Flutter Plugin is open source and available now. You can explore the repository, documentation, and examples at the [VGV AI Flutter Plugin GitHub repository](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin). For teams already using Claude Code, setup takes minutes. For teams new to AI-assisted development, we've included [step-by-step integration guides](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin) and examples that show how to integrate the plugin into your existing workflow. ## What's Next We're excited about the momentum in AI-assisted development and Flutter's readiness for the AI era. This plugin is the first of more efforts we're building to help teams apply best practices with AI tooling. We're already working on complementary tools that will enhance your AI-assisted engineering workflows, so teams of all sizes can apply a systematic approach to a new era of engineering. We also want to hear from you. The plugin is open source ([VeryGoodOpenSource/vgv-ai-flutter-plugin](https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin)), and we welcome feedback, contributions, and use cases from teams using it. Share your experience, file issues, suggest features, and contribute patterns that work for you and your team. --- ### Learn More About Very Good Engineering Our engineering philosophy extends far beyond this plugin. Explore the [Very Good Engineering collection](https://engineering.verygood.ventures/) to dive deeper into the principles behind these best practices, and check out our [blog](https://verygood.ventures/blog/introducing-very-good-engineering/) for detailed guides on Flutter architecture, testing strategies, and accessibility implementation. VGV is deeply committed to advancing engineering excellence at scale.

Engineering Docs

Where these conventions come from

The plugin encodes the same practices we document publicly, so you can read the reasoning before you accept the generated code.

Read the conventions in full

### Build an MCP Server for Your Dart CLI Source: https://verygood.ventures/blog/mcp-server-dart-cli/ When we added MCP support to [Very Good CLI](https://pub.dev/packages/very_good_cli), the first instinct was to build a parallel layer — new commands, new logic, new surface area. The better approach turned out to be the opposite: make the MCP server a thin translation layer that speaks to the CLI you already have. This post shows how we did it, and how you can apply the same pattern to any Dart CLI. The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/learn/architecture) is an open standard that lets AI assistants interact with your external tools and data sources through a unified interface. Instead of building custom integrations for every AI tool, you define your capabilities once as an MCP server, and any MCP-compatible client — [Claude Code](https://www.anthropic.com/claude-code), [Cursor](https://www.cursor.com/), [Windsurf](https://windsurf.com/), and others — can use them. If you are exploring how MCP fits into larger agentic workflows, our [Agentic AI Solutions](https://verygood.ventures/our-services/agentic-ai-solutions/) page covers the patterns we use in production. The practical payoff: once your CLI is MCP-compatible, AI assistants can scaffold projects, run tests, and manage packages on behalf of developers — without any additional integration work on your end. We shipped this pattern in the [Very Good CLI 1.0.0 release](https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/), and you can read more about the broader ecosystem in our companion post on [7 MCP servers every Dart and Flutter developer should know](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/). ## Prerequisites: packages you'll need Two packages power the MCP integration: - [`dart_mcp`](https://pub.dev/packages/dart_mcp) — A Dart implementation of the MCP protocol that provides server and client primitives. This is the official package backed by the Dart team, which we chose over community alternatives like `mcp_dart` for long-term stability and protocol version support. - [`stream_channel`](https://pub.dev/packages/stream_channel) — Abstraction for two-way communication, used for the stdio transport layer. Add them to your `pubspec.yaml`: ```yaml dependencies: dart_mcp: ^0.5.0 stream_channel: ^2.1.4 ``` ## Architecture overview The integration has three layers, each with a single responsibility: ```bash CommandRunner ──registers──▶ MCPCommand ──creates──▶ MCPServer (CLI entry) (transport) (tools & handlers) ``` ![ Isometric three-layer architecture diagram showing how CommandRunner, MCPCommand, and MCPServer connect in a Dart CLI MCP integration — dart_mcp architecture overview](/assets/images/blog/mcp-server-dart-cli/image2.avif) 1. **CommandRunner** — Registers the `mcp` subcommand alongside your other CLI commands. 2. **MCPCommand** — Sets up the stdio transport channel and starts the server. 3. **MCPServer** — Defines MCP tools and maps them to your existing CLI logic. Before we implement, let's understand the key types that make this work. ## Understanding the dart_mcp building blocks This section covers the key types from `dart_mcp` that power the MCP integration. We assume you're already familiar with [`CommandRunner` and `Command`](https://pub.dev/documentation/args/latest/command_runner/command_runner-library.html) from `package:args` — the standard building blocks for Dart CLIs. The MCP server delegates to your existing command runner, so your CLI structure stays exactly as it is. ### MCPServer `MCPServer` is the base class for building MCP-compatible servers. It handles the [protocol lifecycle](https://modelcontextprotocol.io/docs/learn/architecture): the initialization handshake, capability negotiation, JSON-RPC message framing over a `StreamChannel`, and graceful shutdown. The key things it provides: - **`fromStreamChannel` constructor** — Connects the server to a bidirectional communication channel (typically stdin/stdout). You pass an `Implementation` (your server's name and version) and optional `instructions` (a human-readable description of what the server does). - **`initialize(InitializeRequest)`** — Called when an MCP client connects. The client sends its capabilities and info; you respond with yours. This is where you set up tools, resources, or prompts. - **`done`** — A `Future` that completes when the connection closes. Your CLI command awaits this to keep the process alive. On its own, `MCPServer` doesn't do much — mixins like `ToolsSupport` add the actual capabilities. ### ToolsSupport mixin `ToolsSupport` is a mixin from `dart_mcp` that adds tool capability to your server. When mixed in, it: 1. Advertises to clients that your server supports tools during the initialization handshake. 2. Provides the `registerTool` method for defining tools. 3. Handles incoming `tools/call` requests by routing them to the correct handler. 4. Validates incoming arguments against your tool's input schema automatically. ```dart // ToolsSupport gives you this method: void registerTool( Tool tool, FutureOr Function(CallToolRequest) handler, { bool validateArguments = true, // schema validation is on by default }); ``` If a client sends arguments that don't match your schema, `ToolsSupport` rejects the call before your handler is even invoked. ### CallToolRequest and CallToolResult These are the input and output of every tool handler. **`CallToolRequest`** is what the MCP client sends when it wants to invoke a tool. It has two key fields: - `name` — Which tool to call (matched against your registered tool names). - `arguments` — A `Map` of the arguments the AI provided, shaped according to your tool's input schema. **`CallToolResult`** is what your handler returns. It tells the client what happened: - `content` — A list of content blocks. Typically `TextContent` with a message, but can also include `ImageContent`, `AudioContent`, or embedded resources. - `isError` — A boolean indicating whether the tool call failed. When `true`, the AI assistant can read the error message and try to self-correct (e.g., fix an argument and retry). This is different from a protocol-level error, which would abort the conversation. ```dart // Success CallToolResult( content: [TextContent(text: 'Project created successfully')], ); // Failure — the AI can read this and adjust CallToolResult( content: [TextContent(text: 'Invalid template name "foo"')], isError: true, ); ``` The design is intentional: tool errors go through `CallToolResult` so the AI can handle them gracefully, rather than throwing exceptions that break the protocol. ### How these types connect Here's how the pieces fit together at runtime: ```bash CommandRunner.run(args) MCPServer │ │ │ ┌───────────────────────────┤ │ │ initialize() │ │ │ └─ registerTool( │ │ │ Tool('create',...) │ │ │ _handleCreate │ │ │ ) │ │ │ │ │ │ CallToolRequest ────────▶│ _handleCreate() │ │ .name = 'create' │ │ │ │ .arguments = { │ │ translate args │ │ 'template': '...', │ │ │ │ 'name': '...' │ ▼ │◀──┼─── _runCommand(cliArgs) ──┤ _parseCreate() │ │ │ → ['create', 'flutter_app', 'my_app'] │ │ │ runs existing ◀───────────────────────┤ Command.run() │ │ │ ├── exitCode ──────────────────▶│ │ │ │ CallToolResult ──▶ AI Client │ .content = [TextContent('Success')] │ .isError = false ``` ![Runtime flow diagram showing how MCP Client sends CallToolRequest to MCPServer with ToolsSupport, which translates arguments and delegates to CommandRunner, returning a CallToolResult — dart_mcp type relationship](/assets/images/blog/mcp-server-dart-cli/image3.avif) The MCP server doesn't reimplement your CLI logic. It translates structured MCP arguments into the same `List` your `CommandRunner` already knows how to parse, calls `commandRunner.run(args)`, and wraps the exit code in a `CallToolResult`. Now let's build each layer. ## Step 1: Create the server — the minimum surface area The server is where you define what tools AI assistants can use. Extend `MCPServer` and mix in `ToolsSupport` from `dart_mcp`: ```dart import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:dart_mcp/server.dart'; import 'package:stream_channel/stream_channel.dart'; final class MyMCPServer extends MCPServer with ToolsSupport { MyMCPServer({ required StreamChannel channel, required MyCommandRunner commandRunner, }) : _commandRunner = commandRunner, super.fromStreamChannel( channel, implementation: Implementation( name: 'my_cli', version: '1.0.0', ), instructions: 'An MCP server that provides tools for my CLI.', ); final MyCommandRunner _commandRunner; @override FutureOr initialize(InitializeRequest request) async { final result = await super.initialize(request); _registerTools(); return result; } } ``` ### Key points - We extend `MCPServer` and mix in `ToolsSupport` — this is the minimum needed for a tool-serving MCP server. - The `Implementation` passed to `super.fromStreamChannel` identifies your server to clients. The `instructions` string gives AI assistants context about what your server can do. - Tools are registered inside `initialize`, not in the constructor. The `initialize` method is called during the MCP handshake — registering tools here ensures the client receives them as part of the capability negotiation. - The `_commandRunner` field is the bridge to your existing CLI. Every tool handler will ultimately call `_commandRunner.run(args)` to execute the real logic. ## Step 2: Register MCP tools with typed schemas Each tool needs a name, description, an input schema, and a handler function. The schema tells AI assistants what arguments the tool accepts, which is key for generating correct tool calls. ```dart void _registerTools() { registerTool( Tool( name: 'create', description: 'Create a new project from a template.', inputSchema: ObjectSchema( properties: { 'template': UntitledSingleSelectEnumSchema( description: 'The project template to use.', values: ['flutter_app', 'dart_package', 'dart_cli'], ), 'name': StringSchema(description: 'Project name'), 'output_directory': StringSchema( description: 'Where to create the project.', ), }, required: ['template', 'name'], ), ), _handleCreate, ); } ``` `dart_mcp` provides typed schema classes that map to [JSON Schema](https://json-schema.org/): | Schema class | Maps to | |---|---| | `StringSchema` | `{ "type": "string" }` | | `BooleanSchema` | `{ "type": "boolean" }` | | `ObjectSchema` | `{ "type": "object", "properties": {...} }` | | `UntitledSingleSelectEnumSchema` | `{ "enum": [...] }` | ![Developer reference card showing dart_mcp schema classes mapped to JSON Schema types: StringSchema, BooleanSchema, ObjectSchema, and UntitledSingleSelectEnumSchema — MCP tool input schema guide](/assets/images/blog/mcp-server-dart-cli/image4.avif) The `required` list in `ObjectSchema` tells the AI which arguments are mandatory. ## Step 3: Implement handlers that delegate, not duplicate Each handler receives a `CallToolRequest` and returns a `CallToolResult`. The pattern we use is to translate MCP arguments into CLI arguments and delegate to the existing command runner: ```dart Future _handleCreate(CallToolRequest request) async { final args = request.arguments ?? {}; final cliArgs = _parseCreate(args); final exitCode = await _runCommand(cliArgs); return CallToolResult( content: [ TextContent( text: exitCode == ExitCode.success.code ? 'Project created successfully' : 'Failed to create project', ), ], isError: exitCode != ExitCode.success.code, ); } List _parseCreate(Map args) { final template = args['template']! as String; final name = args['name']! as String; final cliArgs = ['create', template, name]; if (args['output_directory'] != null) { cliArgs.addAll(['-o', args['output_directory']! as String]); } return cliArgs; } Future _runCommand(List args) async { try { return await _commandRunner.run(args); } on UsageException catch (_) { return ExitCode.usage.code; } on Exception catch (_) { return ExitCode.software.code; } } ``` Notice the separation of concerns: - `_parseCreate` translates the `Map` from `CallToolRequest.arguments` into the `List` that `CommandRunner.run` expects. - `_runCommand` delegates to the command runner and catches exceptions. - `_handleCreate` wraps the result in a `CallToolResult` with a descriptive message. This means your MCP tools get all the validation, default values, and business logic from your existing commands for free. You're not rewriting anything — just translating the input format. And because errors are returned via `isError: true` (not thrown), the AI assistant can read the error message and self-correct. ## Step 4: Create the MCP command with stdio transport The command is responsible for setting up the stdio transport and starting the server. Users will run it with `my_cli mcp`. ```dart import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:dart_mcp/stdio.dart'; import 'package:stream_channel/stream_channel.dart'; class MCPCommand extends Command { MCPCommand({ required this.serverFactory, }); @override String get name => 'mcp'; @override String get description => 'Start the MCP server.'; final MCPServer Function({ required StreamChannel channel, }) serverFactory; @override Future run() async { // Create a channel from stdin/stdout final channel = stdioChannel(input: stdin, output: stdout); // Create and start the MCP server final server = serverFactory(channel: channel); // Block until the connection is closed await server.done; return ExitCode.success.code; } } ``` ### Key points - **`stdioChannel`** from `dart_mcp/stdio.dart` creates a `StreamChannel` that reads from `stdin` and writes to `stdout`. This is the standard transport layer for MCP CLI servers. - **`server.done`** is a future that completes when the client disconnects. The command blocks on it so the process stays alive. - **Constructor injection** of the server factory makes this testable — you can pass a mock server in tests. ## Step 5: Wire it into your command runner The final step is registering the `mcp` command in your command runner so it's available as a subcommand: ```dart class MyCommandRunner extends CommandRunner { MyCommandRunner() : super('my_cli', 'My CLI tool') { addCommand(CreateCommand()); addCommand(TestCommand()); // Add the MCP command addCommand(MCPCommand(serverFactory: MyMCPServer.new)); } } ``` That's it. Users and AI clients can now start your MCP server with: ```bash my_cli mcp ``` ## How MCP clients connect When an AI assistant wants to use your tools, the flow looks like this: ![MCP client-server handshake sequence diagram showing process spawn, stdio channel setup, initialize request, capability negotiation, tool calls, and disconnect — how AI clients connect to a Dart CLI MCP server](/assets/images/blog/mcp-server-dart-cli/image5.avif) The client discovers your tools through the initialization handshake and can then call them by name with structured arguments. From the AI assistant's perspective, your CLI commands become first-class tools — no plugins, no wrappers, no special knowledge required beyond the schema you defined. ## Making it testable ![Constructor injection diagram showing FakeStreamChannel and MockMCPServer being injected into MCPCommand for testing — testability pattern for dart_mcp Dart CLI integration](/assets/images/blog/mcp-server-dart-cli/image6.avif) This architecture is testable because every layer accepts its dependencies as constructor parameters: - **MCPServer** accepts a `StreamChannel` and `CommandRunner` — in tests, pass an in-memory channel and a mock runner. - **MCPCommand** accepts a factory function for the server — in tests, inject a fake. ```dart // Example: testing the MCP command test('runs the MCP server', () async { final mockServer = MockMCPServer(); when(() => mockServer.done).thenAnswer((_) async {}); final command = MCPCommand( serverFactory: ({required channel}) => mockServer, ); final exitCode = await command.run(); expect(exitCode, ExitCode.success.code); }); ``` This means your MCP integration can have the same test coverage discipline as the rest of your CLI without requiring a live AI client. ## Configuring the MCP server for AI clients To make your MCP server discoverable by [Claude Code](https://www.anthropic.com/claude-code), add a configuration entry that tells it how to launch your CLI. Create a `.mcp.json` file in your project root: ```json { "mcpServers": { "my_cli": { "command": "my_cli", "args": ["mcp"] } } } ``` Once configured, Claude Code will automatically spawn your CLI process, complete the MCP handshake, and start calling tools as needed. Other MCP-compatible clients like [Cursor](https://www.cursor.com/) and [Gemini CLI](https://github.com/google-gemini/gemini-cli) follow a very similar setup. Because the MCP standard defines the configuration format, building once gives you compatibility with the full ecosystem — not just one client. ## The pattern in four lines Adding MCP to an existing Dart CLI requires four things: 1. **Create an `MCPServer`** subclass that mixes in `ToolsSupport` and registers tools during `initialize`. 2. **Define tools** with descriptive names, clear descriptions, and typed input schemas so AI assistants know how to call them. 3. **Delegate to your existing command runner** from tool handlers — don't rewrite business logic. 4. **Add an `mcp` command** that sets up the stdio transport and blocks until the connection closes. The pattern keeps the MCP layer thin. Your CLI commands remain the source of truth, and the MCP server is just a new interface to them. The goal isn't to build an AI interface — it's to build a good CLI. The MCP server is just evidence that you did. ### The Chemistry of GenUI Source: https://verygood.ventures/blog/the-chemistry-of-genui/ It turns out that [GenUI](https://github.com/flutter/genui) is full of atoms, molecules, and organisms. You just need to know where to look. One of the biggest challenges of GenUI is leaving behind the "old" mindset of designing and building "static" interfaces and user journeys in favour of providing building blocks for the LLM to assemble at runtime. What do you hand over to the LLM? Give it too little and you're barely using its power. Give it too much and you've surrendered control of your product's interface to a system that doesn't necessarily know what good design looks like. Before diving in, though, keep in mind the often unspoken hypothesis of GenUI: **The LLM can assemble in real-time an interface that is going to perform better than its static counterpart.** We'll come back to this. ## Atoms, Molecules, and Organisms in GenUI Drawing from [Atomic Design by Brad Frost](https://atomicdesign.bradfrost.com/chapter-2/), the components of a design system go from small to big: atoms, molecules, organisms and templates, all the way up to complete pages. ![__wf_reserved_inherit](/assets/images/blog/chemisty-genui/image3.png) So how big should the building blocks that you feed to the LLM? Our best guess at this point is that **most GenUI catalog items should hover between molecules and organisms.** Let's walk through the scenarios: - If you provide atoms to the LLM, you're giving it a lot of freedom and responsibility — but you might end up with a disjointed, Mr. Potato Head looking interface. You'll have little control over how the components are displayed on screen. - But if you swing all the way to the other side and provide complete pages to the LLM, you've already made all the decisions. There's little left for the LLM, which defeats the purpose of GenUI. The images below display a GenUI application in which the LLM is tasked with being a secondary school student based in the UK. For demo purposes, individual catalog items have been highlighted with dashed borders. In the first 2 images the LLM has been provided with a list of atoms: text, image and button. ![__wf_reserved_inherit](/assets/images/blog/chemisty-genui/image1.png) ![__wf_reserved_inherit](/assets/images/blog/chemisty-genui/image2.png) For the second 2 images, we have added a “student answer” molecule that requires the LLM to fill it with the student’s answer (text only), and 2 CTAs so the teacher can mark up or down the answer. ![__wf_reserved_inherit](/assets/images/blog/chemisty-genui/image4.png) ![__wf_reserved_inherit](/assets/images/blog/chemisty-genui/image5.png) Let’s review the differences: - In the atoms’ version there’re several individual catalog items on screen, as signaled by the components with dashed borders. In the molecules’ version there’s only 1 catalog item on screen. - In the atoms’ version the LLM decides pretty much everything: the text, the order in which the catalog items are rendered, the alignment and whether to display an image or not. In the molecules’ the layout inside the molecule is fixed. - In the atoms’ version the LLM chose to display feedback buttons. In the molecules’ version the LLM absolutely had to because the feedback buttons are part of the “student answer” molecule. So let's focus on molecules and organisms. > Molecules are relatively simple groups of UI elements functioning together as a unit. For example, a form label, search input, and button can join together to create a search form molecule. [...] Organisms are relatively complex UI components composed of groups of molecules and/or atoms and/or other organisms. These organisms form distinct sections of an interface. In the context of GenUI, a molecule catalog item for a search form guarantees consistency with your design and UX guidelines. **The LLM still has the power to decide when and whether to display it.** But if it does, it will look exactly as you want it to look. Could the LLM assemble on its own a perfectly designed search form? Could it design an even better one? Theoretically, yes. But can you risk it? ## From Design System to GenUI Catalog Going from a design system to a GenUI catalog — and deciding how much of it to expose to the LLM — is a balancing act that's going to take some trial and error. Expose too much and you risk the resulting interface looking like a disjointed rehash of your brand guidelines. Expose too little and you won't be making the most of the LLM's potential to adapt based on context. Please note that you not only have to decide which components to expose to the LLM — **you also have to decide which properties of each component the LLM is allowed to control**. For a Text component, does the LLM choose size? Alignment? Colour? For a Card organism, can it adjust padding, elevation, or the number of action buttons? Each exposed property is an additional degree of freedom — and the same "too much vs. too little" tension applies. Getting the catalog right isn't a one-off decision — it's an ongoing calibration as you learn how your LLM uses what you give it. Start with molecules and organisms as the default, watch what gets rendered and when, and adjust from there. ## Can GenUI Outperform Static Interfaces? Let's wrap up with the big question. **Can LLM-driven interfaces yield better metrics than the interfaces we've been designing and honing for decades?** As [we have written before](https://verygood.ventures/blog/what-is-genui-how-generative-user-interfaces-will-redefine-hospitality-in-2026/): *"GenUI adapts layouts, actions, and content as conditions change [...] The hard part isn't personalizing. It's doing it without overwhelming."* This is the power of GenUI and the reason we believe it has the potential to create interfaces and user journeys that perform better than static ones. Get in touch and let's get building! ### Behind the Magic: Meet Our Newest Unicorn Source: https://verygood.ventures/blog/behind-the-unicorn-magic-alessandra-valenti/ ![Alessandra Valenti, Head of Partnerships at Very Good Ventures](/assets/images/blog/behind-the-unicorn-magic-alessandra-valenti/main.jpg) Alessandra joined VGV this month as our new Head of Partnerships, working closely with the Business Development team. Born and raised in the United States and now based in Mexico City, she brings a rare mix of global perspective, strategic clarity, and a genuinely people-first approach to building partnership ecosystems where everyone can win. Throughout her career, Alessandra has worked at the intersection of technology, relationships, and impact across industries and geographies, building connections that grow stronger over time. Now, she's bringing that experience to VGV. We sat down with Alessandra to hear how she thinks about partnerships, what she's building alongside the BD team, and a few fun facts — including the story behind launching a telenovela in Mozambique. Keep reading to find out! ## On Partnerships **What separates a good partnership from a truly great one?** A truly great partnership is built on deep, mutual trust. Not the kind that needs to be stated in a contract, but the kind that's felt. It goes far beyond a formal referral agreement or a co-marketing clause. The best partnerships are synergistic in the truest sense: both companies are greater together than the sum of their parts, and that combined value proposition becomes so compelling that clients don't want to look anywhere else. What separates good from great is that implicit understanding: knowing instinctively what your partner needs, how they work, and what they stand for, without having to negotiate every interaction. There's a natural chemistry, an ease. You're genuinely top of mind for each other, not because you're obligated to be, but because the relationship is built on a shared commitment to serving clients better. A consistent pipeline of co-created opportunities, joint content, and shared wins are the byproducts of that deeper foundation. Not the foundation itself. **What do partners most want, but rarely get, from the companies they work with?** What partners truly crave but rarely find is genuine reciprocity. That elusive chemistry where the relationship has a natural flow, where showing up for each other feels effortless rather than transactional. When two companies come to market together and the result is something neither could have created alone, that's the wow factor partners are always chasing. The reason it's so rare is that most partnerships fail at the most basic level: both sides remain focused on their own individual agendas rather than building something mutually beneficial. True partnership requires a kind of ego dissolution, the willingness to prioritize the relationship over the deal. That's hard. But when you get it right, it's unmistakable. ## Role at VGV **What are you most excited to dig into in this role?** We're living through a moment where the rules are being rewritten in real time, and nobody fully knows what tomorrow will look like. That kind of uncertainty is exactly where I thrive. What excites me most is being at the intersection of that change. At VGV, we're pushing the boundaries of what's possible with AI and Flutter, exploring how GenUI can fundamentally transform the way people experience digital products. The idea that a UI could be generated, adapted, and personalized in real time based on context and behavior isn't science fiction anymore. It's what we're building today, and the early results in our pipeline speak for themselves. Beyond the technology, I'm equally excited about building the partnerships function itself: creating a structured ecosystem of complementary companies that each make VGV's offering stronger, and make our clients' outcomes better. The goal is to build something that compounds over time. Relationships and co-created assets that outlast any single deal or campaign, and that position VGV as the center of gravity in the Flutter and AI-powered digital experience space. ## Who is Alessandra Beyond VGV? **What's something surprising people might not know about you?** I once helped launch Mozambique's first telenovela. It came out of research I was doing with teenagers there. We discovered that deeply entrenched misinformation about health was nearly impossible to dislodge through conventional campaigns. But teenagers were already watching Brazilian soap operas religiously, modeling their understanding of relationships on those characters. So instead of fighting for attention, we went where they already were. We gave them a Mozambican version with local faces and local stories, and it worked. It's still the clearest example I have of a principle I carry into every partnership: don't ask people to enter your world. Find a way into theirs. **What do you do outside of work that shapes how you think professionally?** I love to travel, not to the easy places, but to the ones that challenge you. Myanmar, Uganda, Mozambique, Laos, Cambodia. I seek out communities where I don't speak the language, where I don't share the cultural reference points, where I have to find connection through presence rather than words. Over time, that's made me unusually good at building relationships across difference: reading people, finding common ground, earning trust quickly. In partnerships, that skill is everything. The ability to walk into a room with someone whose world looks nothing like yours and leave with a genuine relationship isn't something you learn in a boardroom. You learn it by showing up somewhere unfamiliar and choosing to stay curious rather than comfortable. --- If your company is building multiplatform digital experiences with AI or Flutter, and you believe the best outcomes are built in partnership, we'd love to connect. Reach Alessandra directly at [alessandra.valenti@verygood.ventures](mailto:alessandra.valenti@verygood.ventures). ### Your Business Doesn't Fit on a Phone. Neither Should Your Flutter App Source: https://verygood.ventures/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app/ ## One of our own is taking the stage in Austin. Marcus Twichel (GDE for Flutter/Dart, Tech Lead at VGV) is speaking at the Flutter Austin & GDG Austin joint meetup on March 30th. **His talk: "Your Business Doesn't Fit on a Phone — Neither Should Your Flutter App"** Most Flutter apps are built for iOS and Android. But the businesses we serve run on dozens of screens. Marcus will live-demo a full coffee shop platform running across eight screens from a single Flutter codebase — with real-time sync between every device — then walk through the code that makes it work. If you're in Austin, this one is worth your Monday night. - **Where:** Voltron Room, Capital Factory - **When:** March 30th | Doors at 6:30 PM CST - **Cost:** Free — [RSVP here](https://www.meetup.com/flutter-austin/events/313641364/) ![Flutter Austin and GDG Austin joint meetup event graphic](/assets/images/blog/your-business-doesnt-fit-on-a-phone-neither-should-your-flutter-app/main.avif) ### How to Write a Mobile RFP That Actually Gets You the Right Agency Source: https://verygood.ventures/blog/how-to-write-a-mobile-app-rfp/ ###### You've secured a budget for a mobile app. Leadership is aligned. Now you need to find the right development partner — and that means writing a Request for Proposal. **Here's the problem:** most mobile RFPs fail before a single agency reads them. They're either so vague that every response looks the same, or so prescriptive that they scare off the partners best equipped to challenge your assumptions. After years of reviewing and responding to RFPs at Very Good Ventures, we've seen both extremes — and everything in between. This guide will help you write an RFP that attracts serious partners, surfaces meaningful differences between proposals, and sets your project up for success from day one. #### Start With the Problem, Not the Solution The strongest RFPs lead with business context. Before you describe screens, features, or integrations, tell prospective partners *why* this app exists. What business problem does it solve? What does success look like in six months? In two years? A good problem statement might read: "Our field technicians currently use paper forms that take 48 hours to process. We need to cut that to real-time." That single sentence tells an agency more than a 40-page feature list. It reveals the user, the pain point, the urgency, and the success metric — all in one breath. Agencies worth hiring will design better solutions when they understand the underlying problem. Agencies that just want a spec to execute against will build exactly what you asked for, whether it's the right thing or not. #### Define Your Users Before Your Features Every mobile app serves someone. The RFP should describe those people clearly: who they are, what devices they carry, where and how they'll use the app, and what technical literacy you can expect from them. Field workers on rugged Android tablets in spotty cell coverage demand a fundamentally different architecture than corporate executives reviewing dashboards on the latest iPhone. Offline-first data sync, camera and sensor integration, accessibility requirements, internationalization — these constraints shape every technical decision downstream. Surface them early so agencies can respond with realistic approaches instead of optimistic guesses. #### Include What You Know (and Admit What You Don't) The best RFPs are honest inventories of certainty and uncertainty. Be specific where you can: **Existing systems and integrations.** Name your ERP, your CRM, your authentication provider. Call out REST vs. GraphQL, API versioning, known rate limits. If an agency needs to interface with a legacy SOAP service from 2008, say so now — that detail will materially affect estimates. **Compliance and security requirements.** HIPAA, SOC 2, GDPR, FedRAMP — these aren't afterthoughts. They shape architecture, hosting, CI/CD, and testing strategy. An agency that discovers compliance requirements mid-project will burn weeks and budget adapting. **Design maturity.** Do you have a design system, Figma files, or brand guidelines? Or does the agency need to start from research and wireframes? The gap between "we have production-ready designs" and "we have a napkin sketch" is easily a six-figure difference in scope. **Timeline and budget range.** Agencies cannot give you a meaningful proposal without understanding your constraints. You don't need to state an exact number, but a range — or at minimum, an order of magnitude — prevents everyone from wasting time. If your budget is $200K and the project requires $1.5M, both sides benefit from knowing that upfront. Where you lack clarity, say so directly. "We haven't decided on a backend provider" or "We're open to recommendations on analytics tooling" gives agencies room to demonstrate expertise. Uncertainty isn't a weakness in an RFP — hidden uncertainty is. #### Address the Platform Question Head-On One of the most consequential decisions in any mobile RFP is platform strategy: native, cross-platform, or web-based. Your RFP should state your current thinking and invite agencies to challenge it. **Native development** (Swift/Kotlin) delivers maximum platform fidelity and access to the latest OS features on day one. The trade-off is maintaining two separate codebases, two teams, and two release cycles. For apps that push hardware boundaries — AR, complex animations, deep OS integration — native still makes sense. For most business applications, though, the overhead is hard to justify. **React Native** popularized the cross-platform approach with JavaScript and a bridge to native components. It has a large community and an enormous ecosystem of third-party packages. However, that bridge architecture introduces performance bottlenecks for complex UIs, and debugging often requires digging through native layers anyway. The reliance on third-party packages for core functionality can also create long-term maintenance risk. **Flutter** takes a different approach entirely. Instead of bridging to platform UI components, Flutter renders every pixel directly through its own high-performance engine (Impeller on iOS, Skia and Impeller on Android). This gives development teams pixel-perfect control across platforms without platform-specific rendering inconsistencies. A single Dart codebase compiles to native ARM code — not interpreted, not bridged — which means performance characteristics closer to native than any bridge-based framework can achieve. Flutter's advantages compound over the lifecycle of a project. A single codebase means one team, one set of tests, one CI/CD pipeline, and simultaneous releases on iOS and Android. Google maintains the framework and its core packages, so teams spend less time chasing community-maintained dependencies. The widget-based architecture makes UI composition predictable and testable. Hot reload keeps development velocity high. And Flutter's reach now extends beyond mobile to web, desktop (macOS, Windows, Linux), and embedded devices — meaning the investment in a Flutter codebase can scale to new surfaces without starting over. At VGV, we've built Flutter applications across industries — from consumer apps with millions of users to enterprise tools running on embedded hardware. The framework's maturity, Google's continued investment, and the growing ecosystem make it the strongest cross-platform choice for most mobile projects today. Your RFP doesn't need to mandate a technology. But if you state your platform preferences and invite agencies to propose alternatives with rationale, you'll learn a great deal about how each partner thinks. ![__wf_reserved_inherit](/assets/images/blog/how-to-write-a-mobile-app-rfp/body-0.jpg) #### Structure Your RFP for Comparison When three or five agencies respond to your RFP, you need to compare them meaningfully. Structure helps. Ask every respondent to address the same core areas: **Technical approach.** How will they architect the application? What frameworks, state management patterns, and backend services do they recommend, and why? **Team composition.** Who will work on your project? What are their roles and relevant experience? Will the team be dedicated or shared across clients? **Process and communication.** How do they run sprints? How will you see progress? What does their QA process look like? How do they handle scope changes? **Timeline and milestones.** Break the project into phases with deliverables. Avoid agencies that give you a single end date and nothing in between. **Pricing model.** Fixed bid, time-and-materials, or hybrid? Each has trade-offs, and the right model depends on how well-defined your scope is. Ask agencies to explain their recommendation. **Risk identification.** The best partners will tell you what might go wrong. An agency that identifies no risks in a complex mobile project is either inexperienced or not being candid. #### Set a Realistic Evaluation Timeline Give agencies enough time to respond thoughtfully — two to three weeks minimum for a meaningful proposal. Rush timelines produce shallow responses and favor agencies with excess capacity rather than the best fit. On your side, commit to a decision timeline and communicate it. Nothing damages a client-agency relationship faster than a four-month evaluation silence followed by "can you start Monday?" #### A Few Things That Separate Good RFPs From Great Ones **Name a single point of contact.** Agencies need someone to ask questions. Ambiguity about who owns the relationship creates friction before the project even begins. **Share evaluation criteria.** If you weigh technical expertise at 40%, cost at 30%, and cultural fit at 30%, say so. Agencies will tailor their responses to what you actually care about. **Allow (and encourage) questions.** A Q&A period between RFP release and response deadline improves every proposal. Publish answers to all participants so the playing field stays level. **Be open about incumbents.** If you have an existing vendor and this RFP is a genuine evaluation, say so. If the incumbent has an inside track, ethical agencies will appreciate the honesty — and may still compete if the opportunity is real. #### The RFP Is the Relationship's First Test How you write your RFP signals how you'll run the project. Clear communication, honest constraints, and structured expectations attract partners who operate the same way. Vague requirements, hidden agendas, and unrealistic timelines attract partners willing to tell you what you want to hear. Write the RFP you'd want to receive, and the right agency will find you. *Very Good Ventures is a leading Flutter development consultancy. We partner with organizations to build multi-platform applications with a single codebase, from mobile to web to embedded. If you're preparing an RFP and want to talk through your approach,* [*reach out*](https://verygood.ventures/contact-us/) *— we're happy to help even before the formal process begins.* ### What is Nostr? The Decentralized Protocol for Developers Source: https://verygood.ventures/blog/what-is-nostr/ *This is Part 1 of a two-part series. [Part 2](/blog/building-on-nostr-real-engineering-challenges/) covers the real-world challenges of building production applications on Nostr.* ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-0.png) *A common logo used to represent Nostr, by Andrea Nicolini* When our team joined the [diVine](https://divine.video/) project—a decentralized short-form video app—we thought we knew how to build networked applications. Then [Nostr](https://github.com/nostr-protocol/nostr) asked us to rethink what a server even does. ## Smart Clients, Dumb Servers Most architectures you’ve shipped follow a familiar pattern: the server is the brain. It authenticates users, stores data, enforces business logic, resolves conflicts, and decides what each client sees. The client renders what it is told. [Nostr inverts this.](http://github.com/nostr-protocol/nostr) The relay—Nostr’s version of a server—is intentionally simple. It accepts events, stores them, and forwards them to subscribers. The relay does not resolve conflicts between data versions and does not decide what a client should see. Some relays support authentication, but by default there is no login — the relay is a pipe with a filter. The client, by contrast, does all the heavy lifting. It generates and manages cryptographic keys, decides which relays to connect to, assembles a coherent view of the world from multiple relay responses, handles data reconciliation when relays disagree, and manages the user’s social graph locally. In Nostr, the intelligence lives on the device. This is the single most important thing to understand before writing a line of Nostr code. If you come from a traditional backend mindset where the server is authoritative, you will need to shift your mental model. The client is not a thin layer over an API—it is the application. That architectural inversion starts with the most fundamental question: who are you on the network? ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-1.png) ## Identity Without Accounts On Nostr, your identity is your key pair. Your public key is your username. Your private key proves you are you. No registration step, no email verification, no username collision. Most Nostr clients generate this key pair for you on first launch and store it locally unless you choose to export or back it up. Under the hood, keys use secp256k1 (the same elliptic curve Bitcoin uses) and are encoded as npub/nsec strings via [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md). That means identity is portable. Your key pair works on any Nostr client, on any relay, forever. Nobody can deplatform you by deleting your account because there is no account to delete. Your identity exists independently of any infrastructure. Nothing prevents a single person from creating multiple key pairs. Some users operate several identities: a public persona, an anonymous account, one for experimentation, one for a project or organization. Because keys are cheap to generate and require no registration, identity on Nostr is fluid and composable rather than singular and fixed. It is also unforgiving. There is no “forgot password” flow. If you lose your private key, your identity is gone. If someone gains access to your private key, they can impersonate you and there is no authority to appeal to. The ecosystem has responded with tools that soften this edge. [Browser extension signers](https://github.com/nostr-protocol/nips/blob/master/07.md) keep your private key secure rather than pasted into every client. [Mobile signer apps](https://github.com/nostr-protocol/nips/blob/master/55.md) do the same, letting applications request signatures without ever touching the raw key. These tools do not eliminate the trade-off between self-sovereignty and user safety—they shift where the trust boundary sits. We dig into these trade-offs in [Part 2](/blog/building-on-nostr-real-engineering-challenges/). ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-2.jpeg) ## Events: The Only Data Type In most protocols, you deal with multiple object types: users, posts, messages, reactions, and profiles. Nostr has exactly one: [the event](https://github.com/nostr-protocol/nips/blob/master/01.md#events-and-signatures). Every piece of data on the network is a JSON object with the same fields—an identifier, the author’s public key, a timestamp, a kind integer that tells clients how to interpret it, a tags array for references and metadata, a content string, and a cryptographic signature proving the author wrote it. That is the entire data model. A profile update, a text post, a follow list, a direct message, and a video event all share this exact structure. What differs is the kind number and how clients interpret content and tags. This uniformity means relays do not need to understand what they are storing—a relay built for text notes will happily forward video events without any code changes. ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-3.jpeg) ## How Events Move: Relays And WebSockets Anyone can run a relay. There is no central registry or approval process. Some relays are public and free to use, while others are private, community-run, invite-only, or charge fees. Relay operators choose what events they will accept, how long they will store them, and what moderation policies they enforce. A client connects to one or more relays and communicates using a simple JSON message protocol defined in NIP-01, the foundational specification. The client can do three things: 1. **Publish** — send a signed event to the relay for storage and distribution 2. **Subscribe** — request events matching a filter (by author, by kind, by tag, by time range) 3. **Close** — end a subscription No endpoints for user registration, no authentication tokens, no rate-limit headers (though relays may enforce their own limits). The relay either accepts your event or it does not. A critical design choice: relays generally do not talk to each other. The protocol defines no gossip layer, no consensus mechanism, no peer-to-peer relay mesh. Some relay implementations do sync with each other, but this is not common and not something clients can rely on. In practice, if you publish an event to Relay A, you should assume Relay B does not have it unless a client also sends it there. ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-4.jpeg) ## NIPs and Event Kinds Rather than a top-down standards body, Nostr evolves through NIPs—Nostr Implementation Possibilities. Each NIP proposes a convention for how clients and relays handle specific use cases. The kind integer in every event is what makes this work: kind 0 is user metadata, kind 1 is a text note, kind 3 is a follow list, kind 10002 is relay list metadata. Different kinds, same event structure, same relays. A few [NIPs](https://github.com/nostr-protocol/nips) to know when starting out: NIP-01 defines the core protocol flow, NIP-19 gives you the human-readable encodings (npub, nsec, nevent), and NIP-65 defines how users advertise which relays they use—the backbone of relay discovery. NIP-55 and NIP-07 cover signer app integration for keeping private keys off devices running untrusted client code. The NIP system is what makes Nostr extensible without coordination. Because event kinds are just integers and relays forward events they do not understand, new functionality can be added without upgrading the network. When the diVine project needed to handle video events, we chose a kind range, documented the content structure, and shipped. No relay upgrades required. ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-5.jpeg) ## The Event Lifecycle A user generates a key pair locally—their public key is now their identity on the network. They compose a text note, and their client creates a kind 1 event, populates the fields, and signs it with the private key. The client then publishes the signed event to the user’s preferred relays (advertised in their relay list event). Another user’s client, subscribed to events from that public key, receives the event from one of those relays. It verifies the Schnorr signature, confirms the event has not been tampered with, and renders it. No server decided these users could interact. No platform approved the content. No algorithm ranked it. The relay just moved bytes. ![__wf_reserved_inherit](/assets/images/blog/what-is-nostr/body-6.png) ## What This Means For Builders The architecture has immediate practical implications: **Your client is the product.** On traditional platforms, two clients using the same API look and feel roughly the same because the server shapes the experience. On Nostr, two clients connecting to the same relays can offer radically different experiences. The client decides what to show, how to rank it, which relays to trust, and how to handle conflicts. This is simultaneously more freedom and more responsibility. **Local state matters.** Because relays can disagree and go offline, your client needs robust local persistence. You are building an eventually consistent system, not a request-response API consumer. **The hard problems are client-side.** Key management, relay discovery, data reconciliation, spam filtering, content moderation—all of these are client responsibilities. Part 2 of this series is dedicated to these challenges because they are where the real engineering happens. **The ecosystem is interoperable.** Anything your app publishes is visible to every other Nostr client. A user’s social graph, follows, and profile travel with them across apps. You do not need to build network effects from zero—you inherit them. ## What Comes Next Now you have the foundation: one data type, dumb relays, smart clients, and cryptographic identity. The protocol is small enough to hold in your head and flexible enough to build almost anything on top of. That combination is rare—and it is what makes Nostr worth taking seriously as an engineering decision, not just a philosophical one. That flexibility comes at a cost. In [Part 2](/blog/building-on-nostr-real-engineering-challenges/), we get into what it is actually like to build on Nostr: the relay discovery problem, the key management UX wall, data consistency across relays, and the trade-offs we encountered working on diVine. That is where the protocol’s elegance collides with real-world complexity. ### Get Ready for the Flame Game Jam 2026 Source: https://verygood.ventures/blog/get-ready-for-the-flame-game-jam-2026/ When we built [Pinball I/O](https://pinball.flutter.dev/) and [Super Dash](https://superdash.flutter.dev/), some of the most valuable lessons came not from the final product, but from the constraints we worked within — tight timelines, creative tradeoffs, and the pressure of shipping something that actually feels good to play. That's exactly the kind of challenge a game jam delivers, compressed into a few intense days. The [Flame Game Jam 2026](https://itch.io/jam/flame-game-jam-2026) runs from March 6–15, and this year's edition comes with serious prizes: a [Mecha Comet](https://mecha.so/comet) device and tickets to [Flutter & Friends](https://www.flutterfriends.dev/) (among others). Whether this is your first jam or your tenth, here's what we've learned about making the most of it. ## Scope Small This is the single most repeated piece of advice in game jam circles, and for good reason — we've seen it play out firsthand. You will have dozens of ideas. You will want to build your dream game. The reality is that you have a handful of days, and ambition is the enemy of a finished product. Think less in quantity, not in quality. A short, polished game with tight controls and a clear loop will always outperform an ambitious prototype that feels unfinished. Cut features early and often. The games that win jams are the ones that feel complete. ## Be Smart About the Theme Most game jams announce a theme when the event starts — something as concrete as "Airplanes" or as abstract as "The passage of time." Your interpretation of that theme is where you stand out. The best entries we've seen strike a balance: creative enough that judges notice the originality, clear enough that players can see the connection. Avoid the two extremes — a literal interpretation that dozens of other teams will also land on, or a stretch so abstract that nobody sees the theme in your game. When in doubt, find where the literal meets the metaphorical. ## Know Where to Find Good Resources If you're not an artist or musician and haven't found a teammate to cover those roles, you can still ship something that looks and sounds great. Free asset libraries have come a long way, and two resources stand out: - [Kenney's asset library](https://kenney.nl/assets)  - [itch.io game assets](https://itch.io/game-assets)  Spending an hour upfront browsing assets can shape your entire game concept. Let the available art inspire your design rather than fighting to create custom assets under time pressure. ## Read the Rules, Watch the Deadline It happens every jam: a team gets so deep into building that they miss a rule or blow past the submission deadline. Read the rules before you write a line of code, and plan to submit at least a couple of hours early. Buffer time isn't wasted time — it's insurance against last-minute build failures, upload issues, and the inevitable "one more fix" spiral. ## Have Fun! Above everything else, a game jam is a community event. The best ones produce friendships and creative breakthroughs, not just games. It's easy to slip into pressure mode, especially with prizes on the line, but treating the jam as a competition first and an experience second will spoil it. Build something you're proud of. Learn something new. Meet people who share your excitement for game development. ## We'll Be Watching We're looking forward to seeing what the community builds this year. The Flame ecosystem keeps growing, and game jams are where some of the most creative applications of the engine show up. If you join and build something, share it with us — we want to play it. ### Flutter GenUI Tutorial: Build an AI Shopping Assistant Source: https://verygood.ventures/blog/flutter-genui-shopping-assistant-tutorial/ Flutter developers have done a lot to integrate AI into their apps, and somehow the best most of us have managed is replacing a search bar with a chatbot. The user asks a question, and the AI responds with a wall of text. That is not the experience anyone envisioned when they first heard "AI-powered app." When I walked through this problem at the [Flutter SF Meetup hosted by SoFi](https://luma.com/5uzi5ws4?tk=W5IuOR) recently, the reaction confirmed what we have been seeing in our own projects at VGV: Flutter developers are ready to move past plain-text AI responses and into real, interactive interfaces that the AI itself assembles. At VGV, we have been building AI-powered Flutter experiences since before GenUI existed as a library. Projects like [Ask Dash, a generative AI Flutter app we built with Google](https://verygood.ventures/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/), taught us the pattern early. When we saw the Flutter GenUI SDK, we recognized it immediately: this is how you go from an LLM that talks to one that build. ![__wf_reserved_inherit](/assets/images/blog/flutter-genui-shopping-assistant-tutorial/body-0.png) *Image taken by Frank van Puffelen (puf) during the Flutter SF Meetup hosted by SoFi.* This post is the beginner-friendly companion to that talk. You will get just enough context to understand what GenUI is, and then we will go straight into building a working shopping assistant with it. The full source is available in the [genui_shopping_assistant repository on Github](https://github.com/VGVentures/genui_shopping_assistant); clone it now if you want to follow along. ## Why Text Responses Are Not Enough Most AI-powered apps today respond with plain text. A user asks "show me running shoes under $150" and gets a wall of text listing options. GenUI flips this: instead of text, the AI assembles real, interactive Flutter widgets (product cards, filters, carousels) on the fly, using your own design system. The user sees a rich UI, not a chat log. ![__wf_reserved_inherit](/assets/images/blog/flutter-genui-shopping-assistant-tutorial/body-1.png) *GenUI Flutter shopping assistant — text response vs. interactive product cards generated by GenUI* Before we build anything, it helps to understand the limitation GenUI is designed to fix. ## What Is The Flutter GenUI SDK? At its core, GenUI does one thing: > GenUI is a Flutter library that lets an AI build your app's UI in real time, using your widgets, your brand rules, and the user's context and intent to power the experience. Three concepts are all you need to get started: ### 1. You Define A Widget Catalog The AI can only compose from widgets you have explicitely registered: product cards, buttons, filters, and so on. Your brand stays intact. This is the key insight: the catalog is the contract between your app and the AI. ### 2. The AI Assembles The UI As JSON The LLM does not generate Dart code. It returns structured data that GenUI deserializes into real Flutter widgets at runtime. This is the mechanism that powers the A2UI [(Agent-to-UI) protocol,](https://github.com/flutter/genui) a structured contract where the AI describes intent, and the client renders it natively. ![__wf_reserved_inherit](/assets/images/blog/flutter-genui-shopping-assistant-tutorial/body-2.png) *Flutter GenUI SDK — AI-generated JSON deserialized into interactive Flutter widgets at runtime* ### 3. Interactions Feed Back To The AI When the user taps, selects, or types, that state goes back to the LLM, which can refine the UI. This loop is what makes GenUI *generative* rather than just *dynamic* ![__wf_reserved_inherit](/assets/images/blog/flutter-genui-shopping-assistant-tutorial/body-3.png) *GenUI Flutter interaction loop — user taps trigger state updates sent to the LLM, which refines the generative UI* You can explore these concepts in depth in the [official GenUI SDK documentation](https://docs.flutter.dev/ai/genui) and the [GenUI SDK components and concepts reference](https://docs.flutter.dev/ai/genui/components). Note that the SDK is experimental and the API is still evolving. Treat it accordingly, and get yourself ready for breaking changes in future releases. For a deeper look at how Flutter GenUI is being applied to real-world products, see how we are [redefining the digital booking experience with Generative UI at VGV](https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/). ## Building The Shopping Assistant: A Step-By-Step Flutter GenUI Tutorial The best way to understand how these three pieces fit together is to build something real. Here is how to get the shopping assistant running. ![__wf_reserved_inherit](/assets/images/blog/flutter-genui-shopping-assistant-tutorial/body-4.png) *A screenshot of the Shopping Assistant demo powered by GenUI* ### Prerequisites Before you start, make sure you have: - **Flutter SDK in the latest stable version:** the *genui* package requires Dart 3.9 or later, shipped with Flutter 3.35. - A **Firebase project** with [**Firebase AI Logic**](https://firebase.google.com/products/firebase-ai-logic) enabled. While you can run this experience with a *Spark* free plan, a *Blaze* plan is recommended, as the token consumption limit will be more generous. - Clone the V[GV genui_shopping_assistant repository from Github](https://github.com/VGVentures/genui_shopping_assistant), run *flutter pub get,* and finish the connection with Firebase running *flutterfire configure.* **Note on API stability:** the *genui package is in alpha (v0.7.0 when this article is posted). The API is subject to breaking changes between releases. This is expected for an experimental SDK at this stage. Check the* [*flutter/genui repository*](https://github.com/flutter/genui) *for the latest release notes before starting****.*** ### Define Your Widget Catalog A **Catalog** is a collection of **CatalogItem** objects that tells the AI exactly which widgets it can compose. Think of it as the contract between your app and the AI: if a widget is not in the catalog, the AI cannot use it. Your design system is enforced at runtime. Each **CatalogItem** requires three things: a **name,** a **dataSchema** (defined with [json_schema_ builder](https://pub.dev/packages/json_schema_builder)) that tells the AI what data the widget expects, and a **widgetBuilder** function that turns data into a real Flutter widget. Here is a simplified product card entry from the shopping assistant: ```dart import 'package:genui/genui.dart'; import 'package:json_schema_builder/json_schema_builder.dart'; // The schema tells the AI what data a ProductCard expects. // Mark required fields explicitly so the AI produces valid data. final _productSchema = S.object( properties: { 'name': S.string(description: 'Product display name.'), 'price': S.number(description: 'Price in USD.'), 'imageUrl': S.string(description: 'URL of the product image.'), }, required: ['name', 'price', 'imageUrl'], ); final productCard = CatalogItem( name: 'ProductCard', dataSchema: _productSchema, widgetBuilder: ({ required data, required id, required buildChild, required dispatchEvent, required context, required dataContext, }) { final json = data as Map; // Build and return your ProductCard widget here, // using json['name'], json['price'], json['imageUrl']. }, ); ``` To register this item alongside the built-in GenUI core widget, pass it to **A2uiMessageProcessor:** ```dart _processor = A2uiMessageProcessor( // CoreCatalogItems includes GenUI's built-in widgets. // copyWith appends your custom items without replacing the core set. catalogs: [CoreCatalogItems.asCatalog().copyWith([productCard])], ); ``` Now that the catalog is defined, we need to connect it to an LLM. ### Set Up The LLM Connection The **ContentGenerator** is the bridget between your Flutter app and the AI. It decouples your app from a specific LLM provider, which matters when the model landscape is moving fast. For this tutorial, we will use **FirebaseAiContentGenerator**. ```dart /// Creates and configures a [FirebaseAiContentGenerator] backed by Firebase AI /// (Gemini 2.5 Flash). /// /// The catalog schema is automatically sent to the AI via tool definitions. /// History and state management are handled by [GenUiConversation]. FirebaseAiContentGenerator buildFirebaseAiContentGenerator() { return FirebaseAiContentGenerator( catalog: shoppingCatalog, systemInstruction: shoppingSystemInstructions + GenUiPromptFragments.basicChat, ); } ``` ![__wf_reserved_inherit](/assets/images/blog/flutter-genui-shopping-assistant-tutorial/body-5.png) *Flutter GenUI ContentGenerator — GoogleGenerativeAiContentGenerator and FirebaseAiContentGenerator bridging Flutter and the LLM* With the LLM connected, it is time to wire everything together. ### Wire It Together And Run The final step is creating a **GenUiConversation**, connecting it to the processor and content generator, and embedding a **GenUiSurface** in your widget tree. **GenUiConversation** is the primary facade that manages the back and forth between your app and the AI. ```dart @override void initState() { super.initState(); _processor = A2uiMessageProcessor( catalogs: [CoreCatalogItems.asCatalog().copyWith([productCard])], ); _conversation = GenUiConversation( a2uiMessageProcessor: _processor, contentGenerator: contentGenerator, // Track which UI surfaces the AI creates and removes. onSurfaceAdded: (update) => setState(() => _surfaceIds.add(update.surfaceId)), onSurfaceDeleted: (update) => setState(() => _surfaceIds.remove(update.surfaceId)), ); } @override void dispose() { // Always dispose the conversation to avoid resource leaks. _conversation.dispose(); super.dispose(); } // In your build method, render each surface: GenUiSurface( host: _conversation.host, surfaceId: surfaceId, ) // To send a user prompt: _conversation.sendRequest( UserMessage.text('Show me running shoes under \$150'), ); ``` Launch the app, type a prompt like *"Show me running shoes under $150",* and watch GenUI assemble a product browsing experience from your catalog widgets. The AI reads your catalog, generates a JSON layout referencing **ProductCard** and other registered widgets, and GenUI deserializes it into your actual Flutter components. Check the [genui package on pub.dev](https://pub.dev/packages/genui) for the current version and changelog. ## Where To Go From Here You just built a working AI shopping assistant that renders real Flutter widgets from natural-language prompts. No chat bubbles, no text walls — just an actual product browsing experience composed by the AI from your own design system. Here are a few directions to explore next: - **Extend your catalog:** try adding a price filter chip, a product comparison view, or a checkout summary widget. Each new *CatalogItem* expands what the AI can compose, and because the catalog is the contract, your brand controls stay intact. - **Try a different LLM provider:** implement a custom *ContentGenerator* that connects to a different model. The abstraction is there specifically because the model landscape will keep changing. - **Explore the official docs:** the [GenUI SDK getting started guide](https://docs.flutter.dev/ai/genui/get-started) covers advanced topics including the A2UI protocol, streaming AI updates, and custom *ContentGenerator* implementations. - **See GenUI in a production context:** read how [VGV is redefining the digital booking experience with generative UI](https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/), or explore our [GenUI solutions resource page](https://verygood.ventures/resources/genui/) for a business-oriented perspective on what this technology enables across retail, travel, banking, and QSR. This kind of problem (making AI a native part of your product's UX layer, not just a featured bolted on) is exactly where we focus at VGV. We have shipped [AI-powered Flutter experiences with Google](https://verygood.ventures/blog/the-shortest-path-to-ai-everywhere-is-flutter/) across industries, and we have been working with Flutter since its early days. If you are thinking through what GenUI means for your product, [we would be glad to think through it with you](https://verygood.ventures/contact-us/). ### Top Companies Using Flutter in 2026 [Updated] Source: https://verygood.ventures/blog/top-companies-using-flutter/ ![Top companies using Flutter in 2026 — enterprise ecosystem spanning financial services, automotive, media, retail, and technology industries](/assets/images/blog/top-companies-using-flutter/body-0.jpeg) *Last updated: February 2026* When engineering leaders are considering Flutter for their new initiatives, they often ask us the same question: "Who else is using Flutter in production at scale?" It's the right question. When you're evaluating a framework for a mission-critical app, you want to know who's already bet on it and what they've learned. BMW, Google Pay, Nubank, and Alibaba each serve 50-100+ million users on Flutter, and we've tracked over 90 enterprise companies using Flutter in production between 2024 and 2026. Below, we've organized the most notable Flutter app examples by industry, so you can see how companies in your vertical are putting it to work. ## Financial Services & Insurance ![Flutter banking and fintech apps — Google Pay, Nubank, SoFi, Betterment, and 20+ enterprise companies using Flutter in financial services](/assets/images/blog/top-companies-using-flutter/body-1.jpeg) Financial services is the largest vertical for Flutter adoption, with 20+ confirmed companies. We've worked with several of them firsthand, and the pattern is consistent: regulated industries need pixel-perfect consistency across platforms, fast iteration cycles to respond to compliance changes, and the ability to unify fragmented mobile teams. Flutter delivers on all three, making it [**especially well-suited to banking and fintech**](https://verygood.ventures/blog/why-mobile-first-fintech-companies-are-betting-on-flutter/). ### Google Pay Google rebuilt [**Google Pay**](https://flutter.dev/showcase/google-pay) in Flutter, migrating from 1.7 million lines of separate iOS and Android code to a unified codebase. A proof of concept built by just three engineers scaled to a 150+ engineer effort, reducing feature development effort from 2x to roughly 1.2x and resulting in a 35% smaller codebase. ### Nubank The largest independent digital bank outside Asia, [**Nubank**](https://flutter.dev/showcase/nubank) serves [**over 100 million customers**](https://international.nubank.com.br/100m/nubank-surpasses-100-million-customers/) on a Flutter-powered app. After adopting Flutter, the company launched a new life insurance product in just three months — their fastest product launch ever — and saw a [**30% improvement**](https://building.nubank.com.br/scaling-with-flutter/) in merge success rates. [**Hear from Nubank's engineering team on the VGV podcast.**](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/) ### SoFi SoFi built its "financial super app" — combining banking, investing, budgeting, credit, loans, and insurance — entirely in Flutter, with 2.5+ million lines of Flutter code making it one of the largest Flutter codebases in production. The company ships weekly and has invested heavily in [**AI-driven workflows to scale its growing engineering team**](https://verygood.ventures/blog/how-sofi-scales-mobile-engineering-with-flutter-and-ai/). ### Betterment [**Betterment**](https://flutter.dev/showcase/betterment), the online investment advisor managing $56+ billion in assets, [**completed a Flutter transformation in collaboration with VGV**](https://verygood.ventures/success-stories/betterment/) using Flutter's add-to-app functionality before migrating completely. [**Betterment's engineering team has described how Flutter revitalized their codebase.**](https://verygood.ventures/blog/betterment-flutter-revitalized-our-codebase/) [**Hear from Betterment's Sam Moore on the VGV podcast.**](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) ### GEICO GEICO adopted Flutter to [**reduce duplicative work and ensure consistent branding**](https://www.geico.com/techblog/flutter-as-the-multi-channel-ux-framework/) across iOS, Android, and web. ### Credit Agricole Bank Polska [**Credit Agricole Bank Polska**](https://flutter.dev/showcase/credit-agricole) launched its CA24 Mobile banking app in Flutter in May 2022, built by a team of 250 people including nearly 30 Flutter developers who delivered over 165 features within 11 months. ### Virgin Money [**Virgin Money**](https://flutter.dev/showcase/virgin-money) unified its mobile banking experience across iOS and Android with Flutter, announced at Google I/O 2024 as a showcase for how established banks are modernizing their mobile strategies. ### Visa Visa built and open-sourced [**Nova Flutter**](https://pub.dev/packages/visa_nova_flutter), a comprehensive library of accessible UI components aligned with the Visa Product Design System, enabling developers worldwide to build consistent, accessible payment experiences. ### Square (Block) Square provides open-source Flutter plugins for its In-App Payments SDK and Mobile Payments SDK, enabling Flutter developers to integrate Square payment processing with support for Google Pay and Apple Pay from a single codebase. ### Skandia [**Skandia**](https://flutter.dev/showcase/skandia), a leading Swedish insurance and banking provider, is featured on the official Flutter showcase for its cross-platform financial services apps. ### Nationwide Nationwide Insurance uses Flutter for its flagship customer app, enabling customers to pay bills, file claims, manage auto/home/pet policies, and access digital ID cards across iOS and Android. ### Amica Insurance Amica Mutual, the oldest mutual insurer of automobiles in the U.S., uses Flutter for its mobile app, which lets policyholders report and track claims, pay bills, access digital ID cards, and request 24/7 roadside assistance. ### Also using Flutter in financial services: Huntington National Bank, Islandsbanki, Bread Financial, Bank of America (merchant services), Prudential Services Singapore, Trust & Will (estate planning), Tide (UK challenger bank for SMEs), GoodLeap (clean energy financing), and [**Nav.com**](https://verygood.ventures/success-stories/nav-fintech-flutter-app/). ## Automotive ![Flutter automotive apps — BMW My BMW and Toyota in-vehicle infotainment powering connected vehicle experiences across 30+ markets](/assets/images/blog/top-companies-using-flutter/body-2.jpeg) Automakers face a unique platform fragmentation challenge: they need consistent experiences across companion mobile apps, in-vehicle infotainment systems, and embedded displays, often running on different operating systems. Flutter's single rendering engine works across all of these form factors, which is why major OEMs are converging on it for both customer-facing apps and in-car software. ### BMW [**BMW**](https://flutter.dev/showcase/bmw) rebuilt the My BMW app entirely in Flutter, [**launching across 30+ markets**](https://www.press.bmwgroup.com/global/article/detail/T0328610EN/the-my-bmw-app:-new-features-and-tech-insights-for-march-2021) on five continents and replacing previously divergent iOS and Android codebases. BMW's Flutter/Dart development team has grown to approximately 300 engineers — one of the largest Flutter teams in the world. ### Toyota [**Toyota**](https://flutter.dev/showcase/toyota) is a member of [**Automotive Grade Linux**](https://www.automotivelinux.org/), which [**officially supports Flutter for automotive infotainment**](https://www.automotivelinux.org/announcements/quirkyquillback/). Flutter is powering the in-vehicle infotainment system in the 2026 Toyota RAV4 — the best-selling car model in America. ### Lucid Motors [**Lucid Motors**](https://flutter.dev/showcase/lucid-motors), the luxury EV maker, built its connected vehicle companion app in Flutter. ### MOIA (Volkswagen) MOIA, the Volkswagen Group's ride-pooling service, uses Flutter for its passenger-facing app across European markets. ### AutoNation / Gather AutoNation, the largest automotive retailer in the United States, built [**Gather**](https://gather.life/) in Flutter — a family location-sharing app that enhances the driving experience with real-time location sharing and driver safety features. ### RUNBUGGY RUNBUGGY uses Flutter for its vehicle transport marketplace app, connecting car shippers with haulers for order management, real-time tracking, and document handling. ## Media & Entertainment ![Flutter media and entertainment apps — Universal Studios, Hamilton, ByteDance, Supercell, MGM Resorts, and Comcast spanning theme parks, Broadway, streaming, and mobile gaming](/assets/images/blog/top-companies-using-flutter/body-3.jpeg) Entertainment companies need rich, branded visual experiences delivered on tight launch timelines — and they need those experiences to feel identical whether a fan is on iOS or Android or even kiosks and embedded devices. Flutter powers entertainment apps spanning theme parks, live sports, streaming, and gaming. ### Universal Studios [**Universal Destinations & Experiences**](https://flutter.dev/showcase/universal-studios) uses Flutter to power guest experiences across its theme parks. [**Very Good Ventures helped Universal**](https://verygood.ventures/success-stories/amusement-park-flutter-apps/) unify park mapping across mobile and web with a single Flutter codebase and built an enhanced mobile food and drink ordering system — including self-service kiosk ordering — delivering solutions ahead of critical park opening deadlines. ### Hamilton The official [**Hamilton**](https://flutter.dev/showcase/hamilton) app was the [**first major commercial Flutter app built outside of Google**](https://verygood.ventures/case-studies/hamilton/). It offers fans exclusive content, daily ticket lotteries, HamCam camera filters, and exclusive interactive features. Launched by founding members of Very Good Ventures in 2017, the app has been continuously maintained by VGV ever since. ### ByteDance Flutter is the go-to framework for multi-platform development at [**ByteDance**](https://flutter.dev/showcase/bytedance), used in flagship apps like Xigua Video and others, with [**over 700 Flutter developers**](https://blog.flutter.dev/google-i-o-spotlight-flutter-in-action-at-bytedance-c22f4b6dc9ef) and a 33% increase in team productivity. ByteDance also built and open-sourced Lynx, a cross-platform framework inspired by Flutter. ### Tencent [**Tencent**](https://flutter.dev/showcase/tencent) moved multiple apps completely to Flutter — including AITeacher, Mr. Interpreter, and Tencent Cloud Chat (ranked #1 in China's chat service market) — achieving a 77% increase in development efficiency. ### MGM Resorts [**MGM Resorts**](https://flutter.dev/showcase/mgm-resorts) rebuilt its mobile app from the ground up in Flutter, enabling hotel rate browsing, dining reservations, contactless check-in/check-out, and a Digital Key feature for unlocking rooms with a smartphone. ### Supercell [**Supercell**](https://flutter.dev/showcase/supercell), makers of Clash of Clans and Brawl Stars, adopted Flutter for Supercell ID and achieved a 45% reduction in code while maintaining the identity service across all their games. ### teamLab [**teamLab**](https://flutter.dev/showcase/teamlab), the international art collective whose digital installations have been experienced by over 35 million people, uses Flutter for companion apps that let visitors capture and study virtual butterflies in real-world art spaces. ### Sky Sky's Global Design Engineering team builds mobile apps and TV interfaces with Flutter, serving customers across the UK, Italy, and Germany through products including Sky Go, My Sky, and the Sky Glass/Stream platforms. ### Comcast Comcast uses Flutter for a TV streaming application deployed across iOS, Android, and RDK-based devices from a monorepo. ### Trackhouse Racing Trackhouse Racing [**partnered with Very Good Ventures**](https://verygood.ventures/success-stories/trackhouse-nascar-vip-experience/) to build Flutter applications that enhance brand engagement, from trackside race-day experiences to digital fan interactions during NASCAR events to data-driven competition enhancement products. ### DiVine [**DiVine**](https://divine.video/) is a [**Jack Dorsey-funded reboot of Vine**](https://techcrunch.com/2025/11/12/jack-dorsey-funds-divine-a-vine-reboot-that-includes-vines-video-archive/), the iconic six-second video platform, built entirely in Flutter and fully open-source. The app prohibits AI-generated content and includes 170,000 archived Vine videos. ### DraftKings DraftKings built the game tracker in its Predictions app using Flutter, Flame, and Rive, combining real-time sports data with rich interactive animations for an engaging prediction experience. ### PGA of America The PGA of America uses Flutter for its digital experiences, with a customer-obsession approach to product development. [**Hear how the PGA of America innovates on the VGV podcast.**](https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/) ### Also using Flutter in media & entertainment: Gray Media, Deutsche Welle, [**Abbey Road Studios**](https://flutter.dev/showcase/abbey-road-studios) (Topline music creation app), and Axel Springer (BILD and WELT news apps in Germany). ## E-Commerce & Retail ![Flutter e-commerce and retail apps — Alibaba Xianyu, eBay Motors, Etsy, and Wendy's mobile ordering built with Flutter from a single codebase](/assets/images/blog/top-companies-using-flutter/body-4.jpeg) E-commerce companies operate under constant pressure to ship faster, maintain consistent brand experiences across platforms, and unify mobile teams that were previously split between iOS and Android. Flutter addresses all three, which is why adoption in retail has grown rapidly. ### Alibaba (Xianyu) [**Alibaba's**](https://flutter.dev/showcase/alibaba-group) Xianyu (Idle Fish), China's largest C2C marketplace for secondhand goods with over 500 million users, was one of the earliest large-scale Flutter adopters, using it for core services including search, product details, and listings. ### eBay Motors [**eBay Motors**](https://flutter.dev/showcase/ebay) built a dedicated car buying and selling app entirely in Flutter, [**launching a beta within three months**](https://innovation.ebayinc.com/stories/ebay-motors-accelerating-with-fluttertm/) and achieving 98.3% code sharing between iOS and Android. In internal surveys, [**100% of developers said they enjoyed Flutter more than native development**](https://verygood.ventures/blog/ebay-motors-flutter-experience/). ### Etsy Etsy built its standalone Seller app in Flutter, maintained by a focused team of just eight engineers. Flutter enables pixel-perfect design implementation across platforms, and the team says they [**rarely have to compromise on experience because of technical constraints**](https://verygood.ventures/blog/how-etsys-mobile-team-built-a-feature-rich-seller-app-with-flutter/). ### Wendy's Wendy's [**rewrote its mobile ordering app in Flutter**](https://verygood.ventures/blog/from-native-code-limitations-to-unified-scalable-delivery-for-40m-users/), achieving 75% faster interaction speeds and higher conversion rates than the native predecessor. Flutter enabled Wendy's to merge separate mobile teams into one unified group and shift from monthly releases to multiple releases per sprint for their 40M+ users. [**Hear from Wendy's engineering team on the VGV podcast.**](https://verygood.ventures/podcasts/brian-abston-the-wendys-company-redefining-qsr-tech-with-quality-at-the-core/) ### SKIMS SKIMS uses Flutter for its customer-facing DTC shopping app on iOS and Android, with in-house Flutter developers supporting a fast-growing global fashion brand by delivering seamless experiences for shopping, personalization, loyalty, and checkout. ### Caribou Coffee [**Caribou Coffee**](https://flutter.dev/showcase/caribou-coffee) rewrote its mobile app in Flutter after evaluating seven frameworks, choosing Flutter for its seamless integration with Firebase and Google tools they already used. Before the migration, 50% of the team was dedicated to maintaining feature parity between platforms — now all developers work on feature creation from a single codebase. ### Blue Bottle Coffee Blue Bottle Coffee, the specialty coffee chain owned by Nestle, built its mobile ordering app in Flutter, enabling customers to browse menus, customize orders, and pay for pickup at their nearest location. ### Dutch Bros Coffee Dutch Bros Coffee uses Flutter for its mobile app supporting the Dutch Rewards loyalty program, with mobile ordering, points tracking, drink customization, and payment processing across its nationwide coffee chain. ### Scotts Miracle-Gro Scotts Miracle-Gro migrated its popular "My Lawn" app to Flutter. The app generates personalized lawn care plans based on grass type, location, and conditions, and integrates IoT capabilities for the Gro smart irrigation ecosystem. ### Keller Williams Realty Keller Williams chose Flutter — [**working with Very Good Ventures**](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/) — to build a smart CRM that enables real estate agents to manage client communications, business insights, and core tasks from a single app across iOS, Android, and web. ### QuintoAndar [**QuintoAndar**](https://flutter.dev/showcase/quintoandar), Latin America's most valuable proptech startup, migrated its mobile apps to Flutter and saw its Google Play rating jump from 3.8 to 4.5 within five months. ### Talabat Talabat, the leading food and grocery delivery platform across the Middle East and North Africa with 10+ million downloads, migrated its Rewards loyalty system from native Android to Flutter, coordinating across hundreds of mobile and backend developers to achieve cross-platform consistency and faster shipping cycles with zero user disruption. ### Also using Flutter in e-commerce & retail: SHEIN (fast fashion), [**Beike**](https://flutter.dev/showcase/beike) (187M property listings in China), Funda (Netherlands' #1 real estate platform), and [**Kijiji**](https://flutter.dev/showcase/kijiji1)/eBay Canada (11M monthly users). ## Consumer Electronics & Smart Devices ![Flutter consumer electronics and IoT apps — LG smart TV, Philips Hue, Xiaomi HyperOS, and GE Appliances using Flutter across connected devices](/assets/images/blog/top-companies-using-flutter/body-5.jpeg) Flutter is expanding beyond mobile into consumer electronics, embedded displays, and connected devices. The reason is structural: Flutter's rendering engine works across form factors in a way that native and other hybrid frameworks cannot, which is why device manufacturers are adopting it for smart TVs, appliances, and vehicles simultaneously. ### Xiaomi [**Xiaomi**](https://flutter.dev/showcase/xiaomi) is rewriting its system apps with Flutter as part of [**HyperOS 4**](https://www.gizmochina.com/2026/02/05/xiaomi-hyperos-4-self-developed-os-ai-framework/), replacing the fragmented Java/Kotlin architecture with Flutter's unified rendering engine — a rollout expected around August 2026. Xiaomi also used Flutter to build the companion app for the Xiaomi SU7 electric vehicle, delivering the app 60% faster than with native frameworks. ### LG Electronics [**LG**](https://flutter.dev/showcase/lg-electronics) adopted Flutter for webOS smart TV apps starting in 2024, with their [**first Flutter-based Electronic Program Guide**](https://webostv.developer.lge.com/news/2024-07-15-new-and-successful-experiment-of-webos-with-flutter) launching twice as fast and using less memory than the original. LG is expanding Flutter to primary TV software globally for 2025-2026 televisions, a move that would place Flutter in tens of millions of LG smart TVs worldwide. ### Whirlpool [**Whirlpool**](https://flutter.dev/showcase/whirlpool), the Fortune 500 appliance maker, uses Flutter for companion apps across its brand portfolio including Brastemp, Consul, and KitchenAid. ### Philips Hue Philips Hue (by Signify) rebuilt its smart lighting companion app in Flutter, making it [**one of the biggest Flutter apps in the app stores**](https://bitrise.io/customer-stories/signify-philips-hue). The app manages six different Flutter-based products through a unified CI/CD pipeline. ### GE Appliances GE Appliances (owned by Haier) uses Flutter for embedded displays on modern smart appliances. ### Foresight Sports Foresight Sports uses Flutter — [**and told us about it on the Build To Succeed podcast**](https://verygood.ventures/blog/building-high-performance-sports-apps-with-flutter-3d-visualization-engineering-leadership/) — for its companion app that connects to Foresight and Bushnell launch monitors, providing real-time shot tracking and interactive 3D visualization of performance data for golfers and coaches. ### Agtonomy Agtonomy uses Flutter to build apps that support remote operation and monitoring of [**autonomous tractors**](https://verygood.ventures/podcasts/david-chen-building-autonomous-systems-for-real-world-challenges/), enabling a single operator to manage 10+ machines simultaneously for permanent crop and turf management. ## Technology Platforms ![Flutter technology platform apps — Google production apps, Ubuntu desktop installer, ClickUp, and enterprise SaaS platforms built with Flutter](/assets/images/blog/top-companies-using-flutter/body-6.jpeg) Technology platform companies tend to adopt Flutter when they need to maintain a portfolio of apps across multiple surfaces. The framework's ability to share code between mobile, web, and desktop from a single codebase makes it particularly appealing for companies managing complex product ecosystems. ### Google Google is Flutter's creator and largest user. Beyond Google Pay, Flutter powers **NotebookLM**, **Google Pay**, **Google Earth**, **Google Ads**, **Google Classroom**, **YouTube Create**, **Google Cloud** (mobile app), **Google One**, **FamilyLink**, **Crowdsource**, **Google Analytics**, **Readalong**, and **Fitbit Ace**. Google's major internal adoption — dating back to 2017 and continuing with [**new production launches in 2026**](https://verygood.ventures/blog/google-i-o-2025---its-a-great-time-to-be-a-technologist/) — validates Flutter at the highest scale. ### Canonical / Ubuntu Canonical rebuilt the [**Ubuntu desktop installer**](https://ubuntu.com/blog/ubuntu-desktop-23-04-release-roundup) in Flutter, making it the default installation experience for one of the world's most popular Linux distributions. ### ClickUp ClickUp built its mobile app in Flutter, investing in a [**well-layered, decoupled architecture**](https://clickup.com/blog/separation-of-concerns-in-flutter-applications/) that enables rapid feature development and weekly releases to both the App Store and Google Play. ### Zoho Zoho provides multiple Flutter SDKs including Catalyst Flutter SDK for serverless backends, SalesIQ for in-app live chat, Apptics for analytics, and Zoho Desk Portal SDK, enabling Flutter developers to build apps integrated with Zoho's enterprise SaaS ecosystem. ### 1KOMMA5° 1KOMMA5°, a cleantech unicorn serving 120,000+ households across 7 countries, uses Flutter for its Installation Manager app — digitizing operations and processes from ground operations to rooftop installations. The team built a custom Flutter design system using atomic design principles and [**Widgetbook**](https://www.widgetbook.io/), achieving zero visual bugs shipped to production and 20% time savings for designers and developers. ### Also using Flutter in technology: Grab (Southeast Asia's leading super app, 36M+ users), Wolt/DoorDash International (36M+ users across 27 countries), Nuna (healthcare data analytics), and Codeway (100M+ users across 160 countries). ## Travel & Transportation ![Flutter travel and transportation apps — Blade air mobility, NCL, JSX, and Kakao Mobility built with Flutter](/assets/images/blog/top-companies-using-flutter/body-7.jpeg) Travel companies face a familiar set of constraints: tight launch timelines tied to seasonal schedules, customers who expect booking flows to work identically on any device, and small mobile teams that cannot afford to maintain separate iOS and Android codebases. Flutter addresses all of these, which is why adoption spans airlines, railways, cruise lines, and ride-hailing services. ### Blade Blade, the premium air mobility company offering helicopter, seaplane, and jet bookings, [**built its Flutter mobile app with Very Good Ventures**](https://verygood.ventures/success-stories/blade-mobile-flutter-app/) in just 8 weeks using a Backend for Frontend architecture powered by Dart Frog. ### Norwegian Cruise Line Norwegian Cruise Line uses Flutter for its guest-facing mobile experience. ### JSX JSX, the semi-private airline, [**migrated from Xamarin to Flutter with Very Good Ventures**](https://verygood.ventures/success-stories/five-star-air-carrier-flutter/), achieving 100% test coverage and automated CI/CD pipelines for its booking and trip management app. ### SNCF Connect [**SNCF Connect**](https://flutter.dev/showcase/sncf-connect), France's national railway booking app, serves 15+ million users and handled 1.3 billion visits in 2023 — all powered by Flutter. ### Kakao Mobility [**Kakao Mobility**](https://flutter.dev/showcase/kakao-mobility), South Korea's dominant transportation platform, is featured on the official Flutter showcase. ## Health & Wellness ![Flutter health and wellness apps — Headspace with 65 million users, CVS Health, and connected fitness platforms built with Flutter](/assets/images/blog/top-companies-using-flutter/body-8.jpeg) Health and wellness apps demand smooth, visually polished experiences that feel trustworthy to users managing sensitive personal data. Flutter's consistent rendering across platforms and strong animation support make it a strong fit for this vertical. ### Headspace [**Headspace**](https://flutter.dev/showcase/headspace), the mental wellness app with 65 million users, transitioned to Flutter to deliver visually rich experiences with consistent animations across devices. [**Hear how Headspace rebuilt with Flutter on the VGV podcast.**](https://verygood.ventures/podcasts/pierre-benz-headspace-merging-culture-codebases-and-care-with-purpose/) ### CVS Health CVS Health uses Flutter as part of its mobile development strategy across iOS and Android. ### NuStep NuStep's Flutter-built companion app connects via Bluetooth to NuStep recumbent cross trainers, allowing users to track workouts, view exercise history, and share workout summaries with trainers or doctors. ### Also using Flutter in health & wellness: Nuna, Reflectly, Sonova (Phonak, Unitron). ## What the Patterns Tell Us ![Flutter enterprise adoption in 2026: 90+ enterprise companies, 2.8 million monthly active developers, 500 million-plus end users worldwide, and 46% cross-platform market share](/assets/images/blog/top-companies-using-flutter/body-9.jpeg) The companies above represent the most notable Flutter adopters, but they're far from alone. Flutter has over [**2.8 million monthly active developers**](https://blog.flutter.dev/dart-flutter-momentum-at-google-i-o-2025-4863aa4f84a4) and powers apps used by more than 500 million people worldwide. Other confirmed Flutter users include **Nestle**, **ADT**, **Generali**, **Delivery Hero**, **Degreed**, [**Tonal**](https://flutter.dev/showcase/tonal), and many more. According to Apptopia, [**Flutter accounts for nearly 30% of all new free iOS apps**](https://blog.flutter.dev/dart-flutter-momentum-at-google-i-o-2025-4863aa4f84a4), and the framework continues to expand beyond mobile into automotive infotainment, smart TVs, desktop applications, and embedded devices. What stands out across these companies isn't just that they chose Flutter. It's what happened after: faster shipping cycles, unified teams, and products delivered in months. The framework debate is settled at the enterprise level. The real question now is how to build well on it. At VGV, we can help. ## About Very Good Ventures is a [**Flutter consultancy**](https://verygood.ventures/our-services/) that has helped [**Universal Studios**](https://verygood.ventures/success-stories/amusement-park-flutter-apps/), [**Hamilton**](https://verygood.ventures/success-stories/hamilton/), [**Betterment**](https://verygood.ventures/success-stories/betterment/), Toyota, and other leading companies [**build Flutter apps at scale**](https://verygood.ventures/industries/). [**See our work.**](https://verygood.ventures/success-stories/) We published the first iteration of this list for our blog in early 2021, then again in 2023. It was intended to be a companion list to other resources in the Flutter Community, such as the [Flutter Showcase](https://flutter.dev/showcase) maintained by Google and [this list](https://itsallwidgets.com/) maintained by the Flutter Community. If you want to suggest a company to add to a future list, let us know about it! ### Very Good CLI 1.0.0: A Stable Foundation for Flutter and Dart Development Source: https://verygood.ventures/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/ Starting a new Flutter project means making dozens of decisions before writing a single line of product code: folder structure, linting rules, test configuration, CI setup, coverage thresholds. Every team resolves these differently, and every new project risks drifting from the patterns that worked last time. [Very Good CLI](https://pub.dev/packages/very_good_cli) is a Flutter and Dart command-line tool that encodes those decisions into a repeatable starting point. It provides project templates, testing utilities, and workflow automation that embody Very Good Ventures' [best practices for building production software](https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/). With v1.0.0, we are formally committing to a stable API surface, backward compatibility, and semantic versioning — so your team can depend on the tool the same way you depend on the code it generates. ## What's New in 1.0.0 This release focuses on improving the developer experience, expanding functionality, and ensuring consistent cross-platform support. ### Enhanced Testing Experience ![__wf_reserved_inherit](/assets/images/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/body-0.png) At VGV, we believe in shipping code with full test coverage. These updates make hitting that bar faster and more reliable across every platform your team targets. - **Fail-Fast Mode**: The new `--fail-fast` flag stops test execution on the first failure, helping you pinpoint failures as soon as they appear. If you have ever waited through a full test suite only to discover the problem was in the first file, this flag cuts that feedback loop from minutes to seconds — especially valuable in CI where every minute costs queue time. For more on optimizing your test workflow, see how to [supercharge your Flutter tests with Very Good CLI](https://verygood.ventures/blog/flutter-tests-very-good-cli/). - **Improved Coverage Exclusions**: The `--exclude-coverage` flag now ships with clearer documentation and inline glob pattern examples — for instance, `--exclude-coverage "**/*.g.dart"` to skip generated files. If your project uses [Very Good Coverage](https://verygood.ventures/blog/very-good-coverage/) to enforce a code coverage threshold, these exclusions let you focus the metric on the code that actually matters. - **Granular Coverage Collection**: The new `--collect-coverage-from` option lets you choose whether coverage is collected from imported files only or all files in the project, giving you more control over your coverage reports. A huge thank you to **@meylis1998** for contributing these amazing enhancements! We are incredibly grateful for our community and their valuable contributions to the project. - **Windows CI Support**: Before this release, certain `very_good test` invocations could fail on Windows CI runners due to path-handling inconsistencies across operating systems. We fixed the underlying path normalization so tests now run consistently on Windows, macOS, and Linux. If you are setting up cross-platform CI pipelines, our [GitHub Actions guide for Flutter apps](https://verygood.ventures/blog/github-actions-for-flutter-apps/) covers the broader automation patterns. ### Quality of Life Improvements We also included smaller updates focused on improving the overall developer experience and day-to-day usability of the CLI. For example, the **Enhanced License Reporting** feature, authored by community member Lukáš Šimon ([https://github.com/mendoxe](https://github.com/mendoxe)), now includes a new `--reporter` option for the `very_good packages check licenses` command. This option allows you to output a full list of all third-party package licenses, rather than just a count of the distinct licenses, which is useful for detailed compliance reporting. ### Model Context Protocol (MCP) Integration ![__wf_reserved_inherit](/assets/images/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/body-1.png) We have added a custom [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) server built directly into Very Good CLI. Unlike IDE-specific MCP configurations, this integration travels with the tool itself — it works in your terminal, in CI, and in any editor that supports MCP. You can connect MCP-compatible tools (such as Claude, Cursor, or other AI assistants) to run CLI commands, inspect project structure, and automate repetitive scaffolding tasks without leaving your workflow. This is the same architectural approach we discussed in [7 MCP Servers Every Dart and Flutter Developer Should Know](https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/), now applied to the CLI itself. The new AGENTS.md documentation in the repository explains how to configure AI agents to work with Very Good CLI, making this one of the first Flutter developer tools to ship dedicated agentic workflow documentation alongside a stable release. ### Latest Flutter and Dependencies ![__wf_reserved_inherit](/assets/images/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/body-2.png) All templates now target the latest stable Flutter ecosystem: - **Flutter 3.41**: New projects start on a current, well-supported foundation with the latest performance and rendering improvements. See the [Flutter release notes](https://docs.flutter.dev/release/release-notes) for full details on what shipped. - [**Very Good Analysis**](https://pub.dev/packages/very_good_analysis) **v10.2.0**: Upgraded to the latest version of our opinionated linting rules, keeping code style consistent across the team. For background on the philosophy behind these rules, read [Introducing Very Good Analysis](https://verygood.ventures/blog/introducing-very-good-analysis/). ## Breaking Changes and Deprecations With the commitment to stability that comes with v1.0.0, we have also taken the opportunity to streamline our offerings and focus our maintenance efforts. We are actively reviewing our priorities and projects to ensure we are focusing on the tools and templates that provide the most value and make the most sense to maintain and continue moving forward. ### Deprecating very\_good\_wear\_app The very\_good\_wear\_app template has been officially deprecated and removed from the core Very Good CLI. This template provided scaffolding for Wear OS applications. While we believe in the potential of the Wear OS platform, the template did not see enough community adoption to justify the continuous maintenance burden alongside our other core templates. The template remains available and accessible for use under the [Very Good Templates repository](https://github.com/VeryGoodOpenSource/very_good_templates) if you need to access it for existing or new projects. However, it will no longer receive updates or official support through the Very Good CLI. ## Why Version 1.0.0? ![__wf_reserved_inherit](/assets/images/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/body-3.png) Version 1.0.0 is a significant milestone. It represents: - **Stability**: Very Good CLI has proven itself in production across real projects and is ready for critical work. - **Maturity**: The core feature set is complete and covers the full lifecycle of Flutter and Dart development — scaffolding, testing, linting, and now AI-assisted workflows. - **Commitment**: We are committed to backward compatibility and [semantic versioning](https://dart.dev/tools/pub/versioning), so upgrades will not break your projects. This is part of VGV's broader [open source commitment](https://github.com/VeryGoodOpenSource): a maintained, interconnected suite of tools that teams can adopt with confidence. ## Getting Started ![__wf_reserved_inherit](/assets/images/blog/very-good-cli-1-0-flutter-testing-mcp-semantic-versioning/body-4.png) Getting started takes a single command. Activate the package globally using: `dart pub global activate very_good_cli` If you are using Dart 3.10 or higher, you can also run: `dart install very_good_cli` Then create your first Flutter project: `very_good create flutter_app my_app` For additional options (such as setting the org identifier), run `very_good create flutter_app --help`. For full documentation, visit the [Very Good CLI docs](https://cli.vgv.dev/docs/overview) or browse the [Very Good CLI GitHub repository](https://github.com/VeryGoodOpenSource/very_good_cli). ## Community and Feedback Very Good CLI is open source, and every release is shaped by feedback from the Flutter community. We would love to hear your feedback, bug reports, and feature requests — [open an issue on GitHub](https://github.com/VeryGoodOpenSource/very_good_cli/issues). There is more to come. We will keep expanding Very Good CLI with new templates, tighter tooling, and deeper integration with the Flutter and Dart ecosystem. When you adopt a 1.0.0 tool, you are placing trust in its maintainers. We take that seriously — and we are building alongside you. Thank you to everyone who contributed to making this release possible. Here's to building very good apps together. ### Streamlining Flutter Backend Development with Full-Stack Dart & Developer-Centered Architecture Source: https://verygood.ventures/blog/streamlining-flutter-backend-development-with-full-stack-dart-developer-centered-architecture/ Backend architecture decisions often happen early—and quietly—but their impact can last for years. They influence how fast teams ship, how systems scale, and how easily organizations can hire and onboard engineers. On this episode of *Build To Succeed*, we sat down with **Viktor Lidholt**, Founder of **Serverpod** and former **Flutter engineer at Google**, to explore how full-stack Dart is changing the way Flutter teams think about backend development. Drawing from his experience building startups, working on Flutter itself, and creating developer tools, Viktor shares practical insights into reducing complexity, improving velocity, and designing systems that support long-lived mobile applications. ## Engineering Perspective Shaped by the Flutter Ecosystem Viktor’s background gives him a rare vantage point. Before founding Serverpod, he worked as a senior software engineer on the Flutter team at Google, helping shape the framework that now powers thousands of production applications. In the episode, Viktor reflects on Flutter’s evolution—particularly the move to break Material and Cupertino out of the core framework—and how those decisions empower the broader community to build high-quality, purpose-driven UI packages. This philosophy of flexibility, ownership, and developer empowerment carries through directly into how Serverpod is designed. ## Why Full-Stack Dart Changes Backend Development for Flutter Teams A central theme of the conversation is the power of **using a single language across client and server**. With Serverpod, Flutter teams can write backend logic in Dart, share code between the app and the server, and automatically generate APIs without manually maintaining contracts. This approach enables teams to: - Reduce handoffs between frontend and backend engineers - Improve type safety across the entire stack - Simplify onboarding and hiring for Flutter developers - Move faster without sacrificing maintainability As Viktor explains, when everything is written in the same language, small backend changes no longer require navigating organizational bottlenecks—teams can operate as a more cohesive unit. ## Automatic API Generation & Versioning for Long-Lived Mobile Apps One of the most compelling technical discussions centers on Serverpod’s approach to **automatic API generation**. Developers define methods on the server, and Serverpod analyzes the code to generate matching client APIs—removing much of the manual overhead and potential for mismatch. Viktor also highlights how Serverpod addresses a common challenge in mobile development: **API versioning**. Mobile apps often have long-lived clients in the wild, making breaking changes costly. Serverpod allows teams to inherit and extend endpoints, preserving old versions while introducing new functionality—saving time and reducing risk as products evolve. ## Developer Experience as a Force Multiplier Throughout the conversation, Viktor emphasizes that developer experience isn’t a *nice-to-have,* it’s a *strategic advantage*. Tooling that reduces friction compounds over time, allowing teams to focus more energy on product outcomes instead of infrastructure maintenance. By automating repetitive tasks, enforcing type safety, and encouraging unified architecture, tools like Serverpod help teams scale both their systems and their organizations more sustainably. ## Lessons for Engineering Leaders Building Scalable Systems While the episode is grounded in Flutter and Dart, the takeaways extend well beyond any single framework. Engineering leaders across industries can apply these lessons when designing modern systems: - Early architectural decisions have long-term consequences - Unified stacks can dramatically improve velocity and collaboration - Strong typing reduces operational risk in complex systems - Thoughtful tooling frees teams to focus on higher-value work Whether you’re building mobile-first products, evaluating backend architecture, or leading teams through growth and change, this conversation offers practical guidance rooted in real-world experience. 🎧 [**Listen to the full episode now!**](https://verygood.ventures/podcasts/viktor-lidholt-serverpod----streamlining-full-stack-dart-for-faster-engineering-teams/) ### Stop Guessing, Start Asking: How AskUserQuestion Improves AI-Assisted Flutter Development Source: https://verygood.ventures/blog/stop-guessing-start-asking-how-askuserquestion-improves-ai-assisted-flutter-development/ ![AI-generated illustration of collaborative AI-assisted development](/assets/images/blog/stop-guessing-start-asking-how-askuserquestion-improves-ai-assisted-flutter-development/body-0.png) *Image generated with Google Gemini, prompt by the author, February 2026* Great engineers ask clarifying questions. They know that unclarified assumptions lead to technical debt.  So why do we let our AI coding assistants guess? ## What is the AskUserQuestion Tool? [AskUserQuestion Tool](https://platform.claude.com/docs/en/agent-sdk/user-input) is a built-in capability in Claude Code that lets AI pause and ask you structured questions before producing any code. Instead of making assumptions about your intent, Claude presents you with options, gathers your input, and proceeds with clearer steps guided by you. In practice, it looks like this: you ask Claude to add authentication to your Flutter app. Rather than immediately scaffolding a username and password flow (or worse, picking a random auth package that’s not been vetted), Claude stops and asks: ![Claude Code asking structured authentication questions with multiple-choice options](/assets/images/blog/stop-guessing-start-asking-how-askuserquestion-improves-ai-assisted-flutter-development/body-1.png) You pick an option. Claude asks another question. And another. By the time it writes code, **every major decision has been explicitly made by you.** ### Even better, make it a skill [Skills are a great way](https://code.claude.com/docs/en/skills) to extend Claude’s capabilities, as if you were teaching Claude additional tips and tricks to perform on your behalf.  This particular skill will create a highly detailed spec file that will help Claude be more successful on a single shot implementation of your feature. ![AskUserQuestion configured as a Claude Code skill to generate a detailed spec](/assets/images/blog/stop-guessing-start-asking-how-askuserquestion-improves-ai-assisted-flutter-development/body-2.png) ## When You Should Use it These are some of the ways I’ve been using AskUserQuestion, but you’ll certainly find more use cases where the tool will be highly useful. ### Complex features with branching implementation paths If there’s more than one reasonable way to build something, the AskUserQuestion tool helps you navigate those forks before they become buried in code.  For example, adding a caching layer to your Flutter app? Claude can ask whether you need offline-first behavior, what your cache invalidation strategy should be, and whether you’re targeting mobile-only or web as well. ### Ambiguous requirements “Add analytics” means something different on every project. Using AskUserQuestion forces you to specify:  - What events matter? - What’s your privacy stance? - Do you need real-time dashboards or batch reporting?  These aren’t the kinds of questions Claude should guess the answer to. ### Spec-based development workflows Some teams have adopted a two-session pattern. First, let Claude interview you to build a detailed spec; then, start a fresh session with that spec as context and let Claude execute. The interview phase surfaces edge cases and tradeoffs you might not have considered, while the execution phase benefits from having all ambiguity pre-resolved. For Flutter development projects specifically, this is powerful. Your architecture, state management approach, and package choices are deeply interrelated. Getting Claude to ask about your [Bloc](https://bloclibrary.dev/) vs [Riverpod](https://riverpod.dev/) vs [Provider](https://pub.dev/packages/provider) preference *before* it generates code means less refactoring later. ### Onboarding to a new codebase If Claude doesn’t know your conventions, it should ask. Combining AskUserQuestion with Plan Mode lets Claude explore your codebase, identify patterns, and confirm assumptions before proposing changes. ## When it Gets Annoying to Use As always, using the right tool for the right job is half the battle. In my experience, AskUserQuestion works wonders for large areas of work that I might need extra confidence that I flash out properly.  However, there were a handful of times when it didn't work for me. ### The task is simple and unambiguous  If you’re asking Claude to “add a null check to this function”, you don’t need an interview. The tool is valuable for decisions that have meaningful tradeoffs, not for tasks with obvious answers. ### When you want exploratory conversation The AskUserQuestion tool presents structured multiple choice options. If you’re in discovery mode and want to think out loud with Claude (e.g. exploring half-formed ideas, or getting feedback on whether something is even feasible), a free-form conversation is better. The formality of the tool can feel constraining when you’re still figuring out where you want to go. ### Trivial implementation details Not every question deserves human escalation. If Claude is asking “Should this variable be named *userId* or *user\_id*?”, something has gone wrong. The tool works best for architectural and product-level decisions, not syntactic choices your linter should handle. ## The Real Value: Explicit Tradeoffs Here’s what changes when Claude starts asking questions: ### Decisions become visible  Every answer you give is a design decision captured in the conversation history. When you revisit the code in six months wondering why you went with [localstorage](https://pub.dev/packages/localstorage) instead of [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage), the reasoning is documented. ### Assumptions become testable  Instead of discovering buried assumptions during a code review *(“wait, why does this retry three times with exponential backoff?”)*, you confront design decisions upfront when they’re cheap to change. ### Your spec gets stress-tested This might be, by far, the most important element. Claude is relentless about edge cases. *“What should happen if the network request succeeds, but returns malformed JSON?” “Should this animation run on first launch only, or every time?”* These are questions you should have answers to, and the interview forces you to answer them. ## Introducing AskUserQuestion in Your Flutter Development Flow The most effective pattern we’ve seen combines AksUserQuestion with Claude Code’s Plan Mode: ### 1. Start with a (minimal) spec Write down what you’re building in a [*plan.md*](http://plan.md) file, even just a few sentences describing the feature. ### 2. Ask Claude to interview you A prompt like this works well: *Read @*[*plan.md*](http://plan.md) *and interview me in detail using the AskUserQuestion tool about technical implementations, UI/UX, concerns, and tradeoffs. Ask non-obvious questions. Continue until we’ve covered everything, then update the spec. * ### 3. Review and iterate Claude’s questions will surface things you hadn’t considered. Update your answers. Ask follow-up questions of your own. ### 4. Execute with fresh context Once your spec is solid, start a new session with that spec as a context. Claude now has clear direction and can execute with precision. For Flutter-specific features, good use cases are: - Target platforms and their unique behaviors - State management approach consistency with your existing patterns - Widget composition and reusability expectations - Testing strategy (unit, widget, integration) - Accessibility requirements - Animation and performance constraints ## The Bigger Picture AI coding assistants are becoming capable enough that the bottleneck is shifting. The limiting factor isn't "can the AI write this code?" It's "does the AI understand what I actually want?" AskUserQuestion tool inverts the typical prompt engineering relationship. Instead of you crafting the perfect prompt to get Claude to do what you want, Claude prompts you to clarify what you want. That's a healthier collaboration pattern—one where the human stays in the decision-making seat while the AI handles the heavy lifting. For Flutter engineering teams that care about craftsmanship, this matters. Quality isn't just about the code that gets written; it's about the decisions that inform that code. Making those decisions explicit, early, and visible is how you maintain high standards as AI becomes a bigger part of how you build. ### Loyalty is a Product, Not a Program: How Modern Brands Build Habit-Forming Engagement Source: https://verygood.ventures/blog/loyalty-is-a-product-not-a-program-how-modern-brands-build-habit-forming-engagement/ Loyalty is no longer about punch cards, discount ladders, or static rewards catalogs. Those models were built for a different era—one where customer relationships were shallow, data was scarce, and personalization across **mobile apps and web** was impossible at scale. The most successful loyalty programs today function as **living products**. They’re designed to create habits, reinforce identity, capture first-party data, and continuously adapt to user behavior in real time. At Very Good Ventures (VGV), we start every loyalty engagement with a simple but powerful belief: **Engagement is the product.** When loyalty is treated as a product—not a campaign or a marketing add-on—it becomes a durable growth engine, rather than a cost center. In this series, our experts explain what that mindset shift looks like, why traditional loyalty programs fail, and how modern brands design loyalty systems that actually change behavior. ## Why Traditional Loyalty Programs Fail Most loyalty programs struggle for the same reasons: - They’re bolted onto an existing experience rather than embedded within it - They over-index on points and perks, not motivation - They’re launched once and rarely evolve - They optimize for enrollment, not engagement The result is predictable: low participation, limited differentiation, and escalating costs with little measurable lift. These programs treat loyalty as a mechanism, not an experience. But customers don’t build habits around mechanisms—they **build habits around value, progress, and identity**. These outcomes require intentional product design, thoughtful flutter architecture, and technology that supports iteration. Modern loyalty requires a fundamentally different approach. ## Loyalty as a Product, Not a Program When loyalty’s treated as a product, ownership shifts. So does accountability. Instead of asking: - “How many points should we give?” - “What rewards should we add?” Product-oriented teams ask: - “What behavior creates value for both the customer and the business?” - “What experience reinforces that behavior over time?” - “How does loyalty evolve as the customer relationship matures?” This shift reframes loyalty from a promotional tactic into a **core part of the customer journey**—one that’s intentionally designed, measured, and iterated on like any other digital product. In this model, loyalty doesn’t sit *on top of* the experience, **it** ***is* the experience.** ## Engagement Is the Product Loyalty exists to deepen engagement. Not transactions. Not discounts. Engagement. High-performing loyalty products are built around a **clear core action**: the behavior that delivers the most value when repeated consistently. That action might be purchasing, using a feature, creating content, referring others, or engaging with services. Everything else in the loyalty system exists to reinforce that action. This reinforcement happens through a **virtuous engagement loop**. ## The Virtuous Loyalty Loop Effective loyalty systems follow a predictable, repeatable pattern: ### Activate Bring users into a known, permissioned relationship. This is where trust is established and value is made explicit. ### Earn Reward behaviors that matter—not just transactions, but actions that signal intent, commitment, or future value. ### Acknowledge Reinforce progress, status, and identity. Humans are **deeply motivated by recognition** and momentum. ### Redeem Turn loyalty currency into **meaningful value**, not filler rewards. Redemption should feel satisfying, not transactional. ### Re-Engage Use personalization and context to restart the loop, nudging users toward the next valuable action. When designed intentionally, this loop increases: - Frequency of engagement - Depth of relationship - Longevity of customer value At the same time, it continuously enriches first-party data, fueling better personalization and smarter decision making. ## Designing for Habit, Not Just Incentives One of the biggest mistakes brands make is assuming loyalty is driven primarily by incentives. In reality, incentives are only effective when they reinforce **existing motivation**. Habit-forming loyalty products leverage: - Progress and mastery - Status and social signaling - Identity and belonging - Autonomy and choice Points alone don’t create habits. **Experiences do.** That’s why the best loyalty products feel less like programs and more like **ongoing relationships**—where customers understand what progress looks like and why continued engagement matters. ## What Changes When You Treat Loyalty Like a Product When organizations adopt a product mindset for loyalty, several important shifts occur: - **Ownership becomes clear:** Loyalty has a roadmap, not just campaigns. - **Experimentation becomes normal:** Features, mechanics, and messaging are tested and refined over time. - **Technology choices become strategic:** Platforms are selected based on scalability and adaptability, not short-term convenience. - **Measurement focuses on behavior change:** Success is defined by incremental lift and engagement quality, not vanity metrics. Most importantly, loyalty stops being something teams “launch” and starts being something they **continuously improve**. ## How VGV Approaches Loyalty Product Design We help organizations design and build custom flutter apps that power loyalty ecosystems that go far beyond points and perks. Our approach blends: - Behavioral design and research - Product strategy and experience design - Modern flutter development and personalization - Rapid experimentation and delivery We design loyalty systems as **products that can evolve**: starting simple, learning quickly, and increasing sophistication over time. Because strategy and flutter engineering teams are tightly coupled, they can move from vision to minimum viable product (MVP) in weeks—not years—without sacrificing long-term scalability. The result is loyalty that: - Feels personal, not promotional - Drives real behavior change - Scales with both ambition and complexity ## Loyalty’s No Longer Optional—and It’s No Longer Simple As third-party data disappears and customer expectations rise, loyalty has become one of the most powerful tools brands have to build durable relationships. But only when it’s designed intentionally. Treat loyalty like a product, and it becomes an engine for engagement, insight, and growth. Treat it like a program, and it becomes another forgettable feature competing for attention. At VGV, we believe the future belongs to brands that build loyalty systems capable of learning, personalizing, and compounding value over time. ### Braze Intelligence Isn’t Hype—It’s a Performance Multiplier Humans Can’t Compete With Source: https://verygood.ventures/blog/braze-intelligence-isnt-hype--its-a-performance-multiplier-humans-cant-compete-with/ I’ve worked with a lot of lifecycle teams over the years; some early-stage, some global, some genuinely best-in-class. The common thread I see right now is: **The teams winning aren’t working harder or sending more messages.** They’re letting intelligence make decisions humans simply can’t make at scale. [Braze’s AI-powered Intelligence Suite](https://www.braze.com/product/brazeai) isn’t theoretical. When [implemented correctly](https://verygood.ventures/success-stories/blade-braze-optimization-roadmap/), it’s **delivering** **material, compounding performance gains** across engagement, retention, and revenue—and it’s doing so in ways that manual marketing can’t mimic. ## From Manual Optimization to Adaptive Systems Most lifecycle marketing today is still constrained by human workflows: - Fixed segments refreshed weekly or monthly - Static send times based on averages - Campaign-level decisions instead of user-level decisions Even highly skilled marketers are forced to generalize. **Braze Intelligence changes the model entirely.** It operates as a real-time adaptive system, continuously learning from individual behavior across channels, devices, and moments. At Very Good Ventures (VGV), when our clients move from rule-based orchestration to AI-driven decisioning, we consistently see **step-change improvements**, not marginal lifts. This is the difference between optimization and evolution.  ## What the Data Actually Shows Across consumer apps, subscription products, and multi-location brands running [mature Braze programs](https://verygood.ventures/success-stories/blade-mobile-flutter-app/), we see consistent benchmark ranges: ### Intelligent Timing - **10-25% lift** in opens or primary engagement - **5-15% lift** in downstream conversions - Reduced send-time fatigue *without* reducing volume Why this matters: No human can calculate optimal send times per user, per channel, per day. [Braze does it continuously](https://verygood.ventures/success-stories/blade-email-design-system/), at scale. ### Intelligent Channel Selection - **8-20% increase** in conversion rates when channel choice is automated - **10-30% reduction** in opt-outs due to less over-messaging - Improved long-term engagement curves, not just short-term wins This is one of the most underrated capabilities in lifecycle marketing. Marketers love channels. Users love relevance. ### Intelligent Content & Offer Optimization - **5-20% lift** in CTR across multi-variant campaigns - Faster creative learning cycles with fewer hardcoded rules - Clear performance separation between variants humans would treat as “equivalent” - Better “building blocks” and rules-based agents that extend to the entire digital experience, creating baseline structure for holistic AgenticUI and advanced personalization (paired with Google) in app, web, and every digital screen. This is where AI starts outperforming intuition—especially at scale. ## Where Results Become Genuinely Incredible ### Lifecycle Acceleration in Flutter Apps Flutter gives teams a massive advantage: a single, high-performance UI layer across platforms. When paired with Braze Intelligence, the impact compounds: - **10-30% improvement** in time-to-first-value - Higher onboarding completion rates without increasing message volume - Journeys that adapt dynamically based on real product behavior, not assumed funnels The combination of Flutter’s consistency and Braze’s intelligence creates a feedback loop humans can’t manually maintain. ### Monetization Without Retention Tradeoffs Human-led monetization almost always over-optimizes short-term revenue. Braze Intelligence balances: - Purchase likelihood - Engagement risk - Message saturation - Granular in-app and web behavioral insights The result: - **5-15% lift** in revenue per user - Neutral to positive impact on 30- and 60-day retention - Lower reliance on discounts and promotions over time This is where AI proves it’s not just fast—it’s *smarter* and more cost-effective. ## AI Is Only as Powerful as Your Content Supply Chain Here’s the part most teams miss: AI doesn’t fail because the model is weak—**it fails because the content system feeding it’s broken.** This is where the Figma + Braze workflow becomes a force multiplier. ### What High-Performing Teams Do Differently Elite teams: - Design **modular, tokenized components** in Figma - Create structured creative variants intentionally (not ad hoc) - Map creative inputs directly to Braze content blocks and attributes (bonus points if they’re also mapped to your Flutter design system) When done right: - Creative variation increases **3-10x** without linear effort - Braze Intelligence has enough signal to actually learn - Marketers stop debating “which version is best” and let performance decide This is the modern content supply chain: **Design once. Scale infinitely. Optimize automatically.** At VGV, this is where we see teams unlock the full value of AI—because intelligence without creative velocity is wasted potential. ## AI Isn’t Replacing Marketers—It’s Outperforming Manual Decisions Here’s the uncomfortable truth: No human team can evaluate millions of user-level decisions in real time—across channels, timing, content, and context. The best teams don’t fight that reality. They design for it by: - Letting AI handle executional optimization - Focusing human energy on strategy, narrative, and experimentation - Building systems that improve themselves over time That’s not automation for automation’s sake.That’s leverage. ## Why VGV, Braze & Flutter Work So Well Together Very Good Ventures sits at the intersection of: - Flutter-first product engineering - Lifecycle and data architecture - AI-enabled growth systems - Smart offer and content strategies that anticipate human needs Flutter gives teams speed and consistency. Braze provides the intelligence layer. VGV ensures the system is designed correctly—from data modeling to canvases to measurement. The result isn’t just better campaigns. It’s a self-improving growth engine. ## Final Thoughts AI in lifecycle marketing isn’t coming, it’s already here—and it’s outperforming manual decision-making. The only real question is whether your organization is structured to let it work. If it is, the gains compound fast. If not, no amount of hustle will close the gap. ![__wf_reserved_inherit](/assets/images/blog/braze-intelligence-isnt-hype--its-a-performance-multiplier-humans-cant-compete-with/body-0.png) ## FAQ: AI, Braze Intelligence & Lifecycle Marketing ### What is Braze Intelligence and why does it matter for lifecycle marketing? Braze Intelligence is an AI-driven decisioning suite that automates user-level decisions across timing, channel, offer, content, and message personalization. Unlike rule-based workflows, it continuously learns from individual behavior and optimizes engagement in real time, driving measurable lifts in opens, conversions, and retention that manual workflows cannot match. ### How does Braze Intelligence improve customer engagement? By replacing static segments and manual timing with adaptive user-level optimization, Braze Intelligence increases open rates, conversions, and long-term retention while reducing fatigue and opt-outs. AI selects the best channel, content, offer, timing, and more for each user based on behavioral signals, driving relevance at scale. ### How does AI-driven lifecycle marketing compare with human-led decisioning? No human team can simultaneously evaluate millions of personalized decisions in real time across channels, offers, context, and timing. AI bridges that gap, allowing marketers to shift focus to strategy and content quality, while the system optimizes execution. This produces compounding gains instead of incremental improvements. ### How does Braze Intelligence integrate with existing product and engineering systems? Braze Intelligence works alongside modern product stacks—including Flutter apps and modular content systems—to deliver dynamic engagement. Structuring creative variants (e.g., in Figma) and mapping content block attributes into Braze enables the AI to learn more effectively, increasing creative velocity and personalization depth. Bi-directional data flows with loyalty platforms, analytics platforms, CDPs and other tools significantly bolster the accuracy and context for each message, as well as the "real-time" feeling. ### What real-world results do businesses see with AI-powered lifecycle systems? Organizations implementing Braze Intelligence commonly report: - Higher engagement rates (double-digit lifts in opens and clicks) - Growth in conversions without increasing message volume - Reduced opt-out rates through better relevance - Revenue growth with neutral or positive retention impact - Faster onboarding, better retention of brand, and more relevant experiences ### How do cross-platform product strategies (e.g., Flutter + Braze) accelerate time-to-value? A unified UI layer, like Flutter, combined with real-time AI optimizations enables rapid iteration, lower per-user cost of engagement, and consistent performance across devices. This synergy drives faster onboarding completion and more adaptive user journeys than legacy native stacks. ### Do marketers still play a role when AI is optimizing lifecycle decisions? Absolutely. Marketers remain essential for developing strategy, crafting narrative, designing creative variants, and curating offer logic. AI augments and executes at scale, but human insight shapes the conditions and goals the system optimizes toward. Humans want to talk to humans. They want their messaging to feel authentic, on-brand and trustworthy. ### How should teams prepare for AI-driven lifecycle marketing? High-performing teams build: - Structured, modular content aligned with product UI systems - Data models that feed continuous learning - Cross-functional processes where product and marketing share measurement criteria Aligning these systems ensures Braze Intelligence has the signal it needs to deliver results. ### Mobile Ordering That Works in the Real World: Scalable Flutter Solutions for Theme Parks, QSR, Cruise Lines & Entertainment Venues Source: https://verygood.ventures/blog/mobile-ordering-that-works-in-the-real-world-scalable-flutter-solutions-for-theme-parks-qsr-cruise-lines-entertainment-venues/ Across restaurants, theme parks, coffee chains, and large hospitality environments, the demand for mobile food & drink ordering (MFDO) has skyrocketed. Guests expect speed, operators need efficiency, and brands want higher engagement and revenue. On paper, the solution seems simple: Build a digital ordering experience. In reality, MFDO sits at the intersection of technology, operations, guest behavior, human connection, and physical space. ## How Flutter is Powering Mobile Ordering Solutions  Flutter is an open-source user interface toolkit created by Google. It allows teams to build applications for multiple platforms—iOS, Android, web, desktop, and embedded devices—from a single codebase. Strong core principles guide Flutter’s design: - **Performance:** Apps are compiled to native machine code, allowing smooth animations and responsive interactions. - **Consistency:** Flutter includes its own rendering engine, making interfaces behave and look the same across platforms. - **Flexibility:** Developers can work across the entire UI stack, customizing components or creating entirely new ones when needed. Flutter solutions emerged in response to a challenge many organizations face: maintaining separate development teams and codebases for each platform, which increases cost, slows down feature delivery, and complicates coordination. With Flutter, product teams can reach users on multiple devices while reducing duplicate effort. ### Why This Matters for Mobile Ordering As mobile food and drink ordering expands across industries—theme parks, QSR, cruise lines, stadiums, and resorts—organizations increasingly require systems that can run reliably across many contexts and device types. Flutter’s multi-platform model naturally supports this, helping teams build: - Unified guest experiences - Operationally consistent interfaces across mobile and kiosk environments (including embedded Flutter apps that can be used in Angular websites) - Scalable systems that evolve without maintaining multiple codebases VGV’s role has been to help organizations apply these capabilities in ways that align with real-world operational needs, not just technical requirements. As MFDO becomes a critical piece of the guest journey, the brands leading the way are turning to Flutter—and to partners who understand both the technical architecture and the operational realities behind mobile ordering. Here’s how leading brands navigate that complexity, and why Flutter’s quickly becoming the backbone of modern food and drink ordering solutions. ## What Organizations Must Know When Developing Mobile Food & Drink Ordering Strategies MFDO is a cornerstone of modern guest experiences, and organizations across theme parks, QSR, hospitality, and entertainment are learning that success and scale require more than just a great app. These key learnings highlight what forward-thinking brands are embracing to build mobile ordering systems that improve service, empower staff, and deliver real-world impact at scale. ### 1. Digital ordering should enrich the in-person experience.  Mobile and kiosk ordering shine brightest when they complement, not replace, human hospitality. When thoughtfully integrated, they create convenience without sacrificing connection, allowing teams to focus on higher-value moments with guests. This aligns with what the mobile app development teams behind great MFDO systems already know—that people matter. ### 2. Strong operations make digital experiences exceptional. Great MFDO is more than just great software. When technology aligns with staffing models, fulfillment workflows, and service design, guests enjoy faster service, fewer errors, and a more cohesive overall experience. ### 3. Omnichannel harmony creates guest confidence. Whether guests choose to order in the mobile app , on the web, at a kiosk, or directly with staff, they expect it to be intuitive and easy—something that apps built with Flutter can deliver. Organizations that balance digital and physical touchpoints build trust, increase satisfaction, and meet guests where they are in their journey. ### 4. Mobile ordering as a powerful growth engine. Across industries, mobile orders make up a large chunk of revenue. When optimized, MFDO increases throughput, boosts average check size, and encourages repeat ordering. ### 5. Accuracy is the foundation of guest delight. A beautiful interface is only half the battle. Successful MFDO systems integrate seamlessly with kitchen, bar, and venue operations to ensure orders are prepared correctly and delivered on time. This is where Flutter’s reliability and performance elevate the entire experience—reflecting why so many organizations choose a Flutter development agency to build their MFDO system at scale. ### 6. Strategic planning unlocks scalable digital ecosystems. Scaling MFDO requires alignment across technology, service design, staffing, menu strategy, and physical space planning. When these elements work together, digital ordering becomes a long-term competitive advantage.  ### 7. The right partners accelerate success. Expert partners with deep hospitality and technical expertise help organizations avoid common pitfalls and unlock the full potential of mobile ordering. With the right guidance, brands build solutions that improve operations and engage staff and guests. This is why organizations increasingly rely on Flutter agency partners who can build custom Flutter applications with multi-platform capabilities. ## How VGV Supports Cross-Industry MFDO Success VGV integrates ideation, product strategy, UX/UI, and high-performance Flutter engineering to deliver MFDO systems that are operationally sound, with thoughtful architecture that improves both the guest experience and team member usability. Here’s our approach:  ## 1. Operationally Intelligent Systems Designed around real-world constraints such as: - Kitchen and bar workflows - Preparation timing - Staff routing and availability - Pick-up zones and throughput capacity This ensures mobile ordering solutions don’t overwhelm teams, but empowers them. ## 2. Scalable Systems for Complex Environments Whether supporting thousands of orders on a cruise ship, a theme park during peak season, or a national QSR network, VGV builds custom Flutter apps  and experiences that: - Handle surges while maintaining performance under extreme load - Deploy across multi-venue and multi-screen environments - Reuse code and reduce tech debt ## 3. Guest-Centric Experience Design… VGV creates intuitive and optimized mobile ordering interfaces that anticipate guests' needs with: - Fast decision making - Frictionless customization - Clear fulfillment expectations - Delightful micro-interactions that reinforce brand identity ## 4. …and Team-Centric Experience Design We also create apps that support teams, including: - Team-focused features that enable easier, more seamless experiences for employees  - MFDO systems for employees ## 5. Deep Integration With Existing Systems From point-of-sale systems and inventory management, to customer relationship management and loyalty, VGV ensures your MFDO system works within your broader ecosystem. ## Scaling MFDO Across World-Class Theme Parks Universal Destinations & Experiences (UDX) parks host millions of guests each year. As digital adoption surged, they [needed to enhance and expand their MFDO](https://verygood.ventures/success-stories/amusement-park-flutter-apps/) inside the Epic park in Orlando, Florida. Guests needed smooth ordering processes, and operators wanted workflows that could withstand massive peaks in traffic. At the same time, leadership needed a platform that wouldn’t multiply tech debt—a challenge solved by VGV’s years of experience with Flutter app development, and its single codebase infrastructure.  UDX partnered with VGV to:  - Use mobile ordering to reduce staff dependency during low-volume times - Integrate seamlessly with existing POS systems - Meet aggressive deadlines tied to new park openings - Avoid maintaining redundant codebases across devices and platforms They also had a major opportunity to transform MFDO into a self-service kiosk solution that works inside physical restaurants, powered by the same multi-platform Flutter codebase. ### The Solution  VGV rebuilt UDX’s kiosk solution using Flutter, turning a mobile-first feature into a robust self-service interface running on Windows hardware. This included: - A rewritten kiosk UI built for high visibility and rapid navigation - Full integration with UDX’s ordering systems ![__wf_reserved_inherit](/assets/images/blog/mobile-ordering-that-works-in-the-real-world-scalable-flutter-solutions-for-theme-parks-qsr-cruise-lines-entertainment-venues/body-0.png) ### The Impact UDX’s upgraded MFDO system has delivered: - Improved operational efficiency inside restaurants - Higher mobile adoption and increased in-app revenue - Streamlined kiosk operations with one scalable codebase - Consistent cross-platform UX, reducing guest confusion - Successful on-time delivery UDX now has a platform designed for long-term expansion—one capable of supporting new venues, new app features, and surges in seasonal park traffic thanks to our combined teams of experienced Flutter Engineers. ## Rebuilding a Nation’s Mobile Coffee Experience As a nationwide coffee chain, our client serves millions of guests every year, and their mobile app is a critical channel for ordering, loyalty building, and brand identity. But their digital ecosystem was fragmented. They had two native apps, inconsistent patterns, and evolving needs that made it difficult to scale or deliver the premium experience guests expected. They partnered with VGV to rebuild everything from the ground up, using our Flutter developers to deliver exactly what they needed. ### The Solution  VGV created one unified, future-ready, Flutter-based app that offers: - A consolidated codebase following Flutter best practices - A fast, stable ordering experience - Smart, guided drink customization - Secure payments and Olo menu integration - A foundation that their engineering team could confidently own The heart of their business is customization. Each drink has dozens of possible combinations, and every order must translate clearly into barista-ready instructions. ### The Impact  VGV built a guided customization flow that: - Makes complex drink building effortless - Uses smart defaults to lower the user’s cognitive load - Updates dependent options automatically - Preserves valid selections during adjustments - Ensures accuracy with real-time availability checks This adaptive architecture supports hundreds of blends without slowing down. Behind the scenes, every choice updates the ordering system dynamically—reflecting store-specific pricing, ingredients, and availability. The result is a mobile ordering system that’s fast, intuitive, and on brand. ##  Personalized, Connected Experiences Onboard VGV partnered with a major global cruise line to reimagine the guest experience—from pre-cruise planning to onboard dining and activities—through a mobile experience designed to work both on land and at sea. A task that VGV’s team of expert Flutter developers was ready to take on.  ### The Solution  Before sailing, VGV helped them create a Flutter-based experience that allows guests to explore, plan, and purchase every part of their trip in advance, including: - Dining options, restaurants, and beverage packages - Add-ons like internet, entertainment passes, and spa & thermal experiences - Shore excursions and on-shore activities - Gifts and personalized items purchased in advance and delivered directly to their cabin - Private professional photos - Unlimited pass options and more This pre-cruise functionality not only increases guest satisfaction, but also drives meaningful pre-sailing revenue. Once on board, guests can manage and personalize their dining and activity plans through a custom-built Flutter mobile ordering and reservation system. The solution supports: - Shipboard connectivity challenges (each ship has its own technical environment) - Multiple venues, restaurants, and menu variations - Real-time specifications like age restrictions and dynamic availability - Staff routing and preparation timing, including a three-day, pre-cruise system lockout ### The Impact They now offer an emphatically smoother onboard dining and activity experience that reduces congestion, and helps distribute guest traffic across venues more intelligently—using technology built by a world-class mobile development team and Flutter app development experts. This is reflected in:  - Faster and more intuitive guest interactions - Increased operational efficiency for staff - Dynamic, real-time availability and customization - A unified platform that enhances both pre-cruise planning and onboard engagement Backed by VGV’s expertise in enterprise mobile development and Flutter architecture, the cruise line has transformed its guest journey—turning a once-fragmented experience into a seamless, high-performance digital ecosystem built for the real cruise experience.  ## The Broader Story: MFDO as a Cross-Industry Engine Leaders like Universal Destinations & Experiences, nationwide coffee chains, and major global cruise lines invest in Flutter partners like VGV because they understand that technology, service, operations, menu, and space must all work together. These organizations represent three very different industries, yet their challenges share the same DNA: - Guests who expect premium, frictionless digital experiences - Staffing variability that demands operational efficiency - Revenue increasingly flowing through mobile - Organizations needing systems that scale without multiplying codebases - Accuracy and integration being as important as good UI Across QSR, hospitality, travel, and entertainment, companies are waking up to the same realization: Mobile ordering is no longer an add-on—it’s a mission-critical operating system for the guest journey. This is where VGV’s expertise truly helps. As the best Flutter agency and Google partner, we unite strategic thinking and strong Flutter engineering practices to build mobile ordering systems that are resilient, scalable, and built for real operational use. ### Why AI Decisioning Is the Future of Customer Engagement—and How to Get Started Source: https://verygood.ventures/blog/why-ai-decisioning-is-the-future-of-customer-engagement--and-how-to-get-started/ Customers expect personalized experiences, and marketing teams have more data than ever to deliver them. But the traditional approaches of building segments, running A/B tests, and creating rules for each scenario weren't designed for the complexity of modern customer journeys. This is why **AI decisioning** represents a fundamental shift.  At Very Good Ventures (VGV), we’ve partnered with organizations across financial services, retail, QSR, and media to design and implement AI-driven customer engagement programs using platforms like [BrazeAI Decisioning](https://www.braze.com/product/brazeai-decisioning-studio). We've seen firsthand how this technology transforms not just campaign performance, but how teams think about personalization, experimentation, and customer value. ## The Limits of Traditional Personalization Most marketing teams are sitting on rich customer data that's difficult to activate at scale. Traditional “next best action” strategies require marketers to manually: - Build segments - Design experiments - Define business rules - Analyze performance after the fact This process is **time-intensive, reactive, and inherently limited**. Consider what this looks like in practice: A marketing team might spend weeks building microsegments, testing different offers against each segment, and analyzing results. Even with substantial effort, they can only test a handful of variables at once. They cannot personalize within segments. They cannot optimize timing, frequency, creative, channel, and offer simultaneously. And by the time they've completed their analysis, customer behavior may have already shifted. The result is a set of common challenges we see across industries: underutilized customer data, time-constrained experimentation, over-communication to some customers and under-communication to others, blanket discounts that erode margins, and siloed channels that create disjointed experiences. ## What Makes AI Decisioning Different **AI decisioning fundamentally changes this equation by replacing static rules with continuous, customer-level optimization.**  Instead of asking *"What should we send to this segment?"* AI decisioning asks **"What’s the optimal action for this individual customer right now?"** With solutions like BrazeAI Decisioning, the system operates as a closed feedback loop: 1. Customer data flows from your data warehouse or CDP into the AI decisioning engine 2. The model generates daily predictions for each customer—optimizing **offer, creative, timing, frequency, and channel** 3. Decisions are activated through your engagement platform 4. Customer interactions feed back into the data layer, improving future predictions  This creates **autonomous experimentation at a scale and speed impossible to achieve manually**. The system continuously tests, learns, and adapts—without requiring marketers to constantly redesign segments or rules. ## What Implementing AI Decisioning Actually Requires A typical AI decisioning implementation takes **approximately sixteen weeks** from kickoff to production. But the real work often begins earlier, with preparation across three critical areas: ### 1. Data Foundations AI decisioning depends on high-quality, [accessible customer data](https://verygood.ventures/success-stories/blade-braze-optimization-roadmap/). This includes: - A clear data strategy - Unified customer profiles across channels - Historical interaction data for model training - Reliable transformation pipelines feeding the AI engine Ask yourself: Can you access a unified view of customer behavior across channels today? Do you have the historical interaction data needed for the model to learn from?  Organizations with mature data infrastructure move faster, but even teams earlier in their journey can launch focused use cases using existing data—as long as expectations are aligned. ### 2. Creative Supply Chain Readiness AI decisioning creates demand for [**more creative variations**](https://verygood.ventures/success-stories/blade-email-design-system/), not fewer: different offers, messages, and treatments that the system can test and optimize. But the system can only optimize what it has available.  Before implementation, teams should assess: - How quickly new creative variants can be produced - Whether assets are modular and reusable - How offers are governed and approved - Whether asset management supports rapid iteration If it takes weeks to produce a single campaign variation, AI decisioning will be constrained. Strong creative operations unlock significantly more value. ### Strategy & Value Alignment AI decisioning changes workflows and decision-making. Alignment is essential. Key questions to answer upfront: - What business metric defines success (conversion, retention, LTV, margin)? - How should teams interpret optimization outputs? - How will roles and responsibilities evolve? Executive sponsorship and shared definitions of success ensure teams trust the system and act on its recommendations. ## What Predicts AI Decisioning Success AI decisioning performs better when organizations have: - Rich, diverse customer data - Sufficient interaction volume for faster learning - Multiple dimensions to optimize (creative, channel, timing, offer) - Control over end-to-end customer experiences - Historical testing data - Strong analytics capabilities - Mature marketing and data technology stacks - Active executive sponsorship Perfection isn’t required; but understanding where you stand helps set realistic expectations and prioritize the preparation work that drives better outcomes. ## Getting Started With AI Decisioning AI decisioning isn’t a plug-and-play feature. It requires investment in: - Data infrastructure - Creative operations - Organizational change management When implemented thoughtfully, however, it enables **truly individualized customer engagement at scale**—moving far beyond the limits of segmentation and rules-based personalization. AI will transform customer engagement. The real question is whether your organization will be ready to capture that value. ![__wf_reserved_inherit](/assets/images/blog/why-ai-decisioning-is-the-future-of-customer-engagement--and-how-to-get-started/body-0.png) ## FAQ: AI Decisioning in Customer Engagement ### What is AI decisioning in customer engagement? AI decisioning uses machine learning models to determine the optimal action for each individual customer in real time, optimizing factors like message, offer, timing, frequency, and channel. ### How is AI decisioning different from traditional personalization? Traditional personalization relies on static segments and manual rules. AI decisioning operates at the individual level, continuously learning and optimizing without requiring marketers to rebuild segments or experiments. ### Does AI decisioning replace marketers? No. AI decisioning augments marketing teams by automating experimentation and optimization, allowing marketers to focus on strategy, creative development, and customer experience design. ### What data is required to use AI decisioning effectively? At minimum, organizations need historical customer interaction data, unified customer profiles, and reliable data pipelines. More data dimensions improve performance, but teams can start with focused use cases. ### How long does it take to implement AI decisioning? A typical implementation takes around 16 weeks, including data preparation, model setup, activation, and initial optimization. ### Is AI decisioning only for large enterprises? While larger organizations often see faster learning due to volume, mid-market teams can also benefit by scoping targeted use cases and scaling over time. ### How does BrazeAI Decisioning fit into an existing martech stack? BrazeAI Decisioning integrates with existing data warehouses, CDPs, and engagement channels, allowing organizations to activate AI-driven decisions without replacing their core stack. ### ### Building High-Performance Sports Apps with Flutter, 3D Visualization & Engineering Leadership Source: https://verygood.ventures/blog/building-high-performance-sports-apps-with-flutter-3d-visualization-engineering-leadership/ Modern sports technology demands more than polished UI—it requires precision, performance, and seamless collaboration between hardware and software. [On this episode of *Build to Succeed*](https://verygood.ventures/podcasts/kody-peterson-foresight-sports-rebuilding-mobile-architecture-with-flutter-and-3d-innovation/), we sat down with **Kody Peterson**, Director of Software Engineering for Digital Studio at **Foresight Sports**, to explore how his team builds high-performance mobile applications using **Flutter**, **interactive 3D visualization**, and strong engineering leadership. From platform decisions to cross-disciplinary collaboration, this conversation offers valuable lessons for engineering and technology leaders building complex, data-driven products. ## Engineering Leadership in Sports Technology Building software for athletes and coaches introduces a unique level of responsibility. Accuracy matters. Latency matters. And usability can directly impact performance outcomes. Kody shared how engineering leadership plays a critical role in balancing innovation with reliability. As teams scale and systems become more complex, leaders must create clarity—aligning teams around shared goals, platform standards, and quality expectations. > In performance-critical systems, leadership is often the difference between technical complexity and user trust. ## Why Flutter Is a Strong Choice for Cross-Platform Mobile Development One of the central themes of the episode was **Flutter’s role as a foundational platform** at Foresight Sports. By adopting Flutter for cross-platform mobile development, Kody’s team was able to: - Maintain a single shared codebase across iOS and Android - Deliver consistent UI and interaction patterns - Reduce overhead from maintaining multiple native stacks - Move faster without sacrificing performance or quality Flutter’s flexibility and performance characteristics made it possible to support rich visuals and complex interactions—an essential requirement for sports technology applications. ## Bringing Interactive 3D Visualization into Mobile Apps A standout part of the conversation was how Foresight Sports integrates **fully interactive 3D models** into its mobile experiences. These aren’t static visuals—they’re tools that help athletes and coaches interpret complex performance data in intuitive ways. Kody discussed the technical challenges of rendering 3D content while maintaining performance on mobile devices, as well as the importance of ensuring that visualization enhances understanding rather than overwhelming users. > Visualization isn’t just about aesthetics—it’s about turning data into actionable insight. ## Hardware & Software Collaboration at Scale Sports technology often sits at the intersection of **hardware sensors, real-time data, and mobile software**. Kody emphasized that success in this space requires tight collaboration across disciplines—engineering teams can’t afford to operate in silos. By aligning hardware, firmware, and software teams early and often, Foresight Sports ensures that product decisions account for real-world constraints and user needs. > The closer software is to the physical world, the more critical cross-functional collaboration becomes. ## Lessons for Engineering Teams Beyond Sports Tech While this episode focuses on sports technology, the lessons extend far beyond the golf industry. Engineering leaders across industries can apply these insights when building sophisticated digital products: - Platform choices directly affect long-term velocity - Cross-platform frameworks can unlock efficiency at scale - Visualization can dramatically improve user comprehension - Strong leadership keeps teams aligned as complexity grows Whether you’re evaluating Flutter, building data-rich mobile experiences, or leading teams through technical scale, this episode offers practical, real-world guidance. 🎧 [Listen to the full episode now!](https://verygood.ventures/podcasts/kody-peterson-foresight-sports-rebuilding-mobile-architecture-with-flutter-and-3d-innovation/) ### Gamification & Behavior Design: Designing Digital Products People Want to Use Source: https://verygood.ventures/blog/gamification-behavior-design-designing-digital-products-people-want-to-use/ Gamification is often misunderstood as simply adding points, badges, or leaderboards to an app. But when done well, gamification is much more than surface-level rewards; it’s a thoughtful application of **behavior design** that helps users build habits, stay engaged, and achieve meaningful outcomes. At **Very Good Ventures (VGV)**, we believe great digital products don’t manipulate users—they **empower** them. By combining ethical behavior design with high-quality engineering and design systems, teams can create experiences that are both engaging and genuinely valuable. Here we’ll explore what gamification really means, how behavior design works, and how product teams can apply these principles responsibly to build better software. ## What Is Gamification (Really)? Gamification is the practice of applying **game design principles** to non-game contexts to influence user behavior. These principles can include: - Progression systems - Feedback loops - Challenges and goals - Social interaction - Rewards and recognition But gamification isn't about turning every product into a game. It’s about understanding **why games are engaging** and applying those insights to real-world problems—whether that’s onboarding new users, encouraging healthier habits, or increasing long-term retention. ## Behavior Design: The Foundation of Gamification At the core of effective gamification is **behavior design**, the study of how and why people take action. One commonly referenced framework is the **Behavior Model**, which states that behavior occurs when three elements converge: 1. **Motivation** – The user wants to do something 2. **Ability** – The user feels capable of doing it 3. **Prompt** – The right trigger appears at the right time Gamification works when it increases motivation, reduces friction, or delivers prompts in a timely and contextual way. ### Example: Instead of reminding users to “complete your profile,” a behavior-designed system might: - Show progress visually (motivation) - Break the task into small steps (ability) - Prompt users immediately after a successful action (timing) ## Ethical Gamification vs. Dark Patterns It’s important to realize that not all gamification is good gamification. Poorly designed systems can rely on: - Fear of missing out (FOMO) - Infinite streak pressure - Addictive reward loops with no user benefit At Very Good Ventures, we advocate for **ethical behavior design**, which means: - Aligning business goals with user goals - Being transparent about incentives - Avoiding coercive or manipulative mechanics Ethical gamification respects users’ autonomy while still creating delightful, motivating experiences. ## Common Gamification Mechanics That Actually Work When gamification succeeds, it’s rarely because of a single mechanic. It works because multiple systems reinforce one another—supporting motivation, reducing friction, and guiding users toward meaningful outcomes. Below are some of the most effective gamification mechanics when applied thoughtfully and ethically. ### 1. Progress & Mastery Progress systems make abstract effort visible. Whether through progress bars, levels, skill trees, or completion states, they help users understand where they are and what comes next. ##### Why it Works Humans are naturally motivated by a sense of advancement. Seeing progress activates a desire to finish what we’ve started and to improve over time. Mastery-based systems shift motivation from short-term rewards to long-term growth. ##### How to Design it Well - Break large goals into small, achievable steps - Show both *current state* and *next milestone* - Emphasize skill development, not just completion ##### Common Pitfalls - Progress that feels arbitrary or meaningless - Endless leveling with no sense of mastery - Progress bars that don’t reflect real effort ##### Good Fit For Onboarding flows, learning platforms, habit-building apps, and complex enterprise tools. ### 2. Feedback Loops Feedback loops tell users that their actions matter. This can be as simple as a micro-animation, a confirmation message, or a visual change in the UI after an action is completed. ##### Why it Works Immediate feedback reinforces learning. When users understand the cause-and-effect relationship between their actions and outcomes, they build confidence and momentum. ##### How to Design It Well - Keep feedback fast, clear, and proportional - Match the tone of feedback to the action’s importance - Use visual and motion cues to reduce cognitive load ##### Common Pitfalls - Overloading users with noisy or excessive feedback - Delayed responses that break the sense of flow - Feedback that feels performative instead of informative ##### Good Fit For Transactional products, productivity tools, forms, and workflows where clarity is critical. ### 3. Goals & Challenges Goals provide direction, while challenges introduce structure and focus. They help users know what to do next without having to think too hard about it. ##### Why It Works Clear goals reduce ambiguity and decision fatigue. Challenges add just enough difficulty to keep users engaged without overwhelming them. ##### How to Design It Well - Offer achievable goals early to build confidence - Gradually increase complexity as users gain competence - Allow users to opt into challenges rather than forcing them ##### Common Pitfalls - Goals that feel imposed rather than supportive - Challenges that are too difficult too early - One-size-fits-all goals that ignore user context ##### Good Fit For Fitness apps, financial tools, education platforms, and onboarding experiences. ### 4. Rewards & Recognition Rewards can take many forms—badges, points, unlocked features, or simple acknowledgments of effort. Recognition is often more powerful than the reward itself. ##### Why It Works Rewards validate effort and reinforce positive behavior. Recognition taps into intrinsic motivation by making users feel seen and appreciated. ##### How to Design It Well - Use rewards to reinforce meaningful actions - Favor symbolic recognition over purely transactional rewards - Celebrate progress, not just end results ##### Common Pitfalls - Over-reliance on extrinsic rewards - Creating dependency on points instead of purpose - Rewards that feel disconnected from user value ##### Good Fit For Community platforms, long-term habit tracking, and products that benefit from sustained engagement. ### 5. Social Interaction & Social Proof Social mechanics leverage our natural tendency to look to others for cues on how to behave. This can include shared progress, team goals, or community milestones. ##### Why It Works People are motivated by belonging and shared achievement. Seeing others succeed—or working toward something together—can increase commitment and accountability. ##### How to Design It Well - Emphasize collaboration over competition - Give users control over what they share - Highlight collective progress, not just top performers ##### Common Pitfalls - Toxic competition or public shaming - Pressure to share more than users are comfortable with - Systems that reward only a small elite group ##### Good Fit For Team-based tools, learning communities, and social or wellness products. ### 6. Autonomy & Choice While often overlooked, autonomy is one of the most powerful motivators. Allowing users to choose *how* they engage increases ownership and satisfaction. ##### Why It Works When users feel in control, they’re more likely to internalize behaviors and stick with them long-term. ##### How to Design It Well - Offer multiple paths to the same goal - Let users customize challenges or pacing - Avoid forcing streaks or rigid schedules ##### Common Pitfalls - Too many choices without guidance - Hidden penalties for opting out - “Fake choice” where outcomes are predetermined ##### Good Fit For Habit-building apps, learning tools, and complex platforms with diverse user needs. ## Designing Mechanics That Work Together The most effective gamified systems don’t rely on a single mechanic. They combine: - **Progress** to show growth - **Feedback** to reinforce actions - **Goals** to guide behavior - **Recognition** to sustain motivation - **Autonomy** to build trust When these elements are aligned with real user value, gamification becomes a **support system**, not a distraction. ## Designing Gamified Experiences That Scale Great behavior design doesn’t stop at UX—it must be supported by robust engineering. This is where **Flutter and modern design systems** shine: - Consistent UI reinforces predictable behavior - High performance keeps feedback loops tight - Cross-platform delivery ensures behavior patterns remain intact across devices By aligning design, engineering, and behavioral insight, teams can build systems that scale without losing their human touch. ## Key Takeaways - Gamification is a tool—not a gimmick - Behavior design focuses on motivation, ability, and timing - Ethical gamification aligns user value with business outcomes - Great implementation requires both strong UX and strong engineering When done thoughtfully, gamification doesn’t just increase engagement—it helps people **change behavior in ways that matter**. ## Final Thoughts At Very Good Ventures, we help teams design and build digital products that are not only beautiful and performant, but also grounded in how people actually behave. Gamification and behavior design, when applied ethically, are powerful tools for creating experiences users return to—not because they have to, but because they want to. If you’re exploring how to apply these principles in your next product, start with empathy, design with intention, and always ask: *Is this helping our users succeed?* ### Building Enduring Enterprise Products with the Hierarchy of Engagement Source: https://verygood.ventures/blog/building-enduring-enterprise-products-with-the-hierarchy-of-engagement/ For enterprise organizations, growth is rarely the primary challenge. Reach, brand awareness, and distribution are often already in place. The real challenge lies elsewhere: **driving sustained, meaningful engagement across complex user bases, platforms, and portfolios of products**. This is where the [**Hierarchy of Engagement**](https://sarahtavel.medium.com/the-hierarchy-of-engagement-expanded-648329d60804) offers a powerful strategic lens. While often discussed in the context of startups, the framework is equally—if not more—relevant for enterprises building and modernizing digital products at scale. At Very Good Ventures (VGV), we apply the Hierarchy of Engagement to help enterprise teams move beyond surface-level adoption toward durable, value-driven engagement. ## What Is the Hierarchy of Engagement? The Hierarchy of Engagement describes three progressive levels that products must master to deliver long-term impact. Each level builds on the previous one, and enterprise teams often struggle when these stages are approached out of order. ### 1. Driving Meaningful Engagement (Not Just Adoption) At the foundation of the hierarchy is a simple but often overlooked question: **Are users engaging in the behaviors that actually deliver value?** In enterprise environments, success is frequently measured by: - Licenses provisioned - Accounts activated - Users onboarded But these metrics don’t guarantee that users are completing the product’s **core action**—the critical behavior that unlocks business value—whether that’s completing workflows, making decisions, collaborating, or acting on insights. Even at scale, organizations can over-optimize for visibility and adoption while under-investing in the behaviors that drive meaningful, repeat engagement. ### 2. Retaining Users Through Accruing Organizational Value For enterprise products, retention is less about churn and more about **relevance, dependency, and embeddedness**. Products retain enterprise users when: - Value compounds with continued use (data depth, personalization, automation) - Workflows become integral to daily operations - Switching costs increase due to integration, institutional knowledge, or process alignment In large organizations, disengagement is often silent; users don’t leave, they simply stop using features or revert to legacy tools. Designing for retention means ensuring the product continues to earn its place within complex ecosystems over time. At VGV, we see retention as a systems problem—one that spans product design, architecture, change management, and user experience. ### 3. Self-Reinforcing Engagement at Enterprise Scale At the top of the hierarchy is **self-perpetuating engagement**—when engagement doesn’t just sustain usage, but actively amplifies value across the organization. In enterprise contexts, this can take the form of: - Cross-team network effects - Shared data and collaboration loops - Standardized workflows that scale best practices - Platforms that become centers of gravity within the organization When designed well, enterprise products don’t require constant top-down enforcement. Engagement spreads laterally as teams see value created elsewhere and adopt organically. This is how internal tools become strategic platforms, and how external enterprise products become deeply embedded with customers. ## Why the Hierarchy of Engagement Matters for Enterprises Enterprise digital initiatives often fail not because of technology, but because **engagement is treated as an outcome instead of a design constraint**. The Hierarchy of Engagement helps enterprise leaders: - Align product metrics with real business outcomes - Prioritize depth of use over breadth of rollout - Avoid costly transformations that never fully land with users - Create platforms that scale engagement, not just features Rather than asking *“How many users do we have?”*, the more strategic question becomes: **“What behaviors are we enabling, and are they compounding value over time?”** ## Applying the Hierarchy of Engagement at Very Good Ventures We use the Hierarchy of Engagement to guide enterprise teams across modernization and transformation efforts: ### Platform & Portfolio Strategy - Identify core actions across products and user roles - Rationalize overlapping tools and fragmented workflows - Define engagement-driven success metrics ### Adoption, Retention & Enablement - Design onboarding and enablement around real work, not features - Ensure value accrues with continued usage - Reduce cognitive and operational friction over time ### Scaling Engagement Across the Organization - Build systems that encourage collaboration and reuse - Leverage data and feedback loops to improve engagement - Enable bottom-up adoption alongside top-down governance ## Engagement as a Strategic Advantage For enterprises, engagement isn't a growth hack—it’s a **strategic advantage**. Products that drive deep engagement: - Deliver higher ROI on digital investments - Reduce tool sprawl and inefficiency - Increase resilience during organizational change - Create durable differentiation in the market ## Learn More If your organization is investing in digital platforms, product modernization, or enterprise transformation, engagement should be a strategic priority—not an afterthought. **Very Good Ventures partners with enterprise teams** to design and build products that drive sustained engagement, measurable outcomes, and long-term value at scale. 👉 [**Contact us**](https://verygood.ventures/contact-us/) to learn how we apply frameworks like the Hierarchy of Engagement to real-world enterprise challenges and help organizations turn adoption into impact. ### GenAI vs. GenUI: What’s the Difference & Why It Matters for Product Teams Source: https://verygood.ventures/blog/genai-vs-genui-whats-the-difference-why-it-matters-for-product-teams/ As artificial intelligence continues to reshape digital products, two closely but often confused terms have emerged: Generative AI (GenAI) and Generative UI (GenUI). While they’re frequently used interchangeably, they represent distinct layers of the modern AI product stack—and understanding the difference is critical for designers, developers, and business leaders building Generative experiences.  Here we break down: - What GenAI is - What GenUI is - How they differ (and work together) - When to invest in each - Why the distinction matters for modern product development ## What is Generative AI (GenAI)? Generative AI (GenAI) refers to a type of artificial intelligence that can create new content—rather than simply analyzing or classifying existing data. ### Core Capabilities of GenAI GenAI systems can generate: - Text (e.g. large language models like GPT) - Images (e.g. diffusion models) - Code - Audio and video - Structured data (e.g. tables, schemas, summaries) Generative AI models (like Large Language Models or Diffusion Models) do this mathematically. They identify underlying patterns and structures in massive datasets and use probability to predict and construct new outputs. ### Common Examples of GenAI - Chatbots and AI assistants - Code generation tools - Image and video generators - Content summarization and translation systems - AI-powered data analysis tools  **In short:** GenAI is a creative *engine* that produces intelligence and content.  ## What Is Generative UI (GenUI)? Generative UI (GenUI) is the experience layer where Artificial Intelligence builds the User Interface in real time based on the user context. Rather than relying on fixed screens and predefined flows, GenUI systems generate interface elements on demand based on: - User intent - Context - AI output - Real-time data ### Key Characteristics of GenUI - Interfaces adapt instead of remaining static - Layouts, components, or flows may be AI-generated - Interaction models are often conversational, multimodal, or intent-based - The UI evolves as the user’s needs change ### Examples of GenUI in Practice - A chat interface that generates custom forms on the fly - An AI assistant that creates dashboards based on a question - A design tool that builds UI layouts from text prompts - An app that changes interaction patterns based on user behavior **In short:** GenUI is the *experience layer* that presents AI-generated intelligence to users. ![GenAI generates the content, GenUI presents the user experience](/assets/images/blog/genai-vs-genui-whats-the-difference-why-it-matters-for-product-teams/body-0.png) A helpful mental model:  - GenAI decides **what** to say or create - GenUI decides **how** users interact with it ## How GenAI and GenUI Work Together GenAI and GenUI are most powerful when combined. ### Example: AI-Powered Travel Planner - **GenAI** generates a personalized itinerary - **GenUI** dynamically builds: - A timeline view - Interactive maps - Editable booking cards - Context-aware recommendations Without GenUI, users might only see a long text response. Without GenAI, the interface would be static and manual. Together, they create an adaptive, intelligent product experience. ## Why the Distinction Matters Understanding the difference between GenAI and GenUI helps teams avoid common pitfalls: ### 1. Better Product Strategy Many teams invest heavily in models but underestimate the importance of interaction design. A powerful GenAI system can fail if the UI doesn’t help users: - Understand outputs - Build trust - Take action ### 2. Clearer Team Ownership - GenAI often lives with AI and backend teams - GenUI requires strong collaboration between product, design, frontend, and AI engineering teams Treating them as separate but connected disciplines leads to better outcomes. ### 3. Improved User Experience Static UIs struggle to keep up with dynamic AI outputs. GenUI allows: - Progressive disclosure - Explainability - Human-in-the-loop controls - Personalization at scale ## When Do You Need GenUI (Not Just GenAI)? You should consider GenUI if: - User intent is ambiguous or evolving - Outputs vary significantly in structure or length - Users need to refine, edit, or act on AI results - Trust, transparency, or control is critical - You’re building AI-native products (not AI features bolted onto legacy UIs) In contrast, simple GenAI integrations (e.g., background summarization) may not require fully generative interfaces. ## GenUI as the Future of AI-Native Products As AI systems move from novelty to necessity, static interfaces will become a bottleneck. GenUI represents a shift from: - Screens → Conversations - Flows → Intent - Interfaces → Experiences In the same way mobile-first design reshaped products in the 2010s, AI-native design—powered by GenUI—will define the next era of software. ## Final Thoughts - GenAI is about generating intelligence and content - GenUI is about generating experiences and interactions - You need both to build truly effective AI-powered products Teams that understand—and intentionally design for—the difference between GenAI and GenUI will be better positioned to create products that are not only powerful, but usable, trustworthy, and delightful.  Very Good Ventures makes it easy for our clients to achieve the right balance and use for their GenAI and GenUI. To see how we can help your brand, [contact our experts](https://verygood.ventures/contact-us/). ### Flutter Development FAQs: Everything You Need to Know Before Building with Flutter Source: https://verygood.ventures/blog/flutter-development-faqs-everything-you-need-to-know-before-building-with-flutter/ We get a lot of questions about Flutter: whether it’s the right fit for a product, how it holds up at scale, and what it’s like to work with a team that specializes in it, to name a few.  And over time, we noticed a pattern. As Flutter matured and teams have used it in more production settings, the focus has shifted. The conversation now spans everything from foundational questions to more nuanced ones about long-term ownership and scale.  So we asked our Flutter experts to weigh in on one easy-to-navigate guide. Our Flutter FAQs bring straightforward, experience-driven answers to your top questions.  ## What Teams are Really Asking We’re still asked plenty of foundational questions—especially by developers who are new to Flutter or evaluating it seriously for the first time. At the same time, more teams are coming to us with real constraints already in place: existing codebases, long-term maintenance expectations, and higher bars for quality.  One question we’ve seen evolve is around scale. Early on, we were asked whether Flutter *could* support large, complex apps. Today, the question is more often how to structure a Flutter codebase so it [stays maintainable as teams and requirements grow](https://verygood.ventures/podcasts/phil-rabin-sofi--enterprise-scale-flutter-modernizing-architecture-for-a-2-million-line-flutter-codebase/).  Another common shift shows up around workflows. What used to be questions about onboarding or setup have turned into conversations about consistency—how multiple teams work in the same codebase, how decisions stay aligned over time, and what needs to be in place so things don’t drift as a product evolves. ## Our Expert Flutter Answers Our Flutter FAQs collect the questions we’re asked most often and answer them directly, based on how we build, ship, and support Flutter apps in the real world. It’s a practical reference you can dip into as questions come up—whether you’re getting oriented or diving deeper. The FAQs aren’t meant to be read straight through. They’re designed to work the same way our real conversations do: jump in where you need clarity, get an answer, and move forward with better context. At Very Good Ventures, this approach extends beyond Flutter. We believe good software decisions come from clear questions, shared context, and an honest look at tradeoffs. When we see the same questions surface again and again, we treat that as a signal to document our thinking—not to prescribe a single answer, but to help teams arrive at better ones. If you’re evaluating Flutter or looking for clarity before taking the next steps, this is a solid place to start.  👉 [Explore Our FAQs](https://verygood.ventures/resources/flutter-development-agency-faq/) now! ### What Is GenUI? How Generative User Interfaces Will Redefine Hospitality Source: https://verygood.ventures/blog/what-is-genui-how-generative-user-interfaces-will-redefine-hospitality-in-2026/ Hospitality has always been an experience-driven industry, but the next five years will test whether digital experiences can finally match the personalization, trust, and fluidity of great service. Recent research highlighted in [*Five Hotel Tech Trends Shaping the Next Five Years*](https://hospitalitytech.com/five-hotel-tech-trends-shaping-next-five-years) makes one thing clear: the hotel tech stack is evolving rapidly. Agentic AI, ambient personalization, digital identity, super apps, and platform consolidation are accelerating in parallel and competing for budget and priority. **So what is GenUI, and why is it poised to revolutionize the hospitality industry? GenUI (Generative User Interfaces) is the experience layer where AI builds the user interface in real time based on the user intent and context.**   Unlike static screens and predefined flows, GenUI adapts layouts, actions, and content as conditions change—making it the natural interface layer for agentic AI, ambient personalization, and identity-aware experiences. It's the missing layer between fast-evolving AI capabilities and real human hospitality. As leaders in GenUI and the world’s leading Flutter agency, **VGV helps hospitality brands build intelligent, multi-platform product ecosystems where AI, experience, and operational workflows come together seamlessly**.  While the technology enables dynamic assembly, **VGV applies a framework we call 'Structured Creativity'** to govern it. This approach ensures that while the AI assembles the UI, it draws only from approved, brand-safe components—giving you the fluidity of AI with the safety of enterprise design. Below, we break down what the report reveals, and how GenUI paired with Flutter will unlock the next era of hospitality. ## Trend #1: Agentic AI is becoming operational—and the UI must evolve with it The report emphasizes that AI is moving beyond automation and into decision-making: systems that can recommend, plan, and act across channels. That’s a major shift. When AI becomes agentic, interfaces can no longer be static. Traditional UI patterns assume: - The user is manually navigating - The flow is linear - The choices are predefined But agentic systems thrive in real time and on deep context. To be effective, the interface presented to the user must: - Adjust dynamically as intent shifts - Generate UI components that are personalized - Present AI-generated actions and options clearly - Create transparency and trust around AI decisions ✅ **This is the core promise of GenUI.** GenUI enables experiences where *the interface is generated and adapted* based on context, rather than being limited to predetermined screens. Think of a common ‘Room Booking Card’ in a traditional app, the cards look identical for every user - same photo, same list of amenities, same layout.  With GenUI, the UI adapts based on the user context: - For the business traveler: The component prioritizes a photo of the workspace, highlights the ‘High-Speed Wi-fi’ badge, and surfaces a ‘One-Click Expense Receipt’ button. - For the family vacationer: The *exact* *same* underlying component re-orders itself to feature the pool image, highlights ‘Kids Eat Free,’ and offers a ‘Connecting Room’ toggle. To achieve this vision, at VGV we build AI first experiences based on the proprietary, Structured Creativity framework.  This framework allows AI to strictly act as an orchestrator: it selects the right component and configures the content to match the user’s need, but it never violates your brand guidelines.   By decoupling the presentation layer (which is generative and adaptive) from the brand rules (which are fixed and safe), VGV enables you to deploy Agentic experiences that are not only magical for the user but safe for the enterprise. ## Trend #2: . Digital Identity should shape the screen, not just unlock it.  The report highlights that Digital Identity and Biometrics are rapidly replacing passwords and keycards. But for most apps, knowing *who* the user is simply changes the welcome message. **The Intersection with GenUI:** When you combine Biometrics with GenUI, identity becomes the architect of the experience. In a GenUI-enabled ecosystem, a biometric scan is more than a security check—it is a **contextual trigger** that instantly clears the administrative hurdles, allowing the genuine hospitality experience to begin immediately. **1\. The Guest Experience (Immediate Relevance):** The moment a guest validates their identity, the GenUI agent clears the clutter. Instead of navigating generic menus, the interface **reassembles itself** to solve the guest's immediate problem. - *The Kiosk:* It skips the 'Search Reservation' flow entirely and immediately surfaces a 'Key Card Replacement' flow because it knows the guest is already checked in but standing at the front desk without a mobile key. **2\. The Employee Experience (Empowered Service):** This is where the operational impact is most profound. For hotel staff, the 'admin work'—looking up profiles, checking status, finding preferences—often eats up the first two minutes of every interaction. - *The Staff Tablet:* With GenUI, that friction vanishes. The staff interface **generates a 'Service Context' card** the moment the guest approaches. It doesn't just show data; it suggests actions—like prompting a specific upgrade or acknowledging a special occasion—allowing the staff member to skip the transaction and focus entirely on the relationship. **The VGV Advantage:** To deliver this efficiently, you need a strategy that covers both sides of the counter. Using Flutter and **'Structured Creativity,'** we build shared components that adapt to the viewer. The same underlying logic powers the simplified guest view and the detailed staff view, ensuring that technology acts as a bridge to better service, not a barrier." ## Trend #3: Ambient personalization is the new standard—and GenUI makes it possible The report points toward personalization becoming ambient: context-driven, always on, and expected. The hard part isn’t personalizing. It’s doing it without overwhelming guests or associates. This is where [GenUI becomes a major competitive advantage](https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/). GenUI enables: - Interfaces that prioritize and simplify AI-driven recommendations - Personalized layouts, content, and calls-to-action - Real-time contextual adjustments for location, loyalty status, and preferences - Staff experiences that reduce cognitive overload In short: it turns “data plus AI” into something people can actually use. VGV’s GenUI approach is rooted in human-centered design. We help hospitality brands avoid “AI noise,” and instead create clear, confidence-building moments that feel like hospitality, not automation. ## Trend #4: Hospitality super apps will define loyalty—and Flutter is the platform advantage A Hospitality Super App is an opportunity to build a continuous digital relationship that spans the entire customer lifecycle. It connects the 'Dreaming' phase, 'Arrival', 'On-Property' experience, and 'Post-Stay' loyalty loop into one seamless narrative—integrating both internal amenities and essential external services, like local transit or event booking, so the guest never has to leave your ecosystem. The challenge is that this journey jumps across devices. A guest might research on a laptop, check in at a lobby kiosk, and order room service on a phone. Traditionally, maintaining consistency across these disjointed touchpoints requires separate development teams, resulting in fragmented experiences and bloated costs. **The Flutter + GenUI Advantage.**  This is where the combination of Flutter and GenUI becomes a strategic multiplier.  Flutter allows you to deploy a single codebase to every screen the user touches. **Flutter** provides the essential foundation: it is the framework that enables you to deploy high-performance, pixel-perfect experiences to mobile, web, and embedded screens from a single codebase. **GenUI** activates this foundation, transforming static screens into adaptive interfaces that dynamically reconfigure themselves to match the user’s unique context at every stage of their journey. **The Result: One Brain, Many Screens.** By defining your 'Structured Creativity' governance once, you create a single source of brand truth that acts as a universal translator for your entire ecosystem. Whether the guest is standing at a 50-inch lobby kiosk or glancing at a 2-inch smartwatch, the GenUI agent projects the exact same approved capabilities onto the screen—ensuring that while the device changes, the relationship remains unbroken. ## Trend #5: Platform consolidation is critical—and GenUI is the strategic control pane you need The report correctly identifies that hotels are moving away from point solutions toward unified backend platforms to create a 'single view of the guest. Frontend Consolidation requires a strategic plan as well.   While fixing the backend is critical, hospitality leaders must also consider the frontend fragmentation that often remains. Unified data loses its value if it is forced through separate codebases to reach your customers (Web, Android, iOS, Kiosk), creating a disjointed experience for the guest. The Solution: Flutter provides the necessary counterpart to your backend strategy. By adopting a "One Codebase" architecture, you ensure that your unified data is presented through a unified interface. Crucially, this foundation creates the perfect stage for GenUI, allowing you to deploy a single adaptive intelligence that governs the experience across all devices—rather than managing fragmented AI silos for every screen. This closes the gap between your infrastructure investment and the actual guest experience, ensuring consistency from the server to the screen. ## GenUI leadership + Flutter execution at scale Most teams approach this era by adding tools: a chatbot, a personalization engine, an identity vendor, or a new dashboard. That creates complexity and distributed tech debt. VGV helps hospitality brands **build a cohesive GenUI ecosystem** where AI doesn’t sit beside the experience—it shapes it. We bring: - **GenUI strategy & product design** (experience systems that adapt in real time) - **Flutter excellence** (multi-platform delivery with high performance and velocity) - **Enterprise-grade architecture and delivery** (built for scale, compliance, and evolution) - **A POV rooted in outcomes** (guest satisfaction, loyalty, operational efficiency, ROI) ## The Bottom Line: GenUI is the new frontier of hospitality experience The trends in the report are real—and they’re converging quickly. But the brands that win won’t just adopt AI, identity, and personalization. They’ll build experiences that **use those capabilities intelligently**, across the guest journey, operational workflows, and every screen. That is exactly what GenUI enables. And it’s why **VGV is leading the shift, helping hospitality brands turn emerging technology into the next generation of human-centered guest experiences.** If you’re ready to explore what GenUI can unlock in your product ecosystem, we’d love to partner. ### Today’s Engineering & Product Leadership: 5 Lessons From Build to Succeed Source: https://verygood.ventures/blog/todays-engineering-product-leadership-5-lessons-from-build-to-succeed/ Here at Very Good Ventures (VGV), we created our Flutter-focused podcast, [Build to Succeed](https://verygood.ventures/podcasts/), to spotlight game-changing tech and product leaders, and to explore a question at the heart of modern product and engineering teams: What does it actually take to build the right thing, the right way, and ensure it succeeds over time? As a Flutter development agency, VGV works closely with global application development teams that are building digital products at scale in the real world. The podcast was also created because so much of today’s conversation about technology focuses on launches, hype cycles, or tools. But the real magic—and the real challenge—lives in the middle, the years between first release and long-term success. That’s where teams scale, systems break, incentives shape outcomes, and culture determines whether products thrive or slowly decay. Hosted by VGV Founder & CEO [David DeRemer](https://www.linkedin.com/in/davidderemer/), the show features leaders who have built and run high-impact products at a global scale—people who live closer to the work than the headlines. They’ve shared candid stories from Nubank, Wendy’s, Etsy, Headspace, PGA of America, BMW, Google, and many others. These aren’t abstract theories. They’re playbooks earned through trial and error, breakthroughs, and years of hands-on experience. Throughout the season, these conversations revealed clear themes about how modern teams actually build in Flutter, and what they've learned as they ship, learn, and grow. In this article, we synthesize those learnings into the top engineering and product leadership trends shaping the next era of software and mobile app development. ## 1. Scaling Technology & Teams Almost every guest on *Build to Succeed* was dealing with the challenges of scaling a product. But “scale” meant very different things depending on where they sat in their organization. These stories provide a rare look at how companies that build Flutter apps and global application development teams scale architecture, culture, and software responsibly. Nubank’s approach has become something many Flutter agencies and mobile development teams reference as an example of Flutter architecture and modern app building best practice. [Thiago Ghisi, former Director of Engineering at Nubank](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/), described a world of extreme scale: over 100 million customers, daily global releases, and a mobile experience with more than a thousand screens. You might expect the story to be about a heroic architecture diagram, but the real unlock was organizational. Teams owned experiences end-to-end, and “glue engineers” connected dots across boundaries. Canonical platforms and patterns meant thousands of developers could move quickly without colliding. [Brian Abston, Senior Director of Digital Technology at The Wendy’s Company](https://verygood.ventures/podcasts/brian-abston-the-wendys-company-redefining-qsr-tech-with-quality-at-the-core/), brought scale into the physical world. 6,000 restaurants (“6,000 little data centers,” as he put it) each needing to stay in lockstep with the mobile app, marketing campaigns, and in-store operations. Their old native setup simply couldn’t keep up with the pace of promotions, menu changes, and loyalty features. “Building a functioning app is easy. Getting it production-ready is hard. Scaling it for 40 million users is another thing entirely,” he reflected.  Flutter wasn’t just a new UI layer; it was the bridge that helped marketing, operations, and engineering trust each other. Delivery speed doubled, and instead of the app being a bottleneck, it became a growth engine—an outcome many Flutter agencies and companies that build Flutter apps strive to achieve. [Landon Robinson, Staff Engineer at Etsy](https://verygood.ventures/podcasts/landon-robinson-etsy-from-platform-silos-to-product-unity-a-new-era-of-app-development/), zoomed in on a different kind of scaling—the complexity of a two-sided marketplace. The seller app team was small, but their impact needed to be enormous. Etsy’s answer was permeability—or the “away team” model—where engineers from across the company were temporarily embedded into the team, which worked because the platform itself was approachable. Flutter allowed newcomers to contribute without months of ramp-up. Gradually, identities shifted from “iOS engineer” or “Android engineer” to something more important—“Etsy engineer”. [And then there was Chris Synan, VP of Engineering at Keller Williams](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/). When Chris Synan arrived, the organization’s app had a crash rate north of 20%, a sprawling React Native codebase, and releases happening once a quarter. The obvious answer might have been to rewrite the app*,* but that alone wouldn’t have fixed anything.  The real transformation started when the team adopted Flutter—paired with Very Good Start, a modern CI/CD pipeline, thousands of automated tests, and a cultural reset around ownership and transparency. Leadership supported a “go slower to go fast” approach, giving the team permission to stabilize, test, and rebuild trust in the release process before speeding anything up. The shift was dramatic. After years of slow, painful releases, Keller Williams went from quarterly deploys to weekly ones—so fast, in fact, that the business eventually asked the Flutter engineering team to slow down. Across all of these stories, the pattern that’s hard to ignore is that teams don’t just scale by hiring more people; they scale when they remove friction in their code, their process, and their incentives. This principle underpins much of modern Flutter development and explains why organizations increasingly seek a trusted Flutter app development agency. ## 2. The Future of Development: AI, LLMs, Low-Code & Cross-Platform If 2018–2022 was about proving that cross-platform frameworks like Flutter could handle serious workloads, 2023–2025 has been about something even bigger— rethinking how software gets written in the first place. Across conversations with folks, one idea resurfaced again and again: AI is dramatically changing how we build software. But it’s nowhere near ready to decide what we should build. As organizations rethink what it means to deliver modern digital experiences, one truth is becoming unavoidable—the future of development won’t be defined by a single technology, but by how AI, low-code tools, and cross-platform frameworks work together. [In our conversation with Dan Hou, Founder and Partner at Eskridge](https://verygood.ventures/podcasts/dan-hou-eskridge-driving-high-impact-solutions-with-ai-native-workflows/), he emphasized that AI is no longer a novelty—it’s a foundational shift. Companies are already using AI to streamline internal workflows, automate decision cycles, and unlock new levels of personalization inside their digital products. But the real opportunity isn’t just in creating efficiency; it’s in reimagining how products are built. Dan cautions that businesses often get stuck before they start, overthinking governance or trying to design the “perfect” AI plan. Instead, he argues for a pragmatic approach: start small, experiment quickly, and scale what works. Organizations that wait risk falling behind competitors who are already using AI to accelerate innovation. And while many businesses assume AI will replace traditional interfaces with chat-driven experiences, Dan challenges that assumption. Conversational UX may play a role, but it won’t replace thoughtful interface design. As he noted: “If you have to take a course to learn how to use your product, you’ve done something horribly wrong.” Instead, the most powerful experiences will combine AI-driven intelligence with intuitive UI, using tools like Flutter to deliver consistent, performant interfaces across devices—mobile, web, desktop, and emerging form factors. Multimodal AI (voice, vision, sensor data, etc.) will only accelerate this shift, enabling products to respond to context in ways that feel more natural than ever. This is where cross-platform frameworks and low-code accelerators become critical. AI dramatically increases the volume and velocity of code generation—but organizations still need a unified architecture, shared patterns, and a consistent delivery pipeline to make that code meaningful. While Flutter provides that foundation, AI multiplies it. [Andrew Brogdon, Staff Developer Relations Engineer at Google](https://verygood.ventures/podcasts/andrew-brogdon-google-developer-relations-in-the-agentic-era/), took the idea even further. If AI is going to generate more of our code, he argued, then languages like Dart need to optimize less for typing and more for reading. In that worldview, the future developer spends more time reviewing and steering than authoring from scratch. The job shifts from “I wrote this” to “I ensured this is correct, clear, and aligned with our intent” by using tools like Flutter, supported by strong architecture and thoughtful Flutter best practices, becomes even more critical. What emerges is a new development stack—human insight to set direction, AI to explore the space, cross-platform frameworks to deliver value everywhere, and disciplined systems to ensure quality. The tools are changing quickly. The need for taste, judgment, and empathy isn’t. ## 3. Leadership, Culture & High-Performing Teams Beneath all the technical conversations this season, a consistent theme emerged— real leadership. Not the flashy kind that lives in slide decks, but the everyday decisions that determine whether teams feel supported, focused, and empowered or burnt out and lost. [Michael Harker, former Vice President of Product Management at Ritchie Bros.](https://verygood.ventures/podcasts/michael-harker-ritchie-bros-the-future-of-product-leadership-ai-customer-empathy-scaling-legacy-enterprises/), highlighted adaptability as a leadership trait. Plans, he argued, are fine until real users interact with your system. Then the real work begins. For him, the best leaders are those who can acknowledge when a strategy is no longer working, without panic or ego, and help the team pivot responsibly. [Pierre Benz, Engineering Manager at Headspace](https://verygood.ventures/podcasts/pierre-benz-headspace-merging-culture-codebases-and-care-with-purpose/), brought a deeply human dimension to technical transformation–one that many Flutter development agencies would recognize immediately. His team inherited three separate apps and codebases after a merger—each with its own priorities, bugs, and release cycles. Consolidating to Flutter wasn’t just a technical decision; it was a cultural and emotional reset for the entire mobile app development team. Native engineers worried about their identity and expertise. Product managers feared losing momentum during the migration. Designers wrestled with preserving Headspace’s iconic calm while integrating new services and experiences. Pierre didn’t gloss over any of this. Instead, he treated the migration as a shared journey. Dedicated time for his engineers to learn Flutter was built into the workday, not squeezed in at night. Admitting “I don’t know Flutter yet” was normalized, not penalized. Failures were surfaced and used as teaching moments. Transparency wasn’t a slogan; it was a practice. In that light, leadership stops being about “driving change” and becomes more about creating conditions where his Flutter development team knows it’s safe to learn, to ask questions, to push back on unrealistic timelines, and to care about craft—even when deadlines loom. Again and again, our guests returned to the conclusion that great Flutter engineering outcomes aren’t the result of heroics, but the result of stable, healthy, well-led teams who can sustain excellence over time—exactly the environment needed to build custom applications that last. ## 4. Building Products the Right Way: Architecture, Process & Discipline Some products succeed almost by accident because of a motivated app building team, a forgiving market, or a lucky sequence of decisions. But the stories that stood out this season were the ones where success was deliberate, and teams invested early in Flutter architecture, disciplined process, and clarity of ownership. The work done by our very own [Head of Engineering, Jorge Coca](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/), at BMW made this particularly vivid. Instead of letting each region, platform, or team spin up its own approach, he pushed toward a unified ecosystem supported by Flutter best practices and engineering. Not for the sake of elegance, but because every additional platform, stack, or deployment path adds cognitive load. Simpler systems make it easier for people to reason about change, catch bugs, and extend functionality without introducing chaos. [Michael Gyarmathy, Staff Software Engineer at the PGA of America](https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/), followed a similar path. Golf operations depended on a patchwork of tools like paper forms, legacy systems, and scattered web portals. By consolidating into a single, cross-platform ecosystem built with Flutter, the team wasn’t just modernizing—they were making the organization legible to itself. In a world where every minute counts on tournament days, fewer moving parts means fewer surprises. Headspace provides perhaps the clearest example. After the merger with Ginger, the company found itself with three apps serving overlapping, sometimes conflicting experiences. Different tech stacks meant different levels of polish, stability, and performance—issues familiar to many companies that build flutter apps before consolidation. Pierre explained it simply: “The ecology of devices is why we chose Flutter. People use multiple devices interchangeably. We can get an app out that can cover all those devices”. The decision to move everything to Flutter wasn’t about ideology or trend-chasing. It was a recognition that mental health support can’t depend on which codebase a feature happens to live in. One platform meant one source of truth for bugs, one set of patterns for accessibility, and one path for improvements to reach everyone, something that building with Flutter enables.  You can see similar thinking in [Andrew Tunall, President of Embrace](https://verygood.ventures/podcasts/andrew-tunall-embrace-from-qa-heavy-to-ai-ready-modernizing-product-delivery/), with his work on observability. Reliability wasn’t treated as a “backend concern”, but as an intimate part of user experience. If a meditation fails to load, if a payment doesn’t go through, if a seller can’t complete a listing, that’s not a minor glitch; it’s a broken promise. The architecture—how services are composed, how they report health, and how they fail—directly shapes that experience.  What unites these leaders is an almost stubborn commitment to clarity. Clear boundaries between services, clear ownership of domains, and clear decision frameworks for when to invest in infrastructure versus features. They resist cleverness when it obscures understanding–an approach aligned with Flutter best practices taught by the best Flutter developers in the ecosystem, VGV. They choose patterns that make sense not just to their current team, but to the next one. Building products the right way isn’t a purity contest. It’s an ongoing practice of making trade-offs explicit, documenting why choices were made, and being willing to revisit them as the world changes. ## 5. Community, Collaboration & Continuous Reinvention The final trend isn’t about code at all. It’s about people gathering around code—and what that enables for the broader Flutter experience. When [Martin Remmelgas, CEO of Codemagic](https://verygood.ventures/podcasts/martin-remmelgas-codemagic----the-power-of-community-in-scaling-devops-and-ci-cd-for-flutter/), talked about the company’s early days, he didn’t start with feature lists. He started a meetup of Flutter engineers and with hallway conversations where he answered questions for free. This same grassroots energy fuels the Flutter blog, Flutter news, and community-driven Flutter development today. Codemagic didn’t grow because it shouted the loudest; it grew because it listened the longest. [Frank “Puf” van Puffelen](https://verygood.ventures/podcasts/frank-van-puffelen-puf-low-code-no-fear-building-the-future-of-apps-with-flutter-and-llms/)—ex-Googler, longtime Firebase advocate, and one of the most recognizable names in the developer community—has done something similar for Firebase. His Stack Overflow answers, talks, and explanations became a kind of shared language for the community. Many of today’s AI tools were trained, in part, on that work—but his impact wasn’t just informational, it was emotional. He made people feel that their confusion was valid, that their questions were welcome, and that learning in public was something to be proud of. “If there’s one thread through everything I’ve done, it’s this: I want more people to build more apps. Every new builder we empower expands what’s possible in tech,” he said. He made people feel welcome to build custom apps, to ask questions, and to learn in public. Andrew Brogdon’s DevRel work is, at its core, about creating on-ramps. His career is a reminder that one well-crafted tutorial, one talk, or one video can change the trajectory of thousands of potential Flutter engineers, who in turn go on to build things he’ll never see. Across all these voices, one thing becomes obvious: ecosystems win, not just products. A strong community helps tools survive hype cycles. It catches you when documentation lags. It creates informal mentorship loops. It keeps standards from decaying. It pushes back on bad ideas and amplifies Flutter best practices. And at the individual level, a personal culture of reinvention is becoming non-negotiable. The Flutter frameworks we use today will evolve. The AI tools we lean on now will look primitive soon enough. The organization's charts that we navigate will be redrawn. The engineers, designers, and leaders who will thrive in that reality are the ones who treat learning as a lifestyle, not a phase. They try things they’re not yet good at, choose communities where they can both give and receive help, and remain skeptical of hype, but open to possibilities. Those are the teams that are truly built to succeed. ### How SoFi Scales Mobile Engineering with Flutter and AI Source: https://verygood.ventures/blog/how-sofi-scales-mobile-engineering-with-flutter-and-ai/ In our latest Build to Succeed episode, Phil Rabin, SoFi's Director of Mobile Engineering, shares what it really takes to scale one of the largest Flutter teams in production—while managing explosive code growth, adopting AI-driven workflows, and shaping the culture and systems that keep engineers moving fast. Here we highlight key insights from our conversation. ## The Early Journey That Forged a Mobile Engineering Leader Phil’s path into engineering leadership didn’t begin in a Silicon Valley unicorn—it started in Vancouver, in the ’90s, with a teenager who liked building websites when almost nobody else around him did. That curiosity led him to the CBC (the Canadian Broadcasting Corporation), where he helped build CBC Radio 3, a pioneering digital music platform that became a backbone for indie music in Canada. Long before “creator platforms” were a buzzword, Phil and his team were experimenting with social listening, streaming, and interactive features. From there, he moved into startups, then eventually to San Francisco. One startup folded (“classic story,” as he puts it), but another opportunity quickly followed: Uber. At Uber, Phil joined as one of the first 75 engineers and one of the earliest mobile engineers. He learned mobile on the job, just as the company was becoming a global phenomenon. Over the years, he built and led platform work for the core Rider app, Jump bikes and scooters, and Uber Eats.  He watched Uber evolve from a few hundred people to tens of thousands. He saw the growing pains, the scaling problems, the architectural resets, the cultural shifts. And he saw a pattern: As systems grow, the problems change. Early on, the challenge is “Can we build this?” At scale, the challenge is “Can we keep this understandable and sustainable?” ## From Uber’s Hyper-Growth to SoFi’s Super App Vision After nearly a decade of hyper-growth at Uber, Phil stepped away for a well-earned break—but it didn’t last long. As he watched the AI boom accelerate, he realized he wasn’t done building. He wanted to return to an environment where his experience scaling teams and systems could make an outsized impact.  That opportunity came when SoFi reached out. The company was in the midst of its own transformation, evolving toward a mobile-first “financial super app” and preparing to dramatically scale both its team and its Flutter codebase. Phil was looking for a director-level role at a growth-stage company facing real scaling challenges, and SoFi checked every box.  > **“Code Is Becoming Free to Write”** This is one of the most striking ideas Phil brings to the conversation. When SoFi rewrote its mobile app in Flutter, the codebase started growing quickly. Then AI tools entered the picture, and the growth curve suddenly went from steep to *very* steep. Phil lays out the numbers almost casually, but they’re stunning: “In the first couple of years, we added about a million lines of code. Last year, another million, and 500,000 last quarter alone. AI tools and a growing team make the curve steep. At that scale, code management becomes its own engineering problem—we’ve had to evolve our developer tools and workflows just to keep up.” It’s not just that more features are shipping; it’s that AI-assisted development is allowing teams to produce code—including tests—at a pace that would have been unimaginable a few years ago. A thousand lines of code per engineer per month doesn’t sound wild until you realize AI can generate hundreds of lines in seconds, especially for test scaffolding and boilerplate. For SoFi, that means new products like SoFi Pay can be backed by thousands of tests generated and refined in a remarkably short period of time. But the flip side is just as important: Once you reach millions of lines of code, the codebase itself becomes a platform that needs architecture, governance, and tooling just as much as any product feature. Build times, CI pipelines, indexing, and test suites can all become drag factors if they’re not deliberately engineered for scale. ## Why Flutter Made Sense for SoFi By the time Phil joined SoFi, the company had already made a big their bet: the mobile app would be built in Flutter. For someone who spent years living the pain of maintaining separate native iOS and Android apps at Uber, that decision was a welcome one. “The decision to use Flutter was an awesome one”, Phil says. “With Flutter, everything is pixel-perfect across devices, since the rendering engine ships with the binary. At Uber, keeping two apps in sync was a huge effort. Flutter’s made it dead easy comparatively.  At Uber, balancing teams across platforms was a constant headache. If one team ended up with three iOS engineers and one Android engineer, the roadmap drifted. Features landed on one platform earlier, the other lagged. Keeping UX consistent across devices required enormous overhead and meticulous coordination. With Flutter, Phil sees a fundamentally different dynamic: - One codebase, not two - One set of engineering standards - One training pipeline for new engineers - Pixel-perfect consistency across devices For a company positioning itself as a “financial super app,” that consistency isn’t just nice—it’s strategic. It means the mobile team can move faster without fighting fragmentation every step of the way. ## You Can’t Scale Without a Platform Team Even with Flutter as a unifying layer, SoFi’s growth created new scaling challenges. More engineers, more features, more products, more lines of code—and all of it shipping weekly into a single app. To keep that from collapsing under its own weight, SoFi made a deliberate investment in platform engineering. As Phil puts it, “You can't get to scale without having a strong platform team. The goal is to make product engineering fast and easy. You want the product teams to be able to crank out new features, get products to market really fast, iterate on UIs, find the product-market fit, and onboard new users.” Phil’s platform team is highly senior—staff and principal-level engineers who live and breathe architecture, tooling, and developer experience. Their mission is simple and ambitious: - Make it *easy* to do things the right way. - Make it *hard* to accidentally do things the wrong way. That means: - Building and enforcing design systems - Removing friction from CI and release pipelines - Modularizing the app so that not everything lives in a single massive package - Moving away from global service locators toward proper dependency injection with tools like Riverpod ## Turning the Codebase into a Code Factory Phil often talks about SoFi’s engineering environment as a code factory—not because it’s mechanical or soulless, but because at scale, reliable production becomes a discipline in itself:  > “You have to do capacity planning, start looking at all of your pipelines, and try to speed up development time, onboarding, and CI runs,” he explains. “You’re looking for efficiencies across your entire pipeline. You’re generating a giant code factory, and that code has to go out every single week. So there’s automation across every part of your stack. Pushing code out and getting everything tested becomes a big effort, so all those things need to scale up.” > Every part of the pipeline becomes its own engineering problem: - How fast can a new engineer become productive? - How long does it take to run tests on a merge request? - How stable is the build? - How often does CI get in the way instead of helping? - How can you shard tests or modularize code to keep everything manageable? At SoFi’s scale, even “simple” things like switching branches or indexing a repo matter. They shape how engineers experience their day-to-day work. A little friction might not seem like a big deal until you multiply it by hundreds of people, every day, for months. ## A Simple Rule That Changed the Culture One of the most surprisingly powerful moments in the conversation isn’t about tools or architecture. It’s about how knowledge moves through a team. Phil describes a cultural rule he introduced. > “One of the most powerful cultural shifts we made was a simple rule: never answer a Slack question without a link or documentation. It forces the team to document the right way to do things, and it eliminates repeated questions. Over time, that one habit transformed how knowledge moved through the organization.” > Instead of long Slack threads with slightly different answers every time a problem resurfaces, engineers are gently nudged toward: - Writing documentation - Improving existing docs - Sharing links instead of opinions It’s a small behavior change with a huge ripple effect. Over time, it turns tribal knowledge into shared institutional knowledge. It makes onboarding smoother. It reduces confusion. And it reinforces a culture where clarity and consistency matter just as much as raw speed. ## Leadership as Energy Management For all the talk about scale, pipelines, and frameworks, Phil keeps coming back to something very human: how people feel at work. He believes deeply in radical candor and in treating people well—not as a nice-to-have, but as a strategic necessity. If people are misaligned, unclear, or stuck in constant friction, they burn out. If they’re doing hard, meaningful work with support and trust, they’ll pour energy into it. Phil imagines the book he might one day write about his career and leadership philosophy. The title he gives is simple and telling—“Treating People Right, and How to Motivate Teams Without Burning Out.” His view is straightforward. People love doing hard things *when* they’re aligned with the mission, supported by good systems, and given real opportunities to grow. The job of a leader is to create that environment—especially when everything around them is scaling fast. [**Listen to the Full Episode Here **](https://verygood.ventures/podcasts/phil-rabin-sofi--enterprise-scale-flutter-modernizing-architecture-for-a-2-million-line-flutter-codebase/) For engineering leaders, product leaders, and anyone building ambitious digital products, Phil’s experience offers something rare: a ground-level view of what it actually means to scale in this new era—beyond hype, beyond tools, down to people, practices, and decisions. If you’re navigating similar questions at your own organization, or you’re just curious how teams like SoFi’s operate behind the scenes, this episode is a must-listen! ### Flutter CustomPainter Tutorial: SVGs, Animations & Advanced Painting Source: https://verygood.ventures/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/ Have you ever looked at a design and thought, "That looks doable with standard Flutter widgets," only for the designer to send over an update that turns everything upside down? Then the next revision arrives, and suddenly the background needs to feel “organic and irregular,” or the UI calls for a speedometer or custom data visualization. ![Design mockup requiring organic shapes beyond standard Flutter widgets](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-0.png) Before we get into how to tackle challenges like these, let’s take a look at what’s possible. Here’s a sneak peek at the final demo we’ll be breaking down in this post—a real-time racetrack animation built for a NASCAR team. ![__wf_reserved_inherit](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-1.gif) You might look at that and think *there is no `RaceTrack` widget in Flutter.* And you'd be right. Trying to build organic curves and real-time path animations with standard rows, columns, or stacks would be nearly impossible. This is exactly where **`CustomPainter`** comes in. It allows you to break free from standard widget constraints and bridge the gap between complex design requirements and functional code.  In this post, we’ll dive into mastering `CustomPainter`, specifically focusing on `drawPath` and how to manipulate paths under the hood to build dynamic, high-performance UIs. ## The Foundation: Flutter’s Rendering Pipeline To understand why `CustomPainter` is so powerful, we need to look at what happens before a pixel hits the screen. Flutter relies on three main trees: 1. **The Widget Tree:** This is the layer you interact with as a developer. Think of widgets as immutable blueprints or configuration files for your UI. They describe *what* the view should look like given the current state. Because they’re incredibly lightweight and cheap to create, Flutter can destroy and rebuild them frequently without performance issues. 2. **The Element Tree:** The glue between widgets and rendering. Elements are mutable and manage the lifecycle of widgets to minimize expensive updates. 3. **The RenderObject Tree:** The heavy lifters. These mutable objects handle layout, painting, and hit testing. ![Flutter rendering pipeline: Widget Tree, Element Tree, and RenderObject Tree](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-2.png) When you use a `CustomPaint` widget, Flutter creates a `RenderCustomPaint` object. During the paint phase, instead of painting directly, it delegates the job to your `CustomPainter`. Essentially, Flutter hands you the canvas and says *you draw what goes here.* You aren't breaking the pipeline; you’re plugging directly into it. ![CustomPaint widget delegating to RenderCustomPaint during the paint phase](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-3.png) ## Meet the CustomPainter `CustomPainter` is an abstract class that gives you direct control over a `Canvas`. To use it, you extend the class and implement two main methods: ### 1. paint(Canvas canvas, Size size) This is where the magic happens. You receive a `Canvas` (your drawing surface) and a `Size` (the drawing area). ### 2. shouldRepaint (CustomPainter oldDelegate) This is critical for performance. It tells Flutter whether the canvas needs to be redrawn when the widget rebuilds. If nothing visual has changed, returning `false` saves resources. ![CustomPainter paint() and shouldRepaint() method signatures](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-4.png) Once defined, you plug it into the widget tree using a `CustomPaint` widget: ![CustomPaint widget usage in the widget tree with painter parameter](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-5.png) ### Adding Interactivity & Accessibility Beyond drawing, you can override optional methods to make your painter interactive and accessible: - **`hitTest(Offset position)`**: By default, `CustomPainter` ignores taps. Overriding this lets you detect if a user's touch hits a specific drawn shape or icon. - **`semanticsBuilder`**: This getter allows you to return a list of `CustomPainterSemantics`, describing parts of your drawing (like a chart or progress ring) so screen readers can announce them properly. - **`shouldRebuildSemantics(oldDelegate)`**: Similar to `shouldRepaint`, this tells Flutter when the semantic description has changed and needs an update. ## The Canvas API Now let’s talk about the **`Canvas`** itself, which is provided as a parameter inside the `paint` method. Think of the Canvas as a literal drawing surface or a blank digital piece of paper— it’s the interface you use to issue drawing commands to the GPU. The coordinate system starts at `(0,0)` in the **top-left corner**. - **X** increases as you move right. - **Y** increases as you move down. ![Canvas coordinate system with origin at top-left, X right, Y down](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-6.png) Drawing operations work in layers, just like stacking sheets of paper. The order in which you write your code determines the order in which shapes are drawn—what you write first ends up in the background. Take the example below: ![Canvas drawing code with layered shapes demonstrating draw order](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-7.png) ![Rendered output of layered canvas drawing with overlapping shapes](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-8.png) The API provides methods for basic shapes (`drawRect, drawCircle`,etc), text (`drawParagraph`), vertices (`drawVertices`), and images (`drawImageRect`). However, the most versatile tool in our arsenal is **Paths**. ## Deep Dive: Paths & SVGs A `Path` in Flutter is very similar to an SVG (Scalable Vector Graphics) path. Both use commands to define shapes. If you look at the raw code of an SVG, you’ll often see a <`path>` tag with a d attribute containing a string of letters and numbers. This <`path>` tag is the equivalent to the `drawPath` method in Flutter—it’s the instruction manual for drawing a complex shape. ![SVG path d attribute compared to Flutter drawPath commands](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-9.png) ### Command Translation Table ![SVG to Flutter command translation table: M, L, C, Q, Z mappings](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-10.png) SVG also supports relative commands (lowercase letters like `m`, `l`, `c`), which calculate positions relative to the last point rather than the (`0,0`) origin. Flutter handles these with "relative" method counterparts: ![Relative SVG commands and their Flutter relative method counterparts](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-11.png) ### Putting It Together: A Real Translation To visualize this, let's look at a concrete example. In the SVG code below, the path definition (d) starts with an **M** followed by four **C** commands. ![SVG path with M and C commands translated to Flutter moveTo and cubicTo](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-12.png) To translate this into Flutter, we simply map each command one by one. The initial **M** becomes a `path.moveTo(391, 36)` to set the starting point. Then, for every **C** that follows, we add a `path.cubicTo`(...), passing in the six coordinates required for the two control points and the final destination point. By strictly following this pattern, we can replicate the exact same curve on our Flutter canvas. ### Parsing SVGs to Flutter Paths Why not just use `flutter_svg`? If you only need to *display* an image, `flutter_svg` is perfect. It converts SVG geometry into optimized binary drawing commands for rendering. However, if you need to **manipulate** the path—like animating a car along a racetrack—you need the actual `Path` object. You can parse SVG data manually or use packages like `path_drawing` to translate SVG commands into Flutter path commands (`moveTo, lineTo, cubicTo`). ## Case Study: Trackhouse Racing At Very Good Ventures, we built an app for [**Trackhouse**](https://verygood.ventures/success-stories/trackhouse-nascar-vip-experience/), a NASCAR team. The challenge? Visualize race car positions on a track map in real-time. The backend sends an SVG path of the track for that specific weekend. We have to: 1. Parse the SVG into a Flutter `Path`. 2. Draw the track and the pit lane. 3. Animate the cars moving along that path based on real-time data. ![Trackhouse Racing app showing car positions on a parsed SVG racetrack](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-13.png) So first, before we can do any cool animations, we need to translate the SVG string from the backend into a Flutter `Path`. For this, we have two different approaches: ### Approach 1: The Manual Parser To really understand how this works, let's look at how we might solve this manually. This was our initial approach to understand the data structure. We can take the `d` attribute string (the path definition) and use Regex to split it into individual commands and coordinates. Then, we loop through them and map them to Flutter methods one by one: ![Manual SVG parser using regex to split path commands into Flutter methods](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-14.png) This works, and it's a great way to learn, but it can be brittle and hard to maintain as path complexity grows. ### Approach 2: The Parsing Combo (`xml` + `path_drawing`) For the production app, we streamlined this by combining two powerful packages: 1. **`xml`**: We use this to parse the raw SVG string and navigate the XML tree. We search for the specific `` element we need (e.g., filtering by an ID like "track" or "pit\_stop") and extract its d attribute. 2. **`path_drawing`**: This package does the heavy lifting of translating that d string (the sequence of `M`, `L`, `C` commands) into a native Flutter Path object using its `parseSvgPathData()` method. ![Production parser using xml and path_drawing packages with parseSvgPathData](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-15.png) ### Bringing the Track to Life Once we have our parsed path, we need to actually render it. In `CustomPainter`, we manage style using `Paint` objects. For this track map, we define separate paints for each element to control colors, stroke widths, and styles: - `trackPaint:` A thick stroke for the main track. - `pitStopPaint:` A thinner stroke for the pit lane. - `pilotPaint:` A fill paint for the car markers. ![Paint objects defined for track, pit stop, and pilot car markers](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-16.png) In the `paint()` method, we draw these elements in layers from back to front. Since the cars need to appear *on top* of the track, we draw the track first: ![Paint method drawing track, pit lane, and cars in layered order](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-17.png) ![Rendered racetrack with track stroke, pit lane, and car markers](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-18.png) Now, let’s look at two specific examples of how we draw these components: the start line (which is static but oriented) and the cars (which are dynamic and moving). ### Example 1: Painting the Start Line The start line isn't just a rectangle; it needs to sit perfectly perpendicular to the track at the starting point. But the track curves and turns, so "perpendicular" changes depending on where the start is. Instead of doing complex math to rotate every square of the checkered flag individually, we can rotate the entire *canvas*. 1. **Find the Start:** We get the track's starting position and direction (tangent) using `computeMetrics`. 2. **Orient the Canvas:** We translate the canvas to that start point and rotate it to match the track's direction. 3. **Draw Simply:** Now that the canvas is aligned, we can just draw simple vertical rectangles for the black and white checks. ![Start line code using canvas rotation to align checkered flag with track](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-19.png) ### Example 2: The Magic of `computeMetrics` for Cars How do we draw a car at exactly 60% of the track? We use `Path.computeMetrics()`. `computeMetrics` returns an iterable of `PathMetric` objects, which allow us to measure the length of the path and find specific coordinates. Here’s how we place the car: 1. **Calculate Total Length:** Sum the length of all path metrics. 2. **Determine Distance:** If the car is at 60%, the distance is `totalLength * 0.6`. 3. **Find the Tangent:** Use `getTangentForOffset(distance)` to get the (`x`, `y`) coordinate *and* the angle (direction) of the track at that point. ![computeMetrics placing a car at 60% of the track path using getTangentForOffset](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-20.png) This allows us to animate cars smoothly along complex Bézier curves without doing the heavy math ourselves–Flutter does it for us! **Performance Optimization ** When working with `CustomPainter`, performance is paramount: 1. **Use Layers:** The `Canvas` creates layers. Use `save`() before transforming (rotating/scaling) and `restore`() afterwards to isolate changes. 2. **Manage Rebuilds:** If your painter relies on an animation, pass the `Listenable` (like an `AnimationController`) to the `repaint` argument of the `CustomPainter` constructor. This triggers only the paint phase, skipping the widget rebuild. 3. **Complex Metrics:** Calculating path metrics can be expensive. If the path doesn't change, calculate metrics once and cache them, rather than re-calculating inside `paint()` every frame. ## Conclusion `CustomPainter` might seem intimidating at first, but it’s just a direct line to Flutter's rendering engine. By understanding how to manipulate Paths and leveraging tools like `computeMetrics`, you can build highly custom, performant, and dynamic UIs that standard widgets just can't handle. So next time a designer hands you an "impossible" design, you know what to do. ### Watch the Talk ![Conference talk slides for Mastering CustomPainter sessions](/assets/images/blog/mastering-custompainter-in-flutter-from-svgs-to-racetracks/body-21.png) I had the opportunity to present this topic deep dive at several conferences around the world, including **Fluttercon USA** in New York City, **Flutterconf Latam** in Quito, and **Fluttercon EU** in Berlin. If you want to hear more details, you can check out the recordings below: - 🇺🇲[Fluttercon USA 25](https://www.youtube.com/watch?v=oTYUt7aL6TE) - 🇪🇨[Flutterconf Latam 25](https://youtu.be/6VJJNjSuXgo?t=23061) - 🇩🇪[Fluttercon EU 25](https://www.youtube.com/watch?v=0cYVZJ_n5eg) ### Inside Codemagic’s Rise: Flutter, Community, and the Future of Developer Tools Source: https://verygood.ventures/blog/inside-codemagics-rise-flutter-community-and-the-future-of-developer-tools/ [On this episode of *Build to Succeed*](https://verygood.ventures/podcasts/martin-remmelgas-codemagic----the-power-of-community-in-scaling-devops-and-ci-cd-for-flutter/), we welcomed Martin Remmelgas, CEO of Codemagic, who shares how an unexpected journey—from studying chemistry in Scotland to doing door-to-door sales in the US—ultimately led him to build one of the Flutter ecosystem’s most recognizable developer tools. ## The Journey That Sparked Codemagic Martin began his career far from software. He planned to become a chemist and even spent a year working in Singapore during his degree. But along the way, his side job selling door-to-door in Montana, Indiana, and Texas turned out to be surprisingly transformative. The work was difficult and unpredictable, but it taught him persistence, attention to detail, and the value of small optimizations—traits that would later influence how Codemagic builds tools for developers. During his industrial placement in Singapore, Martin started to question the decades-long path required in chemistry. Technology, by contrast, offered immediate feedback and the chance to build something from scratch without waiting years or raising significant capital. That realization ultimately pulled him toward startups. ## How an Internal Experiment Became Codemagic After university, Martin joined Ernst & Young, but stayed close to the entrepreneurial community. One mentor at that time (who was the CTO at Nevercode) invited him to join a team experimenting with turning their internal DevOps knowledge into a product. Nevercode began as a development agency that struggled with the complexity of tools like Jenkins and Hudson, and eventually built its own internal CI system. That internal project became Nevercode’s first [CI/CD tool](https://codemagic.io/start/) and ultimately set the stage for [Codemagic](https://codemagic.io/start/). Martin joined at a perfect moment—just as Flutter’s momentum was building. ## A Bet on Flutter At early conferences like droidcon Berlin, the Nevercode team saw demos of Flutter that stood out. The technology felt modern, and the energy in the crowd was undeniable. Developers were excited about Flutter in a way that felt different from other frameworks. Nevercode quickly hacked together early Flutter build support and shared it publicly. To their surprise, Tim Sneath—then leading Flutter at Google—responded right away, sparking a deeper collaboration. From there, Codemagic grew out of a simple insight: Flutter developers needed a CI/CD tool built specifically for their workflows. That focused early bet allowed Codemagic to rise in parallel with Flutter’s own growth. ## Growing Through Community Codemagic didn’t have a large marketing budget. Instead, Martin invested in something more scalable: showing up. He spent months messaging developers, attending Flutter meetups, and asking where people went to learn. What he discovered was a global network of passionate Flutter enthusiasts who welcomed companies willing to contribute, listen, and support their efforts. Rather than selling, Martin built relationships—connecting speakers, helping organizers, and contributing wherever possible. Over time, that consistency created trust. Developers began offering feedback, recommending Codemagic, and shaping its features and pricing. That input even pushed Codemagic to formalize its business model when early users questioned how a free tool could be sustainable. As Flutter matured, so did Codemagic’s customer base. What started as a tool for individuals and startups is now increasingly used by large enterprises—an evolution that Martin says has become especially clear in the last year. Part of the shift is due to Codemagic’s own growth, including earning SOC 2 Type II compliance, which made enterprise security teams far more comfortable evaluating and adopting the platform. But much of it’s also tied to Flutter becoming a mainstream, reliable choice for large organizations. The combination opened doors to major clients that Codemagic never expected to reach so soon.  ## Stellar: Browser-Based App Previews for Faster Collaboration Codemagic’s evolution hasn’t stopped at CI/CD. Recently, the team launched [Stellar](https://docs.codemagic.io/yaml-testing/app-preview/), a feature that lets developers and designers preview app builds directly in the browser—running on a real iOS simulator or Android emulator behind the scenes. Instead of passing around installs through TestFlight or physical devices, teams can simply open a link and: - Test layouts - Change locales - Try accessibility settings - Review UI changes in minutes Stellar extends Codemagic beyond build automation into smoother workflows for collaboration and iteration. ## Flutter’s Rapid Growth Means Endless Opportunities Looking at today’s ecosystem, Martin believes Flutter still offers enormous opportunities for developers and entrepreneurs. As he puts it: *"A third of all new apps are Flutter apps—and there's big demand for the tools to release and maintain these apps. There's so much opportunity here, so many developer tools that can be built." * He argues that the gap between Flutter’s massive user base and the relatively small team maintaining it at Google creates fertile ground for innovation, from testing to performance tooling to deployment. The signals are everywhere—GitHub issues, Reddit threads, X sentiment—and modern AI tools make it easier than ever to scan the ecosystem for unmet needs. ## Competition Isn’t a Threat: It’s Fuel Codemagic faces competition from both large CI/CD platforms and smaller, specialized tools. Martin sees that as a good thing: *“We always have to answer: What sets us apart? Competition forces you to innovate faster and better. If you don't set yourself apart, you're going to die—and that's what leads to better products." * In his view, competition pushes teams to refine their message, improve their product, and ultimately deliver more value to developers. ## Final Reflections: Trust Yourself, But Stay Grounded As the conversation wraps, Martin offers advice to anyone building tools in the Flutter space: - Trust your instincts—but check when optimism slips into bias. - Start with a niche and go deep, rather than trying to serve everyone at once. - Show up in the community and invest early: those relationships compound. - Listen to users; they’ll shape your decisions more than any strategy doc. - Welcome competition; it’s what keeps an ecosystem healthy. Codemagic’s journey shows how deeply a company’s success can be tied to the community it serves—and how the right combination of timing, trust, and persistence can turn a small experiment into a platform used by teams around the world. If you’ve made it this far, you’ll definitely want to [hear the full episode](https://verygood.ventures/podcasts/martin-remmelgas-codemagic----the-power-of-community-in-scaling-devops-and-ci-cd-for-flutter/)—it’s well worth the listen. ### Creating Your First Dart Analyzer Plugin with the New Plugin System Source: https://verygood.ventures/blog/creating-your-first-dart-analyzer-plugin-with-the-new-plugin-system/ At Very Good Ventures (VGV), we believe that code quality isn’t just about making things work—it’s about building in a consistent, scalable way. That’s why we rely heavily on our [Very Good Analysis](https://pub.dev/packages/very_good_analysis) package and document our engineering standards publicly through [Very Good Engineering](https://engineering.verygood.ventures/).  With the new release of [Flutter 3.38 and Dart 3.10](https://blog.flutter.dev/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps-503429eeb685), the ecosystem gained a much-needed, modernized [analyzer plugin system](https://github.com/dart-lang/sdk/issues/53402). To recap: The analyzer plugin system is a feature that allows developers to create additional checks and rules that don't exist out of the box in the Dart analyzer, effectively allowing them to expand that tool. A plugin system in the dart analyzer isn’t something totally new. A plugin system already existed, but it presented two main issues: - **Memory Usage:** The need of having one isolate per plugin meant running multiple plugins could cause significant memory issues. - **Not a Single Command:** Using different custom lint libraries required running different commands (not just dart analyze). The new plugin system solves these issues and makes it much easier for developers to build plugins effectively. It also brings many considerations overall, so if you’re curious about the decision process behind it, make sure to read the original [proposal](https://docs.google.com/document/d/1EwnYlgOd2pmT53lmZ3kJg6rZimOGG8ZI4OzMxlxGqJM/edit?tab=t.0).  Now, let’s deep dive into this new exciting change for the Dart ecosystem.

Engineering Docs

The lint rules we ship, and why

Effective Dart plus very_good_analysis, with the specific practices we enforce on every project.

Read our code style docs

## How to Create a Package Once you have a package created (you can do so with [Very Good CLI](https://cli.vgv.dev/)), the very first step is to declare a new plugin using the dependency [analysis_server_plugin](https://pub.dev/packages/analysis_server_plugin). ```dart class YourPlugin extends Plugin { @override void register(PluginRegistry registry) { // Here we will register all our rules } @override String get name => 'your_plugin_name'; } ``` You must create a new entry point for your plugin. This entry point needs to be in lib/main.dart: ```dart final plugin = YourPlugin(); ``` And you must declare in the pubspec.yaml of your package that it contains a Plugin: ```yaml plugin: platforms: dart: pluginClass: YourPlugin fileName: main.dart ``` Now, to start using this plugin in your application you just need to have an analysis\_options.yaml file with those lines: ```yaml plugins: your_plugin_name: path: yourPath ``` ## Creating Your First Rule Once you're all set up, the fun part starts: creating your own rules! Let's say your team has a best practice that requires using the isTrue/isFalse matchers used during testing instead of manually checking "true"/"false" primitive values. Something like this: ```dart // Bad expect(myVariable, true); // Good expect(myVariable, isTrue); ``` Let's create a rule that will enforce that practice. First, we will add the [analyzer](https://pub.dev/packages/analyzer) dependency. Next, we create a class to represent that rule. The basic structure of any rule will be: A class extending AnalysisRule which defines the rule name, description, and the correction message: ```dart class PreferBoolMatcher extends AnalysisRule { PreferBoolMatcher() : super( name: rule, description: _description, ); static const rule = 'prefer_bool_matcher'; static const _description = 'Prefers using `isTrue`/`isFalse` matchers in `expect(...)` calls.'; static const _correctionMessage = 'Try using the `isTrue` or `isFalse` matcher instead of the raw ' 'boolean literal.'; . . . } ``` Then, we define the DiagnosticCode, which basically will help the developer once it has the issue on the code with the appropriate message and a way to fix it: ```dart /// The code for the lint rule. static const LintCode code = LintCode( rule, _description, correctionMessage: _correctionMessage, ); @override DiagnosticCode get diagnosticCode => code; ``` To implement the code that'll do the actual checking, we use a **Visitor**. The Visitor's job is to "walk through" the code and make sure it follows a specific rule. Here's a closer look: ```dart class _Visitor extends SimpleAstVisitor { _Visitor(this.rule, this.context); final AnalysisRule rule; final RuleContext context; ... } ``` And then we override the most convenient method for us to "visit pieces of code" and perform the checking. In this case, we use visitMethodInvocation since we need to access the way we're invoking a method ("expect"). Basically, we're checking whether the method invoked is expect, and if the second argument is a BooleanLiteral (true/false): ```dart @override void visitMethodInvocation(MethodInvocation node) { if (node.methodName.name != 'expect') return; final arguments = node.argumentList.arguments; if (arguments.length < 2) return; final matcherArgument = arguments[1]; if (matcherArgument is BooleanLiteral) { rule.reportAtNode(matcherArgument); } } ``` Once we have the visitor defined, we need to register it in the rule we created above (PreferBoolMatcher). ```dart @override void registerNodeProcessors( RuleVisitorRegistry registry, RuleContext context, ) { final visitor = _Visitor(this, context); registry.addMethodInvocation(this, visitor); } ``` The easiest way to verify if your new lint rule is working is by forcing the issue with an example: ```dart void main() { const myVariable = true; // Bad - It should give you the warning expect(myVariable, true); } ``` Running dart analyze should throw this error: ![dart analyze output showing prefer_bool_matcher lint warning on expect call](/assets/images/blog/creating-your-first-dart-analyzer-plugin-with-the-new-plugin-system/body-0.png) ## Creating Your First Fix Once your first lint rule is in place, you might wonder if that's enough. While validating your code follows certain rules, it's even more useful when those issues can be automatically fixed. Assuming the previous example, we would just need to move from this: ```dart expect(myVariable, true); ``` To this: ```dart expect(myVariable, isTrue); ``` To do so, we just need to create a class that extends ResolvedCorrectionProducer, define the scope where the fix can be safely applied (CorrectionApplicability), and specify different properties such as the IDE-facing description and the fix's unique identifier. ```dart class LiteralBoolInExpectFix extends ResolvedCorrectionProducer { /// {@macro matchers_in_expect_fix} LiteralBoolInExpectFix({required super.context}); @override CorrectionApplicability get applicability => CorrectionApplicability.singleLocation; @override FixKind get fixKind => const FixKind( 'dart.fix.literalBoolInExpect', DartFixKindPriority.standard, "Use 'isTrue' or 'isFalse' matcher", ); . . . } ``` After that, we'll override the compute method, which will take care of the fix itself. In this case, we'll be replacing the bool expression true/false with isTrue/isFalse: ```dart @override Future compute(ChangeBuilder builder) async { final matcherArgument = node; if (matcherArgument is! BooleanLiteral) return; final expectedMatcher = matcherArgument.value ? 'isTrue' : 'isFalse'; await builder.addDartFileEdit(file, (builder) { builder.addSimpleReplacement( range.node(matcherArgument), expectedMatcher, ); }); } ``` If everything worked fine, on VSC for example, you'll see something like this: ![VS Code quick-fix suggestion to use isTrue or isFalse matcher](/assets/images/blog/creating-your-first-dart-analyzer-plugin-with-the-new-plugin-system/body-1.png) ## Wrapping Up We still have plenty to explore with the new plugin system, but it's already clear how promising it is. At VGV, we've constantly proven how those engineering best practices pay off in the long term—and the ability to automate lint rules that reinforce those practices is especially exciting! This is only a small glimpse of what's possible. The new plugin system unlocks much more, and we're excited to keep exploring it. *A special shout-out to Erick Zanardo, who helped me writing this blog, and Ricardo Dalarme, whose work on our early POCs helped shape many of the insights shared here.* ### How We Efficiently Onboard Engineers at Very Good Ventures Source: https://verygood.ventures/blog/how-we-efficiently-onboard-engineers-at-very-good-ventures/ At Very Good Ventures (VGV), our work often involves welcoming engineers into new projects—whether launching a new initiative, expanding a team, or supporting coverage needs. With new assignments starting almost every week, we’ve gained a deep understanding of what it means to step into a tight-knit team with established rhythms and long-held knowledge. We know that without the right structure, even the most talented engineers can feel slowed down by unclear access, siloed information, or complex enterprise environments. Because we’ve experienced a wide range of onboarding scenarios—both exceptional and less than ideal—we’ve designed a smart, cohesive process that helps teams avoid common pitfalls. Our goal is to make sure engineers feel empowered and set up for success from day one, where new engineers gain momentum quickly, and existing teams stay focused and supported.  Creating these kinds of smooth transitions starts with understanding the core reasons onboarding can break down—and building a process that proactively addresses them. ## The Four Stages of Competence To understand why weak onboarding experiences happen, it's helpful to consider the *Conscious Competence Learning Model* by Noel Burch. I first encountered this concept while [training baristas](https://verygood.ventures/blog/my-journey-from-barista-to-google-developer-expert/) nearly a decade ago, and I've since realized its direct applicability to engineers onboarding onto a project. An engineer typically begins at *Unconscious Incompetence* when starting a new project. At this stage, they’re unaware of the systems they'll need access to, the location of the codebase, or even its fundamental structure. As the onboarding process begins, they quickly realize there's a significant amount to learn. This marks the stage of *Conscious Incompetence*. This phase is often accompanied by discouragement, as contributing to tickets (especially in a complex environment) can feel like a distant goal. During this critical stage, it’s essential that other project members provide ample support. Over time, the engineer gains a solid grasp of the project and begins to make meaningful contributions. They’ve reached *Conscious Competence*. While they don't know everything, they can readily find answers to their questions. They understand the steps required to get code changes approved and merged and can reliably execute them. However, that's not the final stage. After months or even years of working on a project, an engineer attains *Unconscious Competence*. They possess such an innate understanding of the codebase and systems that they no longer need to consciously think about them. They’ve likely participated in significant changes to the codebase over time, understanding not only its structure but also the underlying rationale. At this point, they experience very little friction in closing tickets and guiding the project to success. Understanding this model helps us recognize that onboarding isn’t just about sharing credentials or setup steps—it’s about guiding someone through a psychological and technical journey from uncertainty to mastery. By identifying where new engineers are in this lifecycle, teams can tailor their support to be both empathetic and efficient.  With that context in mind, let’s look at some practical ways to smooth the path and help engineers reach competence faster. ## Practical Tips Here are some tips we've found incredibly helpful for improving the onboarding experience and reducing the time it takes for new engineers to become productive: 1. **Leverage Consciously Competent Mentors:** Your most effective mentors for onboarding are typically those in the *Conscious Competence* stage. These individuals are productive but require conscious thought to maintain that productivity. They know the processes to follow and often rely on checklists and notes to remember the steps. These practical solutions are extremely helpful to someone onboarding to the project. Conversely, those who have reached *Unconscious Competence* often have such a deep understanding of a project that they struggle to recall what it felt like to be new. They may inadvertently omit crucial steps because they perform them without thinking. For example, the experienced developers already have the necessary tools installed in their computers, know all the in-progress refactors, and remember all the tradeoffs that led to the current codebase. This is particularly true if an entire team has been working together on a project for years - it can make it seem like an insurmountable task to bring a new person up to speed. 2. **Continuously Improve Onboarding Documentation:** The simple Boy Scout saying, "Leave it better than you found it" should guide our approach to onboarding documentation. When onboarding, having information readily available in a README or other documentation is invaluable for answering questions. Yet, no one enjoys taking time out of their busy day to write documentation, especially when the information feels like second nature. At VGV, we have a policy that every person who onboards to a project must contribute something to the onboarding documentation. Eventually, we all encounter obstacles that need resolution. The expectation is that you help the next person by documenting how you overcame that issue. 3. **Key Items to Document:** When it comes to onboarding documentation, consider including the following: - What does the overall system look like? Which are the key components, who owns them, and how do they interact? Consider including a simple diagram or C4-style overview to show how the system (and teams) fit together. - What systems require access? This typically includes communication platforms (e.g., Slack), ticket tracking (e.g., Jira), code repositories (e.g., GitHub), design files (e.g., Figma), and knowledge sharing platforms (e.g., Confluence). - Which API keys are necessary to run the application locally and aren’t checked into the remote repository? Where are these keys securely stored? - What steps are required before running the application locally? Do Docker services need to be started? Is a build script necessary? Which environments should be run locally? - What is the established Git workflow? Which branch should be used as a starting point, and which branch should changes be merged into? - What are the expectations for merging code change requests? Is it necessary to update localizations, add test coverage, or ensure code is free from spelling errors? How can all of this be verified before submitting a change? - Provide a high-level overview of what each part of the codebase does. Where should an engineer look to find features they’re working on? If the project utilizes subpackages (which is excellent for separating concerns), consider adding comprehensive README files to those packages explaining their functionality in detail. - Provide an overview of who’s involved with the project, including engineers and support roles. You’ll never be able to document every question imaginable, so sharing who is available to answer questions helps reduce any time wasted bouncing between people. 4. **Integrate Onboarding Documentation with Code Changes:** Treat changes that impact onboarding with the same importance as changes to the code itself. If a code change has onboarding implications, such as introducing a new SDK that requires local configuration, don’t accept that code change until the onboarding documentation has been updated accordingly. Additionally, add a to-do item to your pull request/merge request templates requiring developers to consider if their changes necessitate updates to the onboarding documentation. Furthermore, ensure reviewers also consider this aspect during code review.  Finally, as you add new systems and features—especially before calling it 'finished' (since code and details often evolve)—add comments in the code that would help a newcomer understand its high-level purpose, intent, structure, and usage. It’s worth noting that AI tools are extremely helpful at this task. Configure your AI tools to prioritize writing concise yet helpful documentation and make sure AI code review tools are checking that large code changes are documented. 5. **Design for Easy Onboarding:** The most effective way to avoid an extensive onboarding manual is to make it inherently easy to get started. Utilize IDE features like recommended extensions and launch configurations in Visual Studio Code. Organize your code by feature, co-locating different layers (like state management and views) with easily understandable names. Establish a clear, single vault for API keys and name them simply to ensure clarity regarding what’s needed. Best practices also help a lot with this process, as there are less project-specific practices to learn. Find our [Flutter best practices and engineering standards](https://engineering.verygood.ventures/).  At VGV, caring about onboarding isn’t optional—it’s a core part of how we deliver successful projects. Every team is responsible for ensuring new engineers ramp up efficiently, because the speed and quality of onboarding directly impact project outcomes. It can’t just be the “new person’s problem.” We measure success not just by delivery metrics but by how quickly new teammates move through the four stages of competence and begin contributing confidently.  Setting an outcome-driven goal—for example, aiming for new engineers to reach conscious competence within their first week—keeps teams intentional about onboarding and reinforces our belief that great projects start with great beginnings. That’s why we treat onboarding with the same intentionality and respect given to CI/CD, testing, asset management, and other non-code aspects of a project. Just like those “external” systems only prove their worth in moments of need, great onboarding documentation becomes invaluable the moment a new engineer joins, and future teammates will thank you for having done the work ahead of time. ### From Code to Community: How Puf Inspires More People to Build More Apps Source: https://verygood.ventures/blog/from-code-to-community-how-puf-inspires-more-people-to-build-more-apps/ [On this episode of *Build to Succeed*](https://verygood.ventures/podcasts/frank-van-puffelen-puf-low-code-no-fear-building-the-future-of-apps-with-flutter-and-llms/), David sits down with Frank van Puffelen— “puf” to most of the developer world—an industry veteran, ex-Googler, and one of the most familiar names in the Firebase and Flutter communities. If you’ve ever searched for a Firebase answer on Stack Overflow, there’s a decent chance you’ve stumbled onto one of his posts. With tens of thousands of answers and tens of millions of views, his work has quietly supported a generation of developers trying to ship real products. If you care about impact as an engineer—whether through code, mentorship, or community—this conversation will resonate with you. ## From Stack Overflow Legend to DevRel Icon puf started as a curious engineer, and discovered he loved explaining things just as much as building them. Eventually turned that combination into a career in developer relations. Along the way, he’s developed strong opinions about teaching, communication, and the role AI should play in how we build software. As he puts it himself: > “If there’s one thread through everything I’ve done, it’s this: I want more people to build more apps. Every new builder we empower expands what’s possible in tech.” ## From 8-Bit Curiosity to Firebase and Flutter Puf’s relationship with technology goes back to the 8-bit era: Commodore 64, Atari, ZX Spectrum. At first, computers were just fun machines to play games on, like they were for so many of us. But at some point, the curiosity shifted from playing the game to understanding how it was made. When he started writing his first programs, he had that classic early developer epiphany: You can tell a machine what to do, and it will do exactly what you said—not what you wished you’d said. That unforgiving honesty of computers hooked him. It pushed him toward studying computer science and then spending two decades working as a software engineer. The turn toward public-facing work came almost accidentally. In his spare time, puf wandered onto Stack Overflow and started answering questions simply because it was a good way to learn. He noticed the same problems kept popping up—asynchronous logic, NoSQL data modeling, unfamiliar data structures—and he liked the challenge of not only solving them, but explaining them in a way that actually made sense to someone else. Those explanations caught the eye of the Firebase founders. What began as a habit of answering questions online turned into a job offer and, eventually, into a role where he became one of the most visible faces of Firebase—on Stack Overflow, at Google I/O, and on stages all over the world. ## Teaching as a Superpower A big thread in the episode is the idea that great engineers don’t stop at solving the problem; they learn how to explain it. For puf, that distinction is where impact really multiplies. > “Great engineers don’t just solve problems; they explain them. When you can take a complex technical challenge and turn it into a story others can follow, your impact multiplies. You stop just building software—you start building understanding.” He treats explanations almost like you’d treat a product feature: something you iterate on over time. His now-famous way of explaining async execution—the idea that code doesn’t always run in the neat 1-2-3 order you see on screen, but often in a 1-3-2 pattern—didn’t arrive fully formed. It took years of trying different metaphors, watching where people got stuck, and refining the message until he could see that “lightbulb moment” in their eyes. Part of his process is to test explanations on people who aren’t deep in the tech, including his wife. If someone outside the niche can follow along, it’s usually a sign that the explanation is genuinely clear. And one of the quiet joys he talks about is seeing those explanations escape into the wild—when other people start reusing his analogies and wording, spreading understanding further than he ever could alone. For any engineer, it’s a powerful reminder: Once you know how to make complicated things feel understandable, your influence isn’t limited to the code you personally write. ## Finding a Voice on Stage What might surprise people is that puf didn’t start out as the confident conference speaker many know today. He describes himself early on as the typical quiet engineer, the one who’d rather sit in the back of a conference room than be on stage. Large events weren’t exciting; they were draining. That changed when he realized that his enjoyment wasn’t tied to being in front of a crowd—it was tied to being helpful. When someone walked up to him at a conference with a real problem, something clicked. He could answer their questions, unblock them, and send them away better equipped than before. That feeling turned speaking from a performance into an extension of the same thing he loved about Stack Overflow. Over time, he found himself increasingly on stage: live-coding, presenting new features, and explaining how things worked. He admits that public speaking still comes with a rush of energy he has to control, but the fear that many engineers feel around it just isn’t there for him anymore. And he’s quick to point out that this doesn’t mean he never makes mistakes. In fact, he’s embraced failure as part of the experience. He talks about live demos that didn’t work and how those moments became “shared debugging experiences” rather than disasters. He invites audiences into the process, asking them to shout out typos during live coding so they feel like collaborators instead of spectators. That simple shift turns the talk into something interactive, human, and far less scary. At the core of his speaking philosophy is one simple idea: > “Public speaking terrifies so many engineers, but here’s the truth: your audience chose to be there. They’re not strangers; they’re just friends you haven't helped yet." Once you believe that, the room feels very different. You’re not being judged; you’re being invited to help. ## Rethinking AI: A New Kind of Hammer Eventually, the conversation turns to AI and large language models (LLMs)—a territory that can quickly become abstract or polarizing. puf’s take is neither breathless nor cynical. To him, AI is powerful, exciting…and still just a tool. > “We’ve built this mythology around AI like it’s a sentient co-worker. It’s not. It’s a tool, a new kind of hammer. The question isn’t what AI can do—it’s what you’ll do with it.” He compares this moment to earlier shifts. Firebase made it easier for front-end developers to build full experiences without thinking much about servers. Tools like Visual Basic and today’s low-code platforms opened up app development to people who might never have called themselves “programmers.” AI, especially LLM-powered tools, feels like another turn of that same wheel. He’s very aware of the tradeoffs. When you make it easier for more people to build, you also make it easier for them to make mistakes. He saw this with Firebase when unprotected databases started showing up in the news. Still, he argues, the alternative—locking people out of building altogether—is much worse. The real challenge is education, not blocking access. What really matters, he says, is how you approach AI depending on where you stand. If you’re a beginner in a domain, AI might give you a partial, fuzzy picture of how things work—useful, but dangerous if you don’t know how to check the details. If you’re already an expert, AI can act as a kind of supercharged assistant: a faster way to type, search, and explore ideas you already understand. He’s critical of the way AI is sometimes marketed, especially when companies overpromise what it can do. Grand claims like “it can generate anything you describe” don’t just set unrealistic expectations; they also alienate the very people who could benefit most from using these tools thoughtfully. Underneath the critique, though, is optimism. He sees AI as yet another force that can help more people build more things—as long as we remember that a tool is only as meaningful as what we choose to do with it. ## A Simple Mission: More People, More Apps When you zoom out from the specifics—Stack Overflow answers, talks, videos, product launches, AI experiments—you can see a surprisingly simple throughline in puf’s career. He comes back to it more than once: > “If there’s one thread through everything I’ve done, it’s this: I want more people to build more apps. Every new builder we empower expands what’s possible in tech.” You see that mission in how he helps experienced engineers reason about async and NoSQL. You see it in how he champions tools that lower the barrier to entry for founders and non-traditional developers. You see it in his encouragement for engineers to share what they know, whether that’s in code reviews, documentation, talks, or community answers. He pushes back on the idea that some people are “not technical” and instead frames programming as a way of thinking—about data, about systems, about behavior—that can benefit a wide range of roles. Even if someone never becomes a full-time developer, learning to think more like one can change how they work. If you care about developer experience, DevRel, teaching, or using AI in a way that genuinely helps people build, this episode hits a lot of nerves in a good way. It’s not just about tools or career paths; it’s about what happens when you decide that your work isn’t just to write code, but to help others write theirs too. You can catch the full conversation with Frank “puf” van Puffelen on [*Build to Succeed*](https://verygood.ventures/podcasts/frank-van-puffelen-puf-low-code-no-fear-building-the-future-of-apps-with-flutter-and-llms/) and hear the stories, tangents, and insights that didn’t fit on this page—but might stay with you the next time you open your editor, answer a question, or step onto a stage. ### What It Takes to Modernize Without Breaking Trust Source: https://verygood.ventures/blog/what-it-takes-to-modernize-without-breaking-trust/ Last week, we dropped a [brand-new episode of *Build to Succeed*](https://verygood.ventures/podcasts/), featuring Michael Harker, former VP of Product Management at Ritchie Bros.  Here’s an overview of our conversation with Michael, where you can find key lessons on product leadership and transformation, along with stories that illustrate how curiosity and clear principles can drive meaningful, lasting change. ## Balancing Innovation and Reliability Ritchie Bros. is the world’s largest marketplace for used commercial construction equipment, connecting buyers and sellers through live and online auctions for heavy machinery, trucks, and industrial assets. For many of its customers, the platform isn’t just a tool; it’s their livelihood. As Michael puts it, “When your customers are betting their retirement on your platform, modernization isn’t about shiny new tech—it’s about not breaking their trust.” That mindset defined Ritchie Bros.’ approach to transformation. Modernization became less about introducing cutting-edge technology and more about ensuring that every change made the experience faster, more stable, and more reliable for the people who rely on it daily. ## Set a North Star You Can Actually Steer By At the heart of Ritchie Bros.’ transformation was a clear goal: to modernize without disrupting the customers who relied on them. The team focused on three core principles to make that possible: speed, stability, and continuity. These guided every decision, from which features to prioritize to how fast to replatform core systems. Rather than aiming for a perfect redesign, the focus was on delivering tangible improvements that customers would feel immediately. Michael describes this as the art of balancing momentum with mindfulness. Great product leaders, he says, stay relentlessly curious—about users, systems, and tradeoffs. “The best product leaders don’t just chase features, they chase understanding. Infinite curiosity is how you find the problems worth solving,” he reflects. ## Replatform First, Polish Later When Ritchie Bros. began its modernization journey, the team focused on rebuilding critical foundations before tackling surface-level redesigns. They went API-first to improve performance, reliability, and scalability—saving UX refinements for later phases. This disciplined sequencing kept the project lean and avoided scope creep, ensuring the team didn’t “break what worked” in the name of progress. “For our customers,” Harker explains, “this isn’t just a transaction—it’s their livelihood, sometimes even their retirement. That means modernization can’t just be about a new UI or shiny features; it’s about delivering stability without breaking trust.” ## Communicate Early, Often, and in Writing Transformation at scale is never smooth, but clear communication can make it manageable. Ritchie Bros.’ team over-indexed on written documentation—everything from stakeholder one-pagers to early design mockups—to align decisions and reduce ambiguity. This practice made it easier to “disagree and commit” when tradeoffs inevitably surfaced, keeping the entire organization rowing in the same direction. ## Practical Takeaways For Product Teams If you’re leading digital transformation, here are a few lessons from Ritchie Bros. you can apply right away: - Define a North Star that emphasizes continuity and customer trust - Sequence your work: Prioritize platform stability before design polish - Create a simple yes/no framework (e.g., time-to-market, stability, impact) to prevent scope creep - Document decisions so future teams understand the “why” - Encourage curiosity and collaboration between PMs and engineers to uncover hidden dependencies and legacy logic [Listen to the full conversation with Michael Harker](https://verygood.ventures/podcasts/michael-harker-ritchie-bros-the-future-of-product-leadership-ai-customer-empathy-scaling-legacy-enterprises/) on *Build to Succeed* to learn how trust, clarity, and speed can fuel modernization without losing sight of what matters most: your customers. ### Generative UI for Flutter: Build Adaptive, Branded, and Intelligent User Experiences Source: https://verygood.ventures/blog/redefining-the-digital-booking-experience-with-generative-ui/ Customers trying to book services digitally have traditionally only been offered multi-step forms or text-based chatbots. These solutions work, but users often find themselves clicking endlessly and abandoning their purchase flow altogether. Today, a new frontier is emerging: [Generative UI SDK for Flutter (flutter_genui)](https://github.com/flutter/genui), which blends agentic AI with Flutter’s rendering power to dynamically assemble interfaces. ## The Next Leap in Digital Experiences: What is Generative UI? GenUI represents the evolution of conversational and branded interfaces. Instead of relying solely on text prompts, GenUI dynamically assembles visual interfaces like dropdowns, sliders, and card selectors that adapt in real time to user intent. The result is a more natural, branded, and personalized interaction. ![__wf_reserved_inherit](/assets/images/blog/redefining-the-digital-booking-experience-with-generative-ui/body-0.png) Let’s explore how this works in a real-world scenario: ![__wf_reserved_inherit](/assets/images/blog/redefining-the-digital-booking-experience-with-generative-ui/body-1.png) On the left, traditional AI delivers static, text-based responses, describing results but requiring users to manually take the next step. On the right, GenUI transforms AI intent into live, interactive components that guide users through dynamic, personalized experiences. ## GenUI SDK for Business GenUI is a business advantage—by moving beyond boring chatbot interfaces, enterprises can create booking and engagement experiences that are faster, more intuitive, and deeply personalized to each user. Instead of forcing customers through rigid forms or endless chat prompts, GenUI guides them with adaptive, branded interfaces that feel natural and reduce friction. Here's how we believe it drives measurable value: - **Faster Goal Completion:** Streamlined, guided interactions reduce drop-off and cognitive load. - **Stronger Engagement:** Tailored elements are more inviting than basic text, driving greater customer interaction. - **Smarter Personalization:** Every UI adapts in real time to context, preferences, and behavior, ensuring customers feel understood and supported. For booking journeys, GenUI enhances the experience by reducing friction across key stages—such as discovery, consideration, decision-making, and re-engagement. Instead of relying solely on static forms or text prompts, GenUI introduces adaptive, visual components that guide users more naturally through options, preferences, and upsell opportunities.  While certain steps can remain in traditional web flows, GenUI gives teams the flexibility to bring as much of the booking journey as they choose into the agent interface, including payments, for a more seamless end-to-end experience. This shift enables customers to explore and act within a single, cohesive experience, fostering stronger engagement and satisfaction over time. ## Introducing Generative UI SDK for Flutter Unlike traditional chatbots, the SDK goes beyond handling basic text prompts. It dynamically generates adaptive, visual UI components—such as dropdowns, sliders, or card selectors—that respond to user intent in real time. Its modular architecture decouples AI logic, UI rendering, and backend integration, enabling scalable, cross-platform deployments. The result is a flexible, branded, and personalized customer experience that can integrate seamlessly with existing systems. ## Implementation Paths Every organization approaches GenUI from a different starting point. Some products are already built with Flutter, while others rely on web or native stacks like React, Vue, or iOS/Android. Both can take advantage of the Flutter GenUI SDK—the implementation path just looks a little different. **Flutter-Native Products **For products already built in Flutter (mobile or web), integration is faster and more seamless. Teams can leverage existing components, styling systems, and libraries while aligning directly with Google’s GenUI SDK. This option enables quicker delivery, reduced integration complexity, and strong continuity with the current codebase, making it ideal for rapid pilots or production-ready features. **Non-Flutter Products **For platforms not originally built with Flutter, the GenUI SDK can still be integrated using Flutter’s Add-to-App feature or through multi-platform approaches, including React, Vue, or native mobile frameworks. This allows teams to embed GenUI experiences without rewriting entire apps. In non-Flutter environments, integration involves implementing compatible UI components aligned with the design system and ensuring backend orchestration. While this approach adds some initial implementation and maintenance overhead, it enables teams to deliver the same branded, intelligent booking experience powered by GenUI—without requiring a full Flutter migration. Regardless of approach, both integration paths aim to deliver the same result: a seamless, branded, and intelligent experience powered by GenUI. You can [talk to our team](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) to explore which GenUI implementation path best aligns with your goals and technical ecosystem. ## The Future of Customer-Facing Agents Generative UI is more than a new feature—it’s a paradigm shift. By combining agentic AI with adaptive, visual UI components, enterprises can reimagine how customers book, buy, and interact with their services. Whether implemented as a rapid prototype in Flutter or natively integrated into an existing stack, the opportunity is the same: boost engagement, increase conversions, and future-proof digital experiences. ## Ready to Explore GenUI For Your Business? Generative UI represents a new way to think about digital experiences. From rapid prototyping with Flutter to deep alignment with your existing tech stack, we’ll help you determine the best path forward. At VGV, our team combines deep Flutter expertise, AI-driven design, and scalable architecture practices to bring GenUI to life. [Learn more](https://verygood.ventures/resources/genui/) about how GenUI can elevate your digital customer experience. ### The Flutter News Toolkit Finds a New Home at Very Good Ventures Source: https://verygood.ventures/blog/the-flutter-news-toolkit-finds-a-new-home-at-very-good-ventures/ ## Continuing the Story The [Flutter News Toolkit](https://flutter.dev/news) was designed to help news publishers build their own Flutter-powered mobile apps quickly and easily. By providing a foundation that's modern, performant, and open-source, the toolkit makes it possible for organizations of all sizes to create quality digital experiences for their readers. As longtime partners of the Flutter team, we're honored to continue this work. VGV has been involved with Flutter since its earliest days, helping organizations around the world design, build, and scale products with the framework. Taking ownership of the News Toolkit feels like a natural next step in our ongoing collaboration with the Flutter community. {% video url="https://www.youtube.com/watch?v=dukRAS-OUMM" title="Quick start to building a news app in Flutter" %} ## What This Means Going Forward Our goal is to ensure the toolkit continues to thrive and remains a useful resource for publishers and developers alike. We'll focus on maintaining its stability, supporting contributors, and keeping the project aligned with the broader Flutter ecosystem. We believe this transition will help the toolkit stay vibrant and community-driven, while opening the door for future contributions and ideas from developers everywhere. ## A Shared Commitment to Open Source We're deeply grateful to the Flutter team at Google for their vision and collaboration in creating the News Toolkit. Their work has set a strong foundation for the open-source community to build upon, and we're proud to help carry that work forward. To learn more about the Flutter News Toolkit, visit [VGVentures/news_toolkit](https://github.com/VGVentures/news_toolkit) Together, we'll continue to support the mission that started it all—helping publishers deliver great news experiences, powered by Flutter. ### Flutter for Enterprises: Everything CTOs and Engineering Leaders Need to Know Source: https://verygood.ventures/blog/flutter-for-enterprises-everything-ctos-and-engineering-leaders-need-to-know/ The way organizations design, build, and scale digital products is constantly changing. Customers expect seamless, high-performance experiences across devices, while development teams demand efficiency, consistency, and speed. And leaders need to maximize return on investment (ROI) from their tech stack, while staying ahead of the competition. That's exactly [what Flutter delivers](https://flutter.dev/). Backed by Google, this open-source UI framework not only enables apps to run at native speed—from 60fps up to 120fps when needed—but also ensures design consistency and brand fidelity across every touchpoint. With a single modern toolkit, enterprises can deploy experiences across mobile, web, desktop, and embedded devices at the exact same quality, reducing fragmentation and reinforcing brand identity at scale. At the same time, Flutter gives teams pixel perfect control to create fully custom, branded interfaces, enabling consistency without sacrificing creativity. Enterprises can align engineering and design, accelerate delivery, and innovate customer experiences in ways that redefine business outcomes. More than just a developer tool, Flutter is becoming the foundation for digital transformation at scale. ## Why Flutter Makes Business Sense Flutter isn't just another cross-platform framework—it's the only true multi-platform framework. While other tools can compile apps for iOS and Android, they stop short of delivering consistent experiences across the full range of platforms enterprises depend on, including web, desktop, and embedded devices. Flutter is designed from the ground up to unify development across all of them. That means enterprises don't need separate teams, codebases, and processes for each platform. Instead, they can align engineering around a single toolkit, reducing redundancy, streamlining testing and automation, and cutting long-term maintenance costs. The result is faster release cycles, greater consistency across every customer touchpoint, and a more scalable approach to product growth. ### High Performance Without Compromise Unlike other cross-platform solutions, Flutter doesn't rely on fragile "bridges" to native components. Its architecture compiles directly to machine code, enabling smooth animations and near-native performance across platforms—critical for industries where speed and reliability impact engagement and revenue. ### Productivity That Scales Flutter's vast development ecosystem—from Hot Reload to robust integrated development environment (IDE) extensions—is also extremely high quality, which improves velocity and efficiency, ultimately accelerating feature delivery. Enterprises that unify teams around Flutter report up to 2x productivity gains, freeing resources to focus on innovation instead of platform parity. ### Built for Growth Flutter allows precise control over every pixel, while supporting modular architectures. This makes it easier to maintain design systems across apps, personalize experiences at scale, and adapt quickly to evolving customer needs. ^1^ ## The Business Value of Flutter ### Faster Time to Market Equals Faster ROI Flutter reduces development cycles significantly. Studies show that companies can cut time-to-market compared to those managing separate native teams by up to 62%^2^. That means products launch faster, revenue comes in earlier, and businesses can respond quickly to market shifts. For industries like fintech, travel, and entertainment—where every week of delay impacts adoption—this speed is a huge competitive advantage. ### Unlocking New Revenue Opportunities By consolidating development, businesses can roll out new features across platforms at the same time, meaning no more staggered iOS vs. Android launches. Instead, every user gets access to product updates immediately, creating a unified brand experience. For products with transactional features (like ticketing, e-commerce, or financial services), Flutter helps maximize monetization—often delivering double the revenue on day one by making the full addressable user base available at launch. ^3^ ### Delivering Customer-Centric Experiences Customer expectations are higher than ever. Users demand apps that load quickly, feel smooth, and deliver features simultaneously across platforms. We talked about how Flutter apps compile directly to native machine code, ensuring high performance and frame rates. But beyond speed, Flutter empowers teams to design highly branded, consistent interfaces, ensuring customers feel equally valued across every device and endpoint—from mobile apps to digital signage to point-of-sale kiosks. That consistency builds trust, strengthens retention, and increases customer lifetime value. ### Scaling With Confidence As organizations grow, complexity increases. Flutter's modular architecture reduces that complexity by standardizing tools, processes, and design systems across teams. This not only makes scaling more efficient, but also lowers long-term maintenance costs. Engineering teams gain more bandwidth for innovation, while leadership gains confidence that growth won't be hindered by technical debt or fragmented architectures. In a world where every second of startup time impacts engagement and every release cycle shapes competitive edge, these advantages aren't just technical—they're strategic. [Read more about how to accelerate and impact your business with Flutter in our whitepaper.](https://verygood.ventures/whitepapers/business-value-of-flutter/) ### A Framework With Unmatched Support One of Flutter's biggest advantages isn't just technical—it's the strength of its ecosystem. Backed by Google, Flutter is the most popular framework for multi-platform development, offering enterprises robust resources, tools, and community support that accelerate success, including:  - Time-saving developer tools that improve velocity and reduce overhead - Deep integration with major products and services (like MarTech and RevTech solutions) - High quality third-party tools and OSS libraries to extend functionality - Access to sophisticated agencies and service providers specializing in Flutter - A wealth of resources and online documentation to speed up onboarding and problem solving - A strong talent pool for hiring, making it easier to build and scale teams - A supportive global community and a vibrant conference ecosystem that drives innovation forward Together, these elements make Flutter not just a framework, but a solid foundation that companies can rely on for long-term growth and innovation. > *"Flutter's business value isn't only the lines of code savings. So much of what slows a business down isn't the code, but the communication and coordination of the humans and various teams needed to check all the boxes. Making choices that help us to optimize the coordination helps teams stay focused on the fun part—creating amazing UX and accelerating value."* > \- David DeRemer, Founder & CEO at Very Good Ventures ## Flutter is Powering Global Leaders Google, Toyota, BMW, Disney, GPay, and Alibaba are just a few of the organizations using Flutter to unify digital experiences in their most important production applications. In fintech, travel, hospitality, and entertainment, Flutter is driving multi-device ecosystems and reducing costs while improving customer experiences. [Take the case of Hamilton](https://verygood.ventures/success-stories/hamilton/), the Broadway musical that launched the first commercial Flutter app outside of Google. In 2017, the Hamilton team partnered with VGV's founders to build and release the official Hamilton app for iOS and Android in just three months—offering fans exclusive content, ticket lotteries, trivia, and more, all powered by Firebase and Google Cloud. This groundbreaking launch proved Flutter's potential for production-grade applications. But our work didn't end there. By 2021, with Flutter having evolved significantly, the Hamilton app needed a transition to a modern foundation. VGV led a comprehensive rebuild, upgrading the architecture, improving performance, and implementing best practices through [Very Good Start](https://verygood.ventures/solution/very-good-start/), along with complete test coverage. Relaunched in 2022, the new app featured a fresh design, enhanced stability, and smarter fan engagement tools.  [Watch why leading enterprises are unifying platforms and accelerating delivery with Flutter.](https://verygood.ventures/resources/flutter-migration/) [Or consider Trackhouse](https://www.trackhouse.com/), where we built an immersive, multi-screen VIP experience for race fans. Using Flutter, Firebase, and real-time data integrations, we created a sponsor-themed digital layer that translated millions of race data points into intuitive, visually engaging dashboards. Mounted on the back of the pit boxes, large displays reached crowds with live visualizations, while handheld dashboards gave VIPs a personalized, easy-to-follow race experience—all designed to enhance fan engagement and elevate sponsor value. [Explore more examples of how we help organizations scale, innovate, and deliver with Flutter.](https://verygood.ventures/success-stories/) ## Our Take: Flutter is the Best Option in 2025 & Beyond ### Where Flutter Stands Apart **The Old Way (Native) ** Each app requires its own codebase and dedicated platform team. Architectures, tools, and processes vary widely between iOS, Android, web, and desktop. This fragmentation drives up costs and slows down growth. **The Cross-Platform Way (React Native & Others) **Earlier attempts at single codebases (like PhoneGap, Xamarin, or React Native) solved some efficiency problems, but came with heavy tradeoffs. Performance issues, limited maintainability, and compromised user experience made them difficult to scale for enterprise needs. **Embrace the Multi-Platform Way With Flutter **Flutter delivers the best of both worlds: native-quality performance with true multi-platform reach. From mobile to web, desktop, and beyond, teams can share a single codebase without compromising speed, reliability, or design fidelity. This alignment reduces redundancy, accelerates delivery, and creates consistent experiences across every device. **The Flutter Transformation **For enterprises, Flutter is more than a framework—it's an organizational shift. By consolidating around Flutter, Dart, and VGV best practices, companies can: - Launch features faster and at a lower cost - Reuse code and patterns across projects - Scale teams with more flexibility - Improve developer happiness and reduce management overhead - Deliver more unified, expressive user experiences At VGV, we also help enterprise teams build their own Flutter expertise. [We partnered with Betterment to pilot their first Flutter feature](https://www.betterment.com/), proving the framework could integrate smoothly with their existing native code. Over the course of six months, we trained their engineers, established best practices, and helped design a migration plan that enabled faster feature delivery and long-term scalability.  > *"Developers reported being able to build features faster, and Flutter would allow us to control everything from UI to the business logic with one codebase."* > > \- Sam Moore, VP of Architecture at Betterment [Read more about our work with Betterment here.](https://verygood.ventures/success-stories/betterment/) The transformation extends beyond engineering. Product, design, and quality assurance (QA) teams also benefit from standardized processes, making it easier to collaborate, iterate, and focus on value creation instead of platform maintenance. > *"The difference is profound; companies embracing Flutter aren't just building apps, they're reshaping how organizations scale technology and deliver value. A unified roadmap. A consistent design system across products, devices, and screen types. An engineering team focused on adding value fast, without sacrificing quality. Businesses experiment, innovate, and scale faster. Teams are more productive. Users love the outcomes. It's a win-win situation for everyone." > > *\- Jorge Coca, Head of Engineering Services at Very Good Ventures  ## Choosing the Right Foundation for Enterprise Apps When evaluating frameworks for multi-platform development, it's important to look beyond the buzzwords and focus on stability, performance, and long-term support. Here's a clear comparison of how different frameworks perform:  **Flutter** - Modern, stable solution - High-quality UI with complete design control - High runtime performance due to native compilation - Easy to learn language (Dart) and efficient development due to features like Hot Reload - True multi-platform functionality (mobile, web, desktop, embedded) - Supported by Google and used for Google Pay, Google Earth, Google Ads, Google TV, and many other Google products - Mature ecosystem with quality resources, libraries, and tools - Large, active developer community and deep, experienced talent pool **Maui** - Extremely unstable, pre-alpha quality - .NET overhead on UI clients leads to performance bottlenecks - Less customizable UI due to native components reliance - Not truly multi-platform (e.g. no web support) - Supported by Microsoft, never achieved significant traction—primary adoption reason is investment in .NET ecosystem - New, immature ecosystem; limited tools and resources **React Native** - Mature solution, old paradigm - Runtime performance issues due to architecture (JS bridge) - Less customizable UI due to native components reliance - Most common language - Cross-platform (iOS/Android) - Supported by Facebook, built primarily for web developers - Mature and large ecosystem, but new tools gaining favor - Very large active community & talent pool—primary adoption reason is existing in-house JavaScript team - Very small community & limited talent pool The following images speak for themselves: ![__wf_reserved_inherit](/assets/images/blog/flutter-for-enterprises-everything-ctos-and-engineering-leaders-need-to-know/body-0.png) Flutter's rise is unmatched, as it's quickly overtaken React Native and is far outpacing MAUI and Xamarin in developer adoption. Its momentum signals strong community trust and long-term viability. ![__wf_reserved_inherit](/assets/images/blog/flutter-for-enterprises-everything-ctos-and-engineering-leaders-need-to-know/body-1.png) This chart shows how developer interest on Stack Overflow has shifted over time: Flutter has rapidly risen since 2018 to become the clear leader.  ## The VGV Advantage VGV has partnered with Google on Flutter since 2018. We're the #1 agency Google recommends, and the first they refer to large enterprise organizations needing help with Flutter. From flagship demos at Google I/O to [maintaining flutter.dev](https://flutter.dev/), we've been trusted to showcase Flutter's capabilities at the highest level. We've helped organizations like Universal Destinations & Experiences, Norwegian Cruise Line, Betterment, and Toyota Connected adopt Flutter to scale products faster, modernize architecture, and unlock new customer value.  Our open-source tools and best practices have become the gold standard for Flutter development worldwide. We've made over 20 popular open source tools publicly available and contribute to many more. Based on our internal needs, the tools aim to initiate Flutter projects correctly and ensure code quality throughout development. Learn more at [www.vgv.dev](http://www.vgv.dev) ### Where Does Your Multi-Device Strategy Stand? As many organizations struggle with disconnected experiences across platforms, leading to inefficiencies and frustrated users, at VGV we created the Multi-Device Maturity Model (MDMM). This framework provides a structured way to think strategically about experience delivery. The MDMM enables teams to identify their current level of maturity, benchmark against clear stages, and create a roadmap toward smarter, faster, and more connected digital products. If challenges like platform fragmentation, slow delivery, or scaling innovation sound familiar, this framework offers a practical resource to help chart the way forward. The MDMM outlines five levels of maturity: 1. **Siloed Experiences:** Each platform is managed separately with little consistency. 2. **Fragmented Foundations:** Some shared components, but gaps in delivery and user flow. 3. **Emerging Integration:** Platforms begin to align with shared infrastructure and design. 4. **Cohesive Experiences:** Consistent branding and functionality across devices. 5. **Experience Innovators:** Fully integrated, personalized, and adaptive cross-device ecosystems. By benchmarking against these stages, organizations can identify their current maturity level and chart a path forward. The model highlights both the pain points of disconnected experiences and the opportunities that come with integration: faster delivery, better customer satisfaction, and scalable innovation. Ultimately, the MDMM enables product and engineering teams to take a structured, long-term view of multi-device strategy—shifting the focus from building for individual platforms to designing seamless, connected experiences across the entire ecosystem, powered by Flutter.  [Read the full MDMM framework](https://verygood.ventures/resources/multi-device-maturity-model/) to start building smarter, faster, and more connected digital products. ## Flutter Redefines Apps & Transforms Organizations Adopting Flutter isn't just a technical decision—it's a strategic investment. It empowers organizations to scale their technology, teams, and processes in harmony, creating what we call a Flutter transformation. The result is long-term growth, greater innovation, and digital experiences that meet the expectations of today's users. At VGV, we've helped some of the world's most ambitious companies achieve this shift. Whether your goal is to accelerate time to market, optimize performance at scale, or unify your engineering teams, we can help you unlock the full potential of Flutter. Ready to explore how Flutter can transform your business? [Contact us](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) to start the conversation! :::editorial-note ^1^ When companies leverage a single team for Flutter Development instead of multiple teams for other platforms. ^2^ Compared to launching with native apps. ^3^ When apps have a transactional, revenue-generating component of the experience**.** ::: ### Fluttercon EU 2025 Grows Bigger Than Ever: Highlights From Berlin Source: https://verygood.ventures/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/ Berlin was the backdrop for the largest Fluttercon event to date. Developers, companies, and community leaders from across Europe came together to celebrate Flutter, share knowledge, and imagine the future of multi-platform development. ### Hallo Freunde! [Fluttercon EU](https://www.fluttercon.dev/) was an incredible opportunity for Very Good Ventures (VGV) to connect with the broader developer community, and our booth quickly became a central hub of activity. Attendees stopped by to meet our team, exchange ideas, and gain insights into how we approach innovation with Flutter. ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-0.jpeg) *Unicorns at Fluttercon EU 2025* We also shared exclusive VGV swag, which drew in both long-time Flutter enthusiasts and newcomers curious about our work. The combination of VGV World—the interactive game we debuted in this region—limited edition collectible pins, and the ever-popular unicorn plushies (cutest creatures ever), made our booth one of the most memorable stops at the event. Built with Flutter, Firebase, and AI, VGV World showcased both our creativity and Flutter’s versatility, engaging attendees in a fun, hands-on way that highlighted the breadth and impact of our client projects.  ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-1.png) *Unicorn plushie* This year, the conference itself embraced sustainability and creativity by introducing live screen-printing for attendee merchandise. Instead of distributing pre-made shirts, participants could bring their own shirt and customize it on-site with event-exclusive designs.  The conference also attracted a diverse range of organizations, each showcasing innovations that reflect the growing maturity of the Flutter ecosystem. ## VGV on Stage: Full House VGV took the spotlight with multiple sessions that drew full rooms and sparked lively conversations: #### *The “fastest” application of Flutter of all time!* David DeRemer, Founder & CEO at VGV, and
Kyle Crouse, Director of Concept Technology at VGV ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-2.png) *Photo by droidcon Berlin & Fluttercon EU 2025 on Flickr* Our keynote gave a behind-the-scenes look at building an immersive, trackside VIP experience with [Trackhouse Entertainment Group](https://www.trackhouse.com/), designed and deployed with Flutter. Attendees learned how starting with multi-device thinking changes design research, product strategy, and deployment—and how Flutter makes ambitious, real-world builds possible. [You can watch the keynote here](https://www.youtube.com/watch?v=9w8gGfnDD5g) #### *Mastering CustomPainter: Going Beyond Widgets in Flutter* Sofía Rey, Mobile Engineer at VGV ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-3.png) *Photo by droidcon Berlin & Fluttercon EU 2025 on Flickr* This talk was a deep dive into CustomPainter, a powerful Flutter tool that gives developers full control over the canvas—perfect for crafting visuals that standard widgets simply can’t handle. Sofía walked the audience through how she used CustomPainter in a real production feature to draw and manipulate paths, demonstrating how to transform SVGs into custom path data for direct use within Flutter apps.  #### *Beyond the widgets: Evaluating Flutter apps with code assessments* Óscar Martín, Senior Engineering Lead at VGV ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-4.png) *Photo by droidcon Berlin & Fluttercon EU 2025 on Flickr* This session provided a practical, in-depth look at evaluating and improving Flutter codebases through structured code assessments. Óscar walked the audience through proven methods for analyzing architecture, scalability, and maintainability—core aspects that determine the long-term success of any Flutter app. #### *Under the Hood of ScrollViews* Dominik Šimoník, Senior Engineer at VGV ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-5.png) *Photo by droidcon Berlin & Fluttercon EU 2025 on Flickr* Dominik took developers deep into the mechanics of scrolling in Flutter, revealing what truly happens beneath the surface of widgets like ScrollView, Scrollable, and Viewport. He also broke down how these elements interact to create smooth, performant scroll experiences, and what can go wrong when they’re not properly managed. #### *Building a Full-Stack App for Apple Wallet Passes* Marcos Sevilla, Senior Engineer at VGV ![__wf_reserved_inherit](/assets/images/blog/fluttercon-eu-2025-grows-bigger-than-ever-highlights-from-berlin/body-6.png) *Photo by droidcon Berlin & Fluttercon EU 2025 on Flickr* This session explored how to build a full-stack Flutter application that generates and manages Apple Wallet passes using Dart Frog. Marcos walked the audience through creating a Dart-powered API to generate passes and integrating it seamlessly with a Flutter front end, demonstrating how Dart can efficiently handle both backend and client layers. [You can watch the talk here](https://www.youtube.com/watch?v=m7QC7wN4pzM) Thank you to everyone who joined our talks and engaged with our team. We’ll be sharing the session recordings as soon as they’re available. ### What’s Fluttercon Without an After Party? Conferences are about connection, and we wanted to give the community a place to celebrate. [Together with Shorebird](https://shorebird.dev/), we hosted an official after party at Berlin’s iconic Café am Neuen See Biergarten, where developers and Flutter fans enjoyed beer, laughter, and plenty of lively conversations under the trees. ## The Endless Energy of Community Connections At VGV, we’re proud to support this community, contribute knowledge, and help shape the future of digital experiences. It was amazing to see everyone and spend time with our team in person!  Next year, Fluttercon EU joins forces with Droidcon and other ecosystem events to become [Next.App DevCon](https://www.nextappcon.com/)—the world’s largest mobile app developer conference, uniting six mobile ecosystems in one event.  See you next year at Next.App DevCon, October 7-9 in Berlin! ### A Very Good Hacktoberfest Source: https://verygood.ventures/blog/a-very-good-hacktoberfest/ October is a special month for developers around the globe—it’s time for [Hacktoberfest](https://hacktoberfest.com), the annual, month-long celebration of open source!  Every year, maintainers and contributors come together to push open source contributions across many different projects, create new community networks, and have a lot of fun along the way. Open source has always been at the core of Very Good Ventures. From maintaining our [own projects](https://github.com/VeryGoodOpenSource) to [collaborating with other companies](https://github.com/Betterment/alchemist), we’ve always been committed to giving back to the community. This year, we’re excited to take part in Hacktoberfest once again by including some of our most impactful repositories in the event. ## Very Good CLI Probably our biggest open source project right now, Very Good CLI provides ready-to-use templates, giving developers a head start when creating new projects, as well as a variety of utility commands for day-to-day tasks. [Explore Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli)  ## Very Good Templates This library of templates for a wide range of projects powers Very Good CLI in the commands where new projects are generated. [Explore Very Good Templates](https://github.com/VeryGoodOpenSource/very_good_templates) ## Very Good Workflows A collection of Github Actions with many different types of workflows for developers to create robust CI checking, following the best practices and standards that we apply ourselves in our own projects. [Explore Very Good Workflows](https://github.com/VeryGoodOpenSource/very_good_workflows) ## How to Contribute Participating is simple!  First, be sure to get familiar with the [participation guide](https://hacktoberfest.com/participation/) on the official Hacktoberfest site. Then, check out the repositories for issues that you're interested in working on, and take a look at their labels—the ones marked "Good First" are always the best ones to get started with. Finally, once you choose an issue, send us a PR and we'll review it and work together with you to get it landed. Remember: Hacktoberfest is about learning, collaborating, and having fun while giving back to the open source community. Whether you’re fixing bugs, adding features, or improving docs, every contribution matters. If you need any help, don't hesitate to comment on the issues in the repositories. Let’s all have a very good Hacktoberfest together! ### From Theory to Reality: AI Decisioning & the Era of True 1:1 Personalization Source: https://verygood.ventures/blog/from-theory-to-reality-ai-decisioning-the-era-of-true-1-1-personalization/ We just wrapped up an incredible experience at [Braze Forge](https://forge.braze.com/2025), and I'm energized by what the future of customer engagement looks like. Braze is moving beyond theory and putting AI into action to deliver true 1:1 personalization at scale. ## The Big Picture With the completion of the OfferFit acquisition and the launch of BrazeAI Decisioning Studio, BrazeAI Agent Console, and BrazeAI Operator, Braze is fundamentally transforming how marketers work. This isn't just about automating tasks; it's about evolving marketers from campaign tacticians to strategic conductors. The introduction of agents and intelligent decisioning from the Braze platform isn’t meant to merely accelerate the digital experience, but elevate it at scale, using personalization across millions of potential 1:1 optimizations—from content and channel preference, to time of day and beyond.  ## What This Means for Marketers Autonomous agents will now help you build, launch, and continuously optimize campaigns. Think of it this way: You'll have specialized AI agents working alongside you as team members, each with specific functions that you orchestrate. But unlike flat automation, Braze AI Decisioning will add a human perspective—to listen (yes, to the data, but also the ecosystem, mood, and digital body language), understand (pull insights and context), and act—on new channels and with humanistic back and forth.  You'll be able to have natural conversations with these agents to uncover insights simply by asking questions. BrazeAI Decisioning Studio can personalize every aspect of customer communication (channel, message, creative, offer, and frequency) using all available first-party data. ## Take Aways: Critical Success Factors **1\. Data Hygiene is Non-Negotiable:** AI decisioning is only as good as the data foundation it's built on. If your data quality isn't there, your AI agents will struggle to deliver meaningful personalization. Now is the time to prioritize data infrastructure. **2\. Align on Measurement Early:** Cross-functional organizational alignment on success metrics is essential before deployment. Everyone (from marketing to product to analytics) needs to agree on what success looks like and how you'll measure lift from AI-driven decisioning. Without this alignment, you'll struggle to demonstrate value and optimize effectively. **3\. Start Small, Scale Smart:** Begin with a smaller subset of users and scale as your models learn and mature. These agents continuously learn from every customer interaction, so there's a natural learning curve. Every brand will need to go through this maturation process—the key is to start early so you're building that institutional knowledge now. ## The Bottom Line Our journeys as consumers are increasingly complex; too complex for deterministic segmentation and automation. Braze is combining human imagination with AI intelligence to create a new frontier of capability in customer engagement. The brands that start experimenting now, with clean data and clear success criteria, will be the ones leading the pack in delivering truly personalized experiences. The opportunity is clear, but the execution of BrazeAI Decisioning and Agentic experiences requires thoughtful strategy and expertise. If you're exploring how to leverage these new AI capabilities or wondering where to start with your data foundation and measurement framework, let's chat. We help brands navigate these transformations and unlock the full potential of their customer engagement platforms. ### Mastering Scalable Theming for Custom Widgets Source: https://verygood.ventures/blog/mastering-scalable-theming-for-custom-widgets/ This year, I had the amazing opportunity to share my insights on how to build robust and maintainable design systems for custom widgets at [Fluttercon USA](https://www.flutterconusa.dev/) in New York, and [FlutterConf Latam](https://flutterconflatam.dev/home) in Quito, Ecuador. I wanted to bring these sessions to a broader audience through this blog, to both make this sometimes complex and confusing topic easier to understand, and to highlight practical ways to approach design systems in Flutter. ## What is Theming? ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-0.png) Theming is the process of defining your app's visual identity, which dictates the stylistic aspects of your UI (such as typography, color, and sizing). Crucially, theming doesn’t impact functionality, creating a clear separation between *how something looks* and *how it behaves*. Think of it this way: Instead of hardcoding visual properties into every single widget, you define them once, centrally. Then, widgets throughout your app can simply reference those defined styles.  Theming isn't just a cosmetic choice; it's a foundational aspect of app development that ensures visual harmony and a consistent user experience. ## Separating Style From Behavior Let's illustrate this concept with a common UI element: a button. A button inherently has two fundamental aspects: how it looks (its style) and how it behaves (its functionality when interacted with). Imagine a button styled with a distinct color, rounded corners, and clear text. When pressed, it displays a snackbar, showcasing both style and behavior working in tandem. ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-1.gif) Now, consider a plain text button with no fancy styling. If it still shows the snackbar when pressed, it demonstrates that behavior can exist independently of style. The core functionality remains, even without visual appeal. ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-2.gif) Conversely, imagine a meticulously styled button with a rich purplish background, crisp text, generous padding, and beautifully rounded corners. It looks fantastic! However, if clicking it does nothing, it proves that style can exist completely separate from behavior. You can invest all your effort into making a component look perfect, but without defined actions, it remains a static visual element. ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-3.png) In summary, theming consistently defines and applies the “look” of your UI components across your entire application, allowing their 'behavior' to remain independent. ## Theming Your Custom Widgets Flutter offers two built-in theming systems: [Cupertino](https://docs.flutter.dev/ui/design/cupertino) and [Material](https://docs.flutter.dev/ui/design/material). However, only the Material system provides a structured way to extend and customize themes, which is what we’ll build upon. As you may know, MaterialApp automatically applies theming to all descendant widgets. This works seamlessly with Material widgets because their properties are wired into the theme system by default. But what about your custom widgets? How can you define reusable styles for them while keeping your UI clean and maintainable? In this guide, we’ll explore how to bring Material’s theming approach to your own components, creating a consistent and scalable design system across your app. ### Simple Widget Styling ![Simple custom widget with inline styling from ColorScheme](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-4.png) For a simple custom widget with minimal styling, you can easily style it directly inline. You might get colors from the ColorScheme and set a specific borderRadius for the widget. This approach works well when your widget has only one style and its styling is minimal. ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-5.png) ### Handling Multiple Styles: Encapsulate in a Class ![Custom widget needing primary and secondary visual variants](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-6.png) What if your custom widget needs distinct visual "variants" (e.g. a "primary" and a "secondary" style)? Hardcoding these different styles directly into multiple widget instances can quickly lead to chaos and an unmaintainable codebase. ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-7.png) Instead, when a custom widget requires distinct visual variants, I recommend encapsulating style-related properties in a dedicated class. This clearly separates the concerns between the widget's dynamic style (themable appearance) and its other non-stylistic members. ![MyWidgetStyle class encapsulating backgroundColor and borderRadius](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-8.png) You can then pass an instance of MyWidgetStyle as a parameter to your custom widget. ![Passing MyWidgetStyle instance as a parameter to the custom widget](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-9.png) ### Theme Extensions: Integrating Custom Styles Into Your App Theme To truly integrate your custom widget styles into your app's overall theme, I highly encourage you to use ThemeExtension. This powerful feature allows you to extend the ThemeData class with your own custom styles, providing a centralized and reusable way to manage them throughout your app. Here's how to create a ThemeExtension for your MyWidgetStyle: ![ThemeExtension class for MyWidgetStyle with type definition](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-10.png) Override copyWith() to allow updating specific properties of your extension without redefining the entire object. This enables the ability to change any of the properties of the existing class, allowing for easy modifications. ![copyWith() override allowing partial property updates on the extension](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-11.png) Also override lerp(), which is used to interpolate between two style instances—useful for animated theme transitions like switching between light and dark mode. In Flutter, many properties beyond colors can be "lerped," such as shapes, decorations, and text styles. This method is vital for providing a polished user experience during theme transitions, for example, when animating between light and dark mode. ![lerp() override for animated theme transitions between light and dark mode](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-12.png) You will also need to create a lerp method in your style class to then use in your extension: ![Lerp method in MyWidgetStyle interpolating colors and border radius](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-13.png) ### Adding Your Theme Extension to ThemeData Now, you can add your newly created MyWidgetTheme extension to your ThemeData. ![Registering MyWidgetTheme extension in ThemeData extensions list](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-14.png) ![ThemeData with primary and secondary MyWidgetStyle variants defined](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-15.png) ### Accessing Custom Themes in Your Widgets To access your custom theme extension within your widgets, it's good practice to create an extension on ThemeData. This allows you to call your custom theme extensions as if they were any component theme and also handles null checks in a single place. ![ThemeData extension getter for accessing MyWidgetTheme with null check](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-16.png) Now, in your MyWidget, you can easily reference your custom styles: ![MyWidget build method accessing theme styles via the extension getter](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-17.png) ![Rendered primary-styled widget with themed background and border radius](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-18.png) ![Rendered secondary-styled widget showing alternate theme variant](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-19.png) ### Modifying Styles With copyWith I also recommend defining a copyWith() method in your MyWidgetStyle class, as it's incredibly useful. It allows you to create a copy of a particular style while changing only a few properties, without redefining the entire style. This is handy for situations where a style is mostly the same but needs slight modifications. ![copyWith method in MyWidgetStyle for partial property overrides](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-20.png) ![Using copyWith to modify only the backgroundColor of a style](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-21.png) ![Rendered widget after copyWith applied with modified background color](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-22.png) ## Handling Widget States What if you want a widget's style to react to state changes, such as being pressed, hovered, or disabled?  ![__wf_reserved_inherit](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-23.gif) ![Widget changing color on pressed, hovered, and disabled states](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-24.png) You could add more properties to your style class (e.g. pressedBackgroundColor, hoveredBackgroundColor). ![Style class with separate pressedBackgroundColor and hoveredBackgroundColor](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-25.png) But this quickly becomes unmanageable for multiple states and properties. ![Explosion of state-specific properties making the style class unmanageable](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-26.png) ![Scattered state-checking logic in the widget build method](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-27.png) ### Function > Static Value Flutter offers a solution: instead of using a static value (e.g. for backgroundColor), you provide a function that returns a value based on the widget’s current state. Before diving into that solution, let’s understand what Flutter refers to as WidgetStates. A WidgetState is an interactive state recognized by the Material Design Library (but not limited to it) that includes states such as pressed, hovered, focused, or disabled.  ![WidgetState enum values: pressed, hovered, focused, disabled, and more](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-28.png) Now we’re ready to dive into that function I mentioned earlier, WidgetStateProperty, which is actually a specialized class in Flutter’s framework that allows properties to resolve to different values depending on a widget’s state. This is particularly useful for styling widgets so they can visually respond to user interactions or changes in their state. ### Updating Your Style Class for WidgetStateProperty We should use WidgetStateProperty when the style of our widget depends on different WidgetStates. This avoids polluting the style class with multiple members (e.g. disabledBackgroundColor, selectedBackgroundColor) and centralizes the stylistic logic within the style definition, rather than scattering it in the widget's build method. Instead of static values, your style properties will now accept a WidgetStateProperty. ![Style class updated with WidgetStateProperty instead of static Color values](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-29.png) ### Resolving Widget States in Your Theme Extension In your MyWidgetTheme, you'll define how each style property resolves based on the provided WidgetStates set using the resolveWith method. ![ThemeExtension using resolveWith to define pressed state color](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-30.png) This setup will allow our widget to change its appearance whenever it goes into the pressed state. ![Widget appearance changing when entering the pressed state](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-31.png) But it can be modified to support different styles for any WidgetState: selected, pressed, disabled, and more. ![resolveWith handling selected, pressed, and disabled states](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-32.png) You can also use the fromMap method for a more readable way to define state-dependent values: ![WidgetStateProperty.fromMap for readable state-dependent value definitions](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-33.png) ### Tracking Widget States When using WidgetStateProperty, you need a mechanism to track your custom widget's current WidgetState. One way to do this  is by having a Set and calling setState whenever a WidgetState is added to the Set. In my case, since I want the widget to respond to taps, I wrapped it in a GestureDetector. Then, onTapDown the pressed state gets added and onTapUp gets removed. Then, using that set of states, we call .resolve() on the relevant style properties to determine the correct style (e.g. color or border radius). ![GestureDetector tracking WidgetState set and resolving style on state change](/assets/images/blog/mastering-scalable-theming-for-custom-widgets/body-34.png) ## Final Takeaways Flutter's theming system might seem a bit daunting at first, but I truly encourage you to experiment with it and leverage its power. It's an incredibly valuable tool for any developer's toolkit. The MaterialApp provides a highly opinionated, yet remarkably flexible theming foundation. When building your own custom widgets, strive to mimic that established behavior. This philosophy, of aligning with the framework's approach (while tailoring it to your needs, of course), will help create a cohesive and maintainable codebase. Working on a Flutter project with a robust theming system genuinely elevates your daily development experience. Not only does it simplify the separation of concerns—allowing you to focus on either appearance or behavior independently—but your custom widgets will feel seamlessly integrated into the ecosystem, making them intuitive to use and style. Please note that I delivered this talk in both English and Spanish. If you’d like to watch them, you can find the recordings here: [Fluttercon USA](https://www.droidcon.com/2025/07/24/mastering-scalable-theming-for-custom-flutter-widgets/) & [FlutterConf Latam](https://www.youtube.com/live/otOhNE4kTg0?si=yuIoCN-kRMedh7Ap&t=20757). Do you have any specific custom widgets in mind that you'd like to theme, or perhaps a particular design challenge you're currently facing? I’d love to hear about it. Feel free to [reach out](https://www.linkedin.com/in/mauricio-miguez-b518a9140/), and let’s chat! ### The Value of Community, Code, & Collaboration Source: https://verygood.ventures/blog/the-value-of-community-code-collaboration/ ## Quito, Ecuador: A Stunning Backdrop This year’s [FlutterConf Latam](https://flutterconflatam.dev/home) took us to Quito, Ecuador, one of the highest capitals in the world at 2,850 meters (9,350 feet) above sea level. Between breathtaking mountain views, volcanic landscapes, rich cacao, and warm hospitality, the city offered an unforgettable setting for the event's third edition. The conference was hosted at the Universidad de las Américas (UDLA), a vibrant campus that provided the perfect venue for two days of technical talks, networking, and community activities. ![__wf_reserved_inherit](/assets/images/blog/the-value-of-community-code-collaboration/body-0.jpeg) *Universidad de las Américas (UDLA)* ## What Was New This Year  One of the surprises at this year’s conference was the official event app, built in Flutter and available on [iOS](https://apps.apple.com/us/app/flutter-conf-latam-2025/id6748416769) and [Android](https://play.google.com/store/apps/details?id=dev.flutterconflatam.app&pli=1). Attendees used it to check the agenda, browse speaker sessions, and access event details. The event’s website was also developed in Flutter, showcasing the framework’s versatility beyond mobile. The conference opened with a keynote by Frank van Puffelen—Developer Relations and Experience Lead at FlutterFlow—called, "Better at it Tomorrow." He shared his personal approach to continuous self-improvement and offered practical tips on learning from mistakes. On the second day, the spotlight turned to a lively GDE Panel, where experts fielded audience questions about Flutter, Google, and the path to becoming a GDE. The after-parties and dinners for speakers, sponsors, and staff members were memorable, as always. Attendees enjoyed breathtaking views of Quito, traditional Ecuadorian dishes, and lively activities like the *chiva*—an open-air party bus that winds through the city with music and dancing. These moments of fun and connection added a magical layer to the conference experience, making FlutterConf Latam 2025 truly stand out. ## VGV at FlutterConf Latam For the third year in a row, Very Good Ventures had the privilege of sponsoring the conference—this time as a Silver Sponsor. Our presence was bigger than ever, with a booth that became a central hub for attendees to connect with our team, pick up some exclusive VGV swag, and chat with our HR representative about our hiring process and what it’s like to work at VGV. {% video url="https://www.youtube.com/watch?v=A3zDANot1t4" title="Video" %} Beyond the booth, our engineers took the stage to share their expertise with the community:  **Sofía Rey** dove into the power of CustomPainter in “Mastering CustomPainter: Beyond Widgets in Flutter”.  **Matías Leyba** highlighted best practices for preventing UI regressions in “Pixels Under Control: Prevent UI Regressions with Golden Tests”.  And **Mauricio Miguez** explored the ins and outs of theming with “Understanding Theme: Custom Widgets”.  Each talk showcased the technical depth and creativity that drives our work at VGV. We also introduced **VGV World**, a brand new, interactive game built with Flutter, Firebase, and AI. By scanning a QR code, participants entered a virtual world made up of four islands, each representing one of our client projects. To advance, they needed to discover a secret password with help from AI and, once all four islands were unlocked, they earned a special VGV collectible pin. It was a fun, engaging way to share our work while highlighting Flutter’s versatility. ## Community Spirit Beyond the sessions, the organizers created opportunities for the community to connect. From after-event dinners to a giant Dash plushie giveaway and a treasure hunt game, the event kept the energy high and everyone engaged. What stood out most was the collaboration among developers, leaders, and students. The Latin American Flutter community continues to grow in both talent and adoption, showing how Flutter is powering digital transformation across the region. The heart of FlutterConf Latam is its people—the developers, organizers, and community leaders who bring the event to life. > “The event has been incredible, with many new people joining the conference this year. It’s important for developers to attend events like these to connect with the community, learn, and share knowledge.” > > \-Álvaro Armijos, Software Engineer & Staff Organizer at FlutterConf Latam > “I’m here for the first time in Latin America, and I came because this is an event made by the community, and for the community. Events like these are must-attend—not just for learning and listening, but for networking, which for me is the most important thing. It’s about building useful contacts for the future. And it’s also important to have companies that support this kind of event (like VGV), because companies that are leading Flutter can bring job opportunities and amazing people together.” > > \-Vadym Pinchuk, GDE / Mobile App Developer ![__wf_reserved_inherit](/assets/images/blog/the-value-of-community-code-collaboration/body-1.jpeg) ## Another FlutterConf Latam in the Books This year's event once again proved why this community is so special. Each year it gets bigger, better, and more impactful—not just as a space for learning, but as a hub for connection and inspiration. We’re proud to keep supporting this conference and the community behind it.  The FlutterConf Latam team has already announced next year’s destination: Cancún, Mexico. The only big question is: Are you going? ### Andrew Brogdon on AI, DevRel, and the Future of Apps Source: https://verygood.ventures/blog/andrew-brogdon-on-ai-devrel-and-the-future-of-apps/ Our new episode of *Build to Succeed* dropped last week with Andrew Brogdon, Staff Developer Relations Engineer at [Google](https://www.linkedin.com/company/google/), as our guest. Here’s a quick summary of his insights you’ll want to catch. ## Merging Creative Writing With Code Before joining Google, Andrew earned two degrees in creative writing and even pursued a career as a poet. Today, he’s one of the leading voices in Flutter’s global developer community, helping shape the way teams adopt, use, and innovate with the framework. His unique blend of storytelling and engineering makes him the perfect guide to discuss where DevRel is headed in an AI-powered world. ## AI, Agents, and the New Frontier In the episode, Andrew breaks down why AI is forcing us to rethink how developers learn, build, and lead. He shares: - How AI is raising the bar for quality in developer tools and codebases. - Why agentic apps that can think, plan, and act using AI agents are moving from theory to reality. - The surprising ways AI could reshape languages like Dart, making them easier to read than to write, since AI may generate much of the code. As Andrew puts it: “Take an LLM like Gemini, give it tools, give it a goal, package that up, and that’s an agent. An agentic app would use an agent like that for some or all of its functionality.” ## Rethinking DevRel in the AI Era DevRel has always been about connecting technology with the people who use it. However, that audience is now expanding beyond human developers. AI agents need upskilling, too, Andrew explains. “We’ve never been in the position of needing to upskill AI agents before, but if we’re trying to help developers be happy and effective, that is going to start coming into our work more and more.” That shift means building resources that serve both people and machines—whether that’s making release notes machine-readable or rethinking learning tools so AI can consume them. ## Leadership Takeaways For engineering leaders, the implications are massive. This isn’t just about AI writing code faster, it’s about creating workflows, tools, and cultures that are ready for a hybrid world of humans and agents building together. Andrew reminds us that leadership in this moment still comes back to one timeless skill: empathy. Understanding your team—and now, understanding the tools they work alongside—will be the differentiator that makes great organizations thrive. ## A Worth Listening  If you’re curious about what’s next for Flutter, developer relations, and the future of AI-driven app development, this episode is packed with insights worth sharing with your team! ### Elevating Flutter Test Reports with Allure Source: https://verygood.ventures/blog/elevating-flutter-test-reports-with-allure/ Testing is a crucial part of application development, ensuring software behaves as expected. As testers, we aim to provide clear reports that document test results, highlight failures, and include visual evidence such as screenshots. These reports help developers, project managers, product owners, stakeholders, and testers to assess stability and make informed decisions. For Flutter integration testing, well-structured reports are even more critical. Traditional Flutter end-to-end automation testing reports often include dense logs that are counterintuitive and lack actionable insights to analyze and share. Instead, reports should prioritize clarity, visual representation, and actionable insights, making them accessible to both technical and non-technical audiences. We identified an opportunity to amplify how existing Flutter UI testing frameworks present their results. This blog introduces how reports typically look today, highlights the reporting needs our team faced, and walks through a solution that elevates test reporting with greater control and clarity. ## Reports That Work for Every Audience We wanted to enhance the communication of test results across teams. Our goal was to create a reporting solution that delivers clear, detailed, and well-structured test execution data, accessible to both technical and non-technical audiences. While existing tools—such as [Flutter Integration Tests](https://docs.flutter.dev/testing/integration-tests), [Maestro](https://maestro.dev/), [Fluttium](https://fluttium.dev/), and [Patrol](https://patrol.leancode.co/)—offer valuable capabilities, we identified opportunities to further enhance the experience by building a more intuitive and stakeholder-friendly reporting solution. ### Key requirements for the reporting solution: 1. **Step-by-Step Breakdown:** The report should provide a detailed sequence of test steps, clearly marking passed and failed steps. This helps pinpoint issues and understand the test flow. 2. **Non-Technical Readability:** The report must be easily understandable by PMs, clients, and other stakeholders, avoiding excessive technical jargon while maintaining clarity. 3. **Visual Evidence:** Screenshots or other visual elements should be included at key steps, especially where failures occur, to provide immediate context. 4. **Framework Compatibility:** The solution should support both **Flutter Integration Tests** and **Patrol.** ## Current Solutions Before building a custom solution, we explored how existing tools handle test reporting. We tested the most widely used UI testing frameworks in the Flutter ecosystem: Maestro, Fluttium, Flutter Integration Test, and Patrol, using a basic Flutter sample app to evaluate how each presents test results. While each tool has its strengths, we found that none fully meet our needs: accessible to both developers and non-technical stakeholders. ![__wf_reserved_inherit](/assets/images/blog/elevating-flutter-test-reports-with-allure/body-0.png) ### Examples Below are a few sample outputs to illustrate how each tool presents test results. **Flutter Integration Test – Console Output** Failure case (verbose, technical logs): ![Flutter Integration Test console output showing verbose failure logs](/assets/images/blog/elevating-flutter-test-reports-with-allure/body-1.png) **Patrol – Console Output** Failure stops execution and shows a test summary: ![Patrol console output showing test failure summary](/assets/images/blog/elevating-flutter-test-reports-with-allure/body-2.png) **Maestro – HTML Report and Console** Terminal shows step-by-step output, and HTML/JUnit report shows high-level result only (no step breakdown): ![Maestro HTML report and terminal showing step-by-step test output](/assets/images/blog/elevating-flutter-test-reports-with-allure/body-3.png) ## Introducing Allure [Allure](https://allurereport.org/) is an open-source framework designed to generate rich, interactive, and user-friendly test reports. Originally built for Java-based testing frameworks like [JUnit](https://junit.org/junit5/) and [TestNG](https://testng.org/), it has since evolved to support a broad range of ecosystems—including Python, JavaScript, and Kotlin. While Flutter isn’t natively supported, Allure’s flexible data format allows us to integrate it manually by exporting test results in a compatible structure. What makes Allure compelling is its ability to transform raw test execution data into a structured, shareable HTML report that’s not only informative but easy to navigate. This elevates the experience for both developers and non-technical stakeholders, helping teams better understand test behavior and failures. ### How does it work? Allure consumes test result data stored in JSON files, following a specific format to ensure compatibility. Each test case is represented as a JSON object with key properties such as: - **name**: The name of the test case. - **status**: The result of the test (passed, failed, broken, skipped). - **steps**: A list of step objects, each containing: - **name**: Step description. - **status**: Step result. - **start** and **stop**: Timestamps for execution (milliseconds). - **attachments**: Optional artifacts such as screenshots, logs, or videos. - **parameters**: Input parameters used in the test case. - **uuid**: A unique identifier for the test. These JSON file names should follow a consistent pattern, using the uuid-result.json format to ensure uniqueness and prevent conflicts. Allure then aggregates this structured data and converts it into an interactive HTML report that enhances test result readability and debugging efficiency. For more details on the test result file structure, visit the [Allure documentation](https://allurereport.org/docs/how-it-works-test-result-file/). Note: In this blog, we will showcase a subset of Allure’s capabilities. The framework supports many additional features not covered here—see the [official documentation](https://allurereport.org/docs/) for more. ## Allure for Flutter: Our Initial Effort To meet our requirements while building a scalable solution, we developed a **framework-agnostic custom Allure adapter**. This approach lets us enhance any Flutter UI testing framework by gaining full control over what gets captured and reported—free from the constraints of built-in tools. This marks an initial effort to bring Allure support to Flutter, designed to work with **integration\_test**, **Patrol**, or any other setup you might use. ### Want to see official Flutter support in Allure? As of today, **Allure does not officially support a Flutter/Dart adapter**. However, we saw the potential and built a **custom proof of concept (POC)** to bridge the gap. Our goal is to inspire the community and demonstrate what’s possible. If you'd like to see **first-class Flutter support in Allure**, consider adding your voice to the discussion on this [GitHub issue](https://github.com/allure-framework/allure2/issues/1119#issuecomment-818904583). The more people contribute or show interest, the better the chances of making it happen! ### Introducing the TestResults class We designed the TestResults class to wrap and log test execution steps, making it possible to track, manage, and save test results. This class helps structure test execution details by recording test names, timestamps, and statuses while providing utility methods to register, update, and manage test steps. Additionally, TestResults generates structured test reports in JSON format, making it compatible with Allure reporting. You can see the implementation of the class in [integration_test/utils/test_results.dart](https://github.com/VGVentures/allure_reports/blob/main/integration_test/utils/test_results.dart) on our Allure-Reports repo. ### How to integrate with your existing tests or new ones When a test starts, an instance of TestResults is created with the test name. This automatically initializes the report with: - The test name (name field). - A default failed status. - A start timestamp indicating when the test began. Each test step is logged using the addStep() method, which: - Registers a new step with a default status (in progress). - Executes the provided function, the step. - Updates the step’s status to passed or failed based on execution outcome. - Adds timestamps to track execution duration. Note: For more granular control, the registerStep() and updateStep() methods allow direct manipulation of test steps without executing a function with the actual step. When we reach the end of the test without any exceptions being thrown, we call the passTest() method to mark the test as passed. Let’s see what a test would look like: For this example, we’re using a simple Flutter app with the integration\_test package, you can see the implementation on [integration_test/test/app_test.dart](https://github.com/VGVentures/allure_reports/blob/main/integration_test/test/app_test.dart). ```dart void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); const testName = 'Tap on the floating action button, verify counter'; late final TestResults report; late final String testUUID; setUp(() { report = TestResults(testName); testUUID = report.generateAllureReportId(); }); testWidgets(testName, (tester) async { await report.addStep('Load app widget', () async { await tester.pumpWidget(MyApp()); }); await report.addStep('Verify counter starts at 0', () async { expect(find.text('0'), findsOneWidget); }); final actionButtonFinder = find.byKey(ValueKey('increment')); await report.addStep('Finds the floating action button to tap on', () { expect(actionButtonFinder, findsOneWidget); }); await report.addStep('Tap on the floating action button', () async { await tester.tap(actionButtonFinder); }); await report.addStep('Pump and settle the widget', () async { await tester.pumpAndSettle(); }); await report.addStep('Verify counter increments by 1', () { expect(find.text('1'), findsOneWidget); }); report.passTest(); }); tearDown(() async { await report.uploadReportToGoogleCloudStorage(testUUID); }); } ``` The resulting JSON after the test execution looks like this: ```json { "name": "Tap on the floating action button, verify counter", "status": "passed", "start": 1745503452538, "steps": [ { "name": "Load app widget", "status": "passed", "start": 1745503452675, "stop": 1745503452876 }, { "name": "Verify counter starts at 0", "status": "passed", "start": 1745503452876, "stop": 1745503452889 }, { "name": "Finds the floating action button to tap on", "status": "passed", "start": 1745503452889, "stop": 1745503452892 }, { "name": "Tap on the floating action button", "status": "passed", "start": 1745503452892, "stop": 1745503452928 }, { "name": "Pump and settle the widget", "status": "passed", "start": 1745503452928, "stop": 1745503453637 }, { "name": "Verify counter increments by 1", "status": "passed", "start": 1745503453637, "stop": 1745503453638 } ], "stop": 1745503453638 } ``` As you can see, the output contains the test execution details, including the test name, overall status, timestamps, and a detailed list of steps with their statuses and execution times. ### Saving test results in JSON Since the TestResults class runs on Flutter, you have the flexibility to store test execution results either locally on your machine, or on the cloud by uploading them to whatever service you like. We chose the second option, uploading them to Google Cloud. It allows us to access the results from anywhere and enables some additional capabilities such as an automated GitHub workflow. Feel free to replace the storage logic with any other service of your choice, or implement a local solution for storing and retrieving test results. Current storage logic is in [integration_test/utils/google_cloud_service.dart](https://github.com/VGVentures/allure_reports/blob/main/integration_test/utils/google_cloud_service.dart). ### Retrieving test results and generating the Allure report locally In our solution, once the JSON result file is saved in Google Cloud Storage, we retrieve them to generate the Allure report. To do so, we store them in a designated allure-results/ folder and then feed them to Allure. **Generating the Allure Report** There are two ways to generate the Allure report: 1. Serve the report locally Using the command below, Allure will generate the report in a temporary directory and launch a local server to display it: ```dart allure serve allure-results/ ``` 2. Generate a single HTML file If you want a shareable, self-contained HTML report, Allure provides you with the following command: ```dart allure generate --single-file allure-results/ ``` 3. There are two ways to open the report:  - You can double-click to open the **single** **HTML** file - You can also launch the local server from the **single HTML** file with allure openHere are some images showing the Allure report look: ![__wf_reserved_inherit](/assets/images/blog/elevating-flutter-test-reports-with-allure/body-4.png) ![__wf_reserved_inherit](/assets/images/blog/elevating-flutter-test-reports-with-allure/body-5.png) ## Conclusion Current Flutter UI automation frameworks offer valuable features, but we saw an opportunity to go beyond their existing capabilities. Specifically, we needed a solution that delivers both detailed execution insight and shareable reports that resonate with technical and non-technical stakeholders alike. By integrating **Allure**, we’ve taken a significant step toward closing that gap—creating clear, structured, and visually rich test reports. This improves not only the debugging experience but also team collaboration and decision-making, helping Flutter teams communicate results more effectively. 🚀 ## Next Steps ### Include visual evidence Currently, our solution does not provide visual evidence, but Allure makes this possible. If we capture a screenshot during a test, Allure can embed it directly into the report, making debugging easier and test results more insightful. By integrating screenshots, logs, and additional artifacts, we can enhance report clarity and provide visual confirmation of test failures. This step will further improve collaboration between developers, testers, and stakeholders, ensuring faster issue resolution and **better test analysis.** ### How Server Driven UI (SDUI) Changed the Way I Think About Mobile Development Source: https://verygood.ventures/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/ Hi! I’m Jordan Nnabugwu, a Software Engineer III at Very Good Ventures.  Let me paint a picture: You’ve got a few paywall designs you want to test. Normally, this means building custom A/B test logic, waiting for app store approvals, maybe even pausing dev work while marketing catches up. What’s the solution?  Recently, I’ve been working with something that feels like a cheat code for mobile development: Server Driven UI (SDUI). And I’m here to tell you, it’s a game-changer. ## From Hardware to High-Velocity Mobile Before joining Very Good Ventures, I spent time as a hardware engineer at Intel and Galtronics. Then I transitioned into software, working at places like Whisker and contributing to Open Food Facts. Whether it was IoT devices or mobile apps, I kept running into the same friction: UI updates were slow, and reacting to real-time user needs felt impossible. That’s what got me excited about SDUI. This year, I had the pleasure of attending [Fluttercon USA 2025](https://verygood.ventures/blog/inside-fluttercon-usa-2025-innovation-leadership-a-very-good-time-in-nyc/) for the first time—and even more exciting, I got to take the stage for a lightning talk on Server-Driven UI in Flutter. ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-0.png) *Photo by Droidcon Global on Flickr* In my session, I explored how a server-driven architecture can decouple content from logic, giving teams the flexibility to release faster and adapt in real time. ## The Old Way Slows You Down Let’s talk real talk: Traditional mobile development has serious bottlenecks. - **You’re at the mercy of the App Store:** Every tweak, even a button label, requires a new build, a new submission, and a wait. - **Marketing campaigns are stuck in the past:** Want to launch a promo tomorrow? Sorry, you should’ve planned that two weeks ago. - **Personalization? Good luck:** Unless you baked in every variation upfront, you’re not adapting to real-time behavior. And in an environment where user expectations are sky high, that just doesn’t cut it. ## Enter SDUI: The Server Drives, Your App Renders Server Driven UI flips the script. Instead of shipping all your UI in the app itself, you let your server define what the UI should be—layouts, text, components—and your app renders it on the fly. The result? UI changes become instant. - Want to launch a new banner? Go for it. - Need to personalize content based on user behavior? Easy. - Want to A/B test three layouts? You can do that without even updating the app. SDUI removes the friction—and it’s kind of addictive. ## This Isn’t a New Concept: it’s a Smarter Take on the Old Web If this sounds familiar, it should. Web development has done this forever. In the early 2000s, every web page came fully rendered from the server. Then we went all-in on client-side frameworks. Now, we’re seeing the pendulum swing back. Why? Because today’s tools have eliminated the technical barriers, and SDUI is just smarter. ## Real-World Inspiration: NuBank One company that totally nails this approach is [NuBank](https://nubank.com.br/). They serve over 100 million users with a rapidly growing team. And they use SDUI to scale fast. - 70% of new screens are built with SDUI - 43% of the app is now server-driven - They push updates instantly—no app store delay - Their teams ship independently and iterate in real time That’s the kind of velocity I want for every product team. ## Picking the Right Tool for the Job If you’re thinking about trying SDUI, there are a few tools I’ve explored: - [Remote Flutter Widgets](https://pub.dev/packages/rfw) (RFW): Powerful, binary-based. A bit of a learning curve, but solid. ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-1.png) ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-2.png) - [Swap](https://pub.dev/packages/swap): A Dart wrapper around RFW with a smoother developer experience. ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-3.png) ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-4.png) - [Stac](https://pub.dev/packages/stac): JSON-based, super friendly for teams just getting started. ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-5.png) ![__wf_reserved_inherit](/assets/images/blog/how-server-driven-ui-sdui-changed-the-way-i-think-about-mobile-development/body-6.png) Your choice depends on your team, timeline, and how deep you want to go. ## When to Use (and Not Use) SDUI **Great for:** - Promo banners - Real-time personalization - A/B tests - Dashboards with flexible content **Not ideal for:** - Animations or gesture-heavy screens - Offline-critical experiences - Entire apps (trust me—go hybrid) I suggest using SDUI where flexibility matters most, and where keeping your performance-critical views static is key. ## Final Thoughts The first time I saw a fully updated UI land in an app without a release, I had one thought: “Why isn’t everyone doing this?” SDUI has changed how I think about mobile development. It’s not just faster—it’s smarter. It helps you: - Stay nimble - Deliver better experiences - Give your teams autonomy - Build products that can adapt in real time And honestly, it’s fun. It unlocks a new level of creativity for developers—and that’s something I think we could all use more of. Curious about implementing SDUI? [Reach out](https://www.linkedin.com/in/jordan-nnabugwu/), I’d love to talk! ### A New Era for App Design (and What it Means for Flutter) Source: https://verygood.ventures/blog/a-new-era-for-app-design-and-what-it-means-for-flutter/ This year, the mobile operating system landscape is undergoing a significant transformation. Both Apple and Google are rolling out ambitious new design systems: Apple’s [**Liquid Glass**](https://www.youtube.com/watch?v=jGztGfRujSE) and Google’s [**Material 3 Expressive**](https://www.youtube.com/watch?v=n17dnMChX14). These are both significant changes to the way apps are designed, and while it’s unclear how high the pressure will be to adopt them quickly, it undoubtedly puts the ball in Flutter’s court to see if it can adopt them.  Despite social posts showing rudimentary liquid glass renderers for Flutter just hours after WWDC, the Flutter team's initial response may have surprised some:  "[Currently, we are not actively developing Material 3 Expressive](https://github.com/flutter/flutter/issues/168813#issuecomment-2880255353)," and "[we are not developing the new Apple’26 UI design features in the Cupertino library right now](https://github.com/flutter/flutter/issues/170310#issuecomment-2959275864)." This begged the question: What will this mean for making native-feeling apps in Flutter? It soon became clear that those worries were unwarranted, as Google announced that Flutter would be uncoupling from Cupertino and Material packages. These foundational UI components are being moved to their own independent packages, readily available on [pub.dev.](https://pub.dev) It's important to note that Material and Cupertino will still be considered "official" and fully supported by the Flutter team. Their plan is to add them to the [flutter/packages](https://github.com/flutter/packages/) repository, the same place they maintain packages like [path_provider](https://pub.dev/packages/path_provider) and [image_picker](https://pub.dev/packages/image_picker). This ensures a consistent and reliable foundation for developers who choose to stick with these established design languages. ## Why Developers Win With Decoupled UI At Very Good Ventures (VGV), we’re really excited about this for a few reasons. First, all Flutter developers will get faster updates to these packages. UI changes and critical bug fixes for Material and Cupertino components will no longer be tightly coupled to the Flutter SDK's stable release cycle. This means that new design specifications and essential updates can be shipped rapidly via [pub.dev](https://pub.dev), rather than waiting for a Flutter release. Second, developers will gain increased flexibility. They’ll have more granular control over precisely which UI libraries they import, making it more obvious for developers and companies adopting Flutter that they can build entirely unique design systems that perfectly match their brand. Developers can now import only the specific design languages they require, streamlining project dependencies and fostering a more modular and efficient approach to UI development. Finally, this presents an amazing opportunity for 3rd-party UI libraries to shine, as the “official” packages will need to be installed just like packages from the community. It puts everyone on a level playing field. Some libraries we like are: - [macos_ui](https://pub.dev/packages/macos_ui): macOS styled UI components - [fluent_ui](https://pub.dev/packages/fluent_ui): Windows styled UI components - [shadcn_flutter](https://pub.dev/packages/shadcn_flutter): beautifully designed components from shadcn/ui It’s also a great time to think about making your own UI package specific to your designs and projects. At VGV, we’ve always built custom UI libraries for clients. In fact, it’s included in [Very Good Start](https://verygood.ventures/solution/very-good-start/), and we use tools like [Widgetbook](https://www.widgetbook.io) to make it easy to share them with others and receive visual QA from designers. ## Faster Updates, Greater Control Flutter's decision to decouple UI libraries marks a key moment for all Flutter developers. Rather than seeing this as a retreat from supporting new design systems, we see it as a strategic move that democratizes UI development within the Flutter ecosystem. This shift empowers developers with greater freedom to create interfaces that truly reflect their brand and design vision, whether by adopting third-party libraries or crafting custom solutions.  As the mobile design landscape continues to evolve with Liquid Glass and Material 3 Expressive, Flutter is positioning itself not as a rigid framework tied to specific design systems, but as a flexible canvas where any design language can thrive. ### Rewriting the Rules of Hardware Engineering with AI Source: https://verygood.ventures/blog/rewriting-the-rules-of-hardware-engineering-with-ai/ In this [episode of Build to Succeed](https://verygood.ventures/podcasts/pratap-ranade-arena-the-future-of-applied-science-and-humanity-with-ai/), [Arena](https://arena-ai.com/) CEO and Co-Founder Pratap Ranade takes us inside the high-stakes worlds of aerospace, defense, and automotive—where failure is not an option and speed, safety, and precision rule the day. From his early ambitions to work at NASA, through influential roles at McKinsey and Palantir, Pratap has built his career on tackling the hardest problems in hardware engineering. Today, with Arena, he’s building AI-native tools that help operators turn massive streams of industrial data into fast, actionable decisions. "We've built a new kind of intelligence that's available on tap," says Pratap. "This isn't about AI eating jobs—it's about problems that were too hard becoming solvable. A 10-year problem can now he tackled in 2 months." ## From Data Overload to Decision Power Traditional BI tools simply can’t keep pace with the data volume, speed, and complexity in multi-site industrial operations. Arena’s systems are built from the ground up for this reality—designed to process, interpret, and act on data in real time. “AI isn’t an invention—it’s a discovery. We’ve built systems that show emergent intelligence, not by design, but by complexity and scale,” Pratap explains. Instead of replacing people, Arena’s systems work alongside operators, giving them the insights and speed they need to make critical decisions in real time. One of Arena’s most radical approaches is the creation of multi-agent swarms, digital 'societies', where AI agents interact and evolve strategies through structured interaction. This is how they tackled emergent problem-solving capabilities that adapt to real world challenges in ways pre-programmed systems can’t: “We're building multi-agent swarms—like Petri dishes of digital societies—where emergent behavior evolves through structured interaction,” he reflects.  ## The Future of Applied AI in Industry Arena’s work isn’t about hypothetical future AI; it’s about operational advantages today, where it’s already transforming operations in industries where the stakes couldn’t be higher. Whether it’s predicting component failures before they happen, optimizing assembly line performance, or improving safety protocols in real time, the impact is measurable and immediate. In industries where physics won’t forgive mistakes, AI done right is a competitive advantage we can’t ignore, moving the needle where speed and accuracy are mission critical. If the future of engineering lies in speed, safety, and adaptability, Arena is showing what that future looks like—today. Want to learn more insights? Listen to the full episode [here](https://verygood.ventures/podcasts/pratap-ranade-arena-the-future-of-applied-science-and-humanity-with-ai/)! ### Beyond Crash Reports: Why Observability Is a Business Imperative Source: https://verygood.ventures/blog/beyond-crash-reports-why-observability-is-a-business-imperative/ In this episode of the Build to Succeed podcast, host David DeRemer welcomes Andrew Tunall, President of [Embrace](http://embrace.io), for a real talk on what it actually takes to ship reliable software today. From growing user expectations to the pressure to move fast, they unpack the messy—but important—truth behind building tech that holds up under real-world demands. ## From Unconventional Path to Strategic Vision Andrew's career path is anything but conventional. With an academic background in political science and economics, and a minor in computer science, he entered the tech industry not through a traditional route, but rather through his ability to bridge the communication gap between technical and business teams. This ability shaped his product philosophy—one that prioritizes empathy for both users and engineers. Before joining Embrace as President, Andrew held product leadership roles at New Relic and Amazon Web Services (AWS), where he helped scale developer-focused tools and observability solutions for some of the world’s most complex systems. These experiences gave him a front-row seat to the evolving needs of engineering teams and the importance of clarity at scale. As President of Embrace, a company focused on observability for mobile and web apps, Andrew has helped redefine what visibility means for development teams. This isn’t just about crash reports or analytics dashboards; it’s about providing product and engineering leaders with shared, actionable context, turning observability into a critical part of an organization's strategic toolkit. ## Subtle Performance Issues Quietly Hurt Retention Andrew recalls how too many teams still operate with a false sense of confidence: performance problems are often overlooked until user engagement drops. Without proper tooling, teams can’t trace the user journey or connect small delays with big business impacts. However, as Andrew explains, observability isn’t just a backend problem; it begins with shared accountability. It’s about connecting front-end product experiences with backend infrastructure realities: "The shared context between developers building the user experience and the cloud-based systems that power it, is what leads software engineering teams to build really resilient, high-performing, excellent technology," he says.  ## Why Observability is the New Product Strategy One key theme in the episode is the evolution of observability practices, such as OpenTelemetry, which helps standardize how telemetry data is collected and analyzed across distributed systems. Embrace has leaned into this approach, not only to improve visibility, but also to help large organizations adopt a culture of experimentation without compromising user trust. "In tech organizations that are selling to developers, having people who come in through product or engineering backgrounds as the most senior level of leadership is critical to building a brand, product, and go-to-market motion that’s authentic and resonates with their user persona," explains Andrew. ## What Does it Take to Build Software that Scales without Breaking? The episode is as much about leadership as it is about tooling. Andrew shares candid lessons on hiring, managing through ambiguity, and knowing when a product is truly ready to scale. His advice: hire fewer people, but ensure they’re top performers with high ownership. And don’t wait for perfection—build a culture where fast feedback and rapid iteration drive quality. From embracing AI to managing performance trade-offs, Andrew's perspective is pragmatic and rooted in the realities of growing a startup in today’s market. Whether you're a developer, product manager, or executive leader, his insights are a valuable reminder that good tech is built on great communication, observability, and trust. ### Dart Frog Has Found a New Pond! Source: https://verygood.ventures/blog/dart-frog-has-found-a-new-pond/ We’re thrilled to share some exciting news about the future of **Dart Frog**, the backend framework for Dart that’s been helping developers build fast, testable, and modern APIs with confidence. Dart Frog is moving into its very own GitHub organization: [dart-frog-dev](https://github.com/dart-frog-dev)! This transition marks a huge milestone, not just for the framework itself, but for the vibrant community that has grown around it. Since its inception at Very Good Ventures (VGV), Dart Frog initially grew with contributions and direction from our very own unicorns, and it has grown to a point of impact where the community can evolve its thriving ecosystem even further than before. Now, it’s time to take the next step forward: **giving the community an even greater role in its future.** ## From Idea to Ecosystem Dart Frog began as an internal experiment, born out of our desire to bring the elegance of Dart to the world of backend development and help Flutter developers feel at home when working in the cloud. We wanted something fast, expressive, and testable; something that felt *very good* to use. What started as a tool for our own teams quickly found a home in the wider Dart and Flutter community. Dart Frog has been critical to delivering many popular public projects, like [I/O FLip](https://github.com/flutter/io_flip), [Flutter News Toolkit](https://github.com/flutter/news_toolkit), or [I/O Crossword](https://github.com/VGVentures/io_crossword) (just to name a few), and organizations all around the world. Since then, developers have embraced Dart Frog’s simplicity and power. They built APIs, shipped products, opened issues, contributed code, and, in true open source spirit, helped it grow into something much bigger. We knew we were onto something, but more importantly, we knew it wasn’t just ours anymore. ## A New Home, the Same Heart With this move to dart-frog-dev, Dart Frog enters a new chapter. A new GitHub organization means a structure that better reflects the momentum and enthusiasm coming from the community. It opens the door for broader collaboration, community-led discussions, and an even more transparent roadmap. And yes, we’ll still be there. VGV engineers will continue contributing, reviewing, and shaping the direction of the project, just now as part of a larger chorus of voices. ![__wf_reserved_inherit](/assets/images/blog/dart-frog-has-found-a-new-pond/body-0.jpeg) ![__wf_reserved_inherit](/assets/images/blog/dart-frog-has-found-a-new-pond/body-1.png) *Dart Frog’s new group of core maintainers will include our very own engineers Erick Zanardo and Marcos Sevilla.* You could say we’re not just frogs in the pond anymore—we’re part of a full ecosystem. ## From Tadpoles to Unicorns: VGV’s Growing Family of Open Source Tools As Dart Frog finds its own lily pad, Very Good Ventures remains deeply committed to the broader Dart and Flutter ecosystem. Our open source journey didn’t start—or end—with Dart Frog. From tools like [Very Good CLI](https://cli.vgv.dev/) and its powerful templates, to helpful libraries like [formz](https://pub.dev/packages/formz) for managing form state, to [Very Good Analysis](https://pub.dev/packages/very_good_analysis) for enforcing consistent code quality (and even more that we’re slowly getting ready for prime time! 🤫), we’ve always believed that great tooling can uplift entire communities. We’ll continue to invest our time, energy, and unicorn-fueled enthusiasm in building, maintaining, and improving the open-source tools that help developers everywhere ship confidently. Our goal is simple: complex problems, easy solutions. Always enable developers to ship faster and ship safer! ## What’s Next? If you’re already using Dart Frog, nothing changes in your day-to-day. The packages, documentation, and support are all still there, just under a new banner. What *does* change is the sense of shared ownership. The door is wide open, and we can’t wait to see who comes through. So jump in.  Explore the new GitHub organization, join the discussions, and keep an eye on what’s ahead. With your help, Dart Frog’s future is looking ribbitting. Thanks to everyone who helped Dart Frog grow from a single idea to a fully fledged framework. And an extra special thanks to all the contributors—past, present, and future—who’ve helped shape it into the powerful, community-driven tool it is today. See you at the pond 👋 ### A Very Good Time at Fluttercon USA 2025 Source: https://verygood.ventures/blog/a-very-good-time-at-fluttercon-usa-2025/ Hi! We’re Sofía Rey, Mobile Engineer, and Mauricio Miguez, Senior Engineer, from Very Good Ventures. We were so excited to represent the VGV team at Fluttercon USA, and we’re just as excited now to share what stood out to us across every moment of an action-packed two days. From technical deep dives to cross-community conversations (and of course a healthy dose of unicorn swag), Fluttercon was an unforgettable experience. Held in Brooklyn, the event gathered hundreds of developers, product leaders, and enthusiasts from around the world to celebrate all things Flutter—and we couldn’t have been more proud to be part of it. ## First Impressions There was a lot to love about Fluttercon this year: amazing sessions from technical experts and engineering leaders, deep-dive roundtables, and great after-hours networking events, to name a few. ![Fluttercon USA 2025 venue and attendees at Brooklyn Navy Yard](/assets/images/blog/a-very-good-time-at-fluttercon-usa-2025/body-0.jpeg) *Photo by droidcon Global on Flickr* But what truly stood out were the people. We had the opportunity to meet high-profile Flutter engineers, passionate developers from diverse backgrounds, and even longtime followers of our work who use our packages, read our blog, or tune into our podcast. Those moments—when someone quoted something your team has said or built—made the trip more than worth it. The speaker dinner, hosted at Duolingo’s stunning WTC 4 offices, was another highlight. With great food, a DJ, and sweeping views of NYC, it offered the chance to connect with folks from both the Flutter and Android communities in a relaxed, festive setting. ## The Venue This year's venue was the iconic Brooklyn Navy Yard, with the event spanning an end-to-end warehouse showcasing stages, booths, and networking zones. Attendees could discover talks while walking by and be instantly drawn in, enjoy snacks and discussions with fellow developers, and learn directly from brands on the cutting edge of Flutter technology. ## The Booth Experience Our VGV booth was a hit. From unicorn-branded sunscreen and lip balm to stickers and tote bags, our swag was a conversation starter on its own. A looping video of VGV’s project highlights drew attention, and interactive tablets let attendees explore apps and other digital experiences our team has built with Universal Destinations & Experiences. ## Our Talks ### Sofía Rey – Mastering CustomPainter: Going Beyond Widgets in Flutter This was my first-ever talk in English at a major conference, and it felt really good. I shared advanced techniques for drawing custom UI with CustomPainter using a real-world example from the custom VIP digital experience app we built for Trackhouse Entertainment Group, a 3-car NASCAR team. While nerves were high, the response was overwhelmingly positive, and I’m proud of facing the challenge head-on. ![Sofia Rey presenting Mastering CustomPainter talk on stage](/assets/images/blog/a-very-good-time-at-fluttercon-usa-2025/body-1.jpeg) ### Mauricio Miguez – Mastering Scalable Theming for Custom Flutter Widgets It was also my first time giving a talk in English, and I enjoyed every second of it. I walked attendees through how we build flexible and scalable theming systems at VGV—something we rely on heavily in our work. The best part was having community members come up to me afterward to continue the conversation and dive deeper into the topic. ![Mauricio Miguez presenting Mastering Scalable Theming talk on stage](/assets/images/blog/a-very-good-time-at-fluttercon-usa-2025/body-2.jpeg) *Photo by droidcon Global on Flickr* ## One Talk We Recommend If you watch just one talk from Fluttercon USA 2025, make it Chris Sells’ keynote. He offered a bold, thoughtful perspective on how the community should support Flutter’s growth in an AI-driven future. It was both inspiring and pragmatic, a rare mix in tech keynotes. ## Why Events Like This Matter Conferences like Fluttercon aren’t just about talks or swag. They’re about reinforcing Flutter's presence in the ecosystem, building bridges between teams, and creating space for curiosity and collaboration. For us at VGV, it was also a reminder of the impact our work has. When someone approaches you because they use your package daily or have read your article, that’s the kind of real-world validation that fuels the open-source spirit.  We were especially proud to showcase LATAM talent on the Fluttercon stage in NYC, standing alongside teammates in a truly global representation of VGV. And in this case, sharing the venue with Droidcon turned out to be a strength. It encouraged crossover—Android developers became curious about Flutter (and vice versa), sparking conversations that wouldn’t have happened in a siloed event. Until next time, Fluttercon. 🦄💙 ### From Pain Point to Product: How Snipd Is Reinventing Podcast Listening with AI and Flutter Source: https://verygood.ventures/blog/from-pain-point-to-product-how-snipd-is-reinventing-podcast-listening-with-ai-and-flutter/ What if you could capture insights from a podcast as easily as you take notes during a meeting? That’s the question that sparked the creation of [Snipd](https://www.snipd.com/), an AI-native podcast app built to change how we learn from audio. [In the latest episode of Build to Succeed](https://verygood.ventures/podcasts/kevin-smith-and-mikel-corcuera-snipd-small-team-big-vision-shipping-fast-with-autonomy-and-ai/), Very Good Ventures founder and CEO David DeRemer sits down with Snipd co-founders Kevin Smith and Mikel Corcuera to explore their startup journey—from personal frustration to a globally used product built with Flutter and powered by AI. ## Solving a Real Problem with AI The idea behind Snipd came from a very real user experience: riding a bike to work while listening to a podcast, hearing something brilliant, and promptly forgetting it by the time you stop pedaling. Snipd makes that moment frictionless. With a simple triple tap of your headphones, Snipd saves the clip and uses AI to summarize the insight. Users can revisit those “snips” at any time, complete with context, titles, and transcripts. “Podcasts are one of the largest knowledge libraries in the world—and AI will fundamentally change how we can interact with spoken audio,” says Kevin. And we agree.  This vision of AI-native interaction—using AI as the foundation of a product experience, rather than merely layering it on top of existing features—underscores Snipd’s unique approach. ## Built with Flutter, Designed for Speed In addition to their AI-first thinking, the Snipd team made a pivotal technical choice early on: building their mobile app with Flutter. That decision allowed them to move fast, maintain a lean team, and create a beautifully consistent experience across iOS and Android. “Flutter lets you create polished experiences that are so much harder in other apps—and I think developers have more joy using Flutter to create them,” explains Mikel.  He shares how Flutter enabled rapid iteration, real-time UI adjustments, and even eliminated the need for a full-time designer, while still delivering a sleek, high-performing app that users love. ## Building Real Products in the AI Era This episode isn’t just a story about another AI tool—it’s a masterclass in building something that matters, using tools and methods that make modern product development faster, smarter, and more joyful. Kevin and Mikel reflect on the evolution of Snipd—from a TestFlight prototype to a full-fledged app serving thousands of users. They open up about the early technical hurdles of building with open-source AI models before ChatGPT even existed, and how they made scrappy but strategic decisions to ship fast and learn faster.  They also share the team’s unique workflow, their approach to feedback loops, and why embracing imperfection was key to making progress. It's an honest, insightful look at building modern products in an era where the tech is still catching up to the vision. ## A must-listen Snipd’s journey speaks to engineers, designers, product thinkers, and startup founders alike. It’s about solving a clear problem, using the right tools for the job, and keeping users at the center of everything. We love sparking conversations like this—sharing diverse stories, lessons learned, and real-world insights.  And there’s a bonus for our listeners: [Snipd’s co-founders are offering 1 month free of Snipd Premium](https://link.snipd.com/Cx7S/vgv), unlocking powerful AI features to help you get more out of every episode. Enjoy! ### Very Good Ventures Hits the Track: Designing for Impact at 200 MPH Source: https://verygood.ventures/blog/very-good-ventures-hits-the-track-designing-for-impact-at-200-mph/ At Very Good Ventures (VGV), we’re all about pushing boundaries when delivering solutions. So when the unique opportunity arose to [design a NASCAR Cup Series paint scheme for Daniel Suárez’s car at Pocono Raceway](https://verygood.ventures/news/vgv-will-sponsor-trackhouse-no-99-chevrolet-at-pocono-raceway/), we immediately were in.  This was an amazing opportunity to collaborate with Trackhouse Racing and take our relationship to the next level, highlighting both brands in a major way. ## Designing at Full Speed The canvas was unlike any we’d worked on before—a fixed, 3D canvas, which is a significant departure from the fluid, multi-screen digital journeys we typically architect. Every inch counted, not just for aesthetics, but for inclusion of critical sponsor placements and adherence to rigid NASCAR regulations. Our challenge became the strategic puzzle of integrating numerous sponsor logos and required association elements, while ensuring our core brand pieces remained cohesive and impactful. That’s where the fun began: We approached this not just as a design task, but as an opportunity for storytelling and brand immersion.  - The vibrant VGV blue forms the car’s unmistakable foundation. - The white nose? A subtle, clever nod to our beloved unicorn mascot’s snout, with a fade designed to convey our speed in delivering work. - Look closely at the roof, and you’ll find our horizontal logo woven into a textured, tire-like pattern: a way to display our full name on the car where space is at a premium, adding depth without clutter.  This scheme was designed with our classic brand elements in mind—the VGV blue, our unicorn, unique shapes, and typeface—ensuring it stands the test of time. ![VGV-branded #99 Chevrolet on track at Pocono Raceway](/assets/images/blog/very-good-ventures-hits-the-track-designing-for-impact-at-200-mph/body-0.jpeg) *Photo courtesy of Daylon Barr Photography* ## Fueling Identity Beyond the main visuals, we sprinkled in plenty of easter eggs for those in the know. Our Flutter expertise is literally built into the car, with Flutter logos subtly placed on the bumper and fenders. Adjacent to the gas tank, you’ll spot "Hot Reload!", a Flutter feature that allows you to quickly inject updated source code into a running application without needing to restart the entire app. But on the #99 Very Good Ventures Chevrolet, it was a playful nod to our development speed, merging the world of fueling a race car with pushing new code.  Above the jack points, "Get Faster" is a direct nod to conversations with Andrew Lukanich, Trackhouse's Chief Revenue Officer, about aligning all goals to gain speed both on track and in support off track, doubling as a literal instruction for the pit crew. And for a truly personal touch, the "A+I" on the rear of the car represents the first initial of VGV's CEO's two daughters, a sentimental play on AI. As a remote and international team, [our passport initiative](https://verygood.ventures/blog/unicorn-passport-celebrating-journeys-achievements-at-vgv/) is a core part of VGV culture—an engaging way to document our teammates’ journeys and celebrate their milestones along the way. This inspired the "bumper sticker" design on the car’s rear, symbolizing our diverse team with unique badges, including both the United States and Mexico flags for Daniel Suárez, alongside a "Global Team" sticker. ![Rear view of the #99 car showing bumper sticker details and flag badges](/assets/images/blog/very-good-ventures-hits-the-track-designing-for-impact-at-200-mph/body-1.jpeg) *Photo courtesy of Daylon Barr Photography* [Our partnership with Trackhouse Racing runs deep](https://verygood.ventures/news/trackhouse-racing-partners-with-very-good-ventures-to-drive-digital-experience-innovation-in-motorsports/); we’re not just a sponsor, but their digital & AI solutions partner. We wanted this collaboration front and center. You'll see the Trackhouse, VGV, and Flutter logos aligned—visually representing a layered partnership: Trackhouse and VGV collaborating on digital innovation, with Flutter as a key part of the technology driving it forward.  This sponsorship was a natural evolution, building on our work in designing their VIP pit box experience, which led directly to this Amazon Prime-broadcasted race. The branding extended beyond the car. We designed Daniel Suárez’s race suit and the crew's gear with the same VGV brand elements, ensuring a holistic visual experience from driver to car to crew, both on and off the track. ![Daniel Suarez race suit and crew gear with VGV branding](/assets/images/blog/very-good-ventures-hits-the-track-designing-for-impact-at-200-mph/body-2.png) *Photos courtesy of Daylon Barr Photography* Ultimately, this project wasn't just about designing a cool race car; it was about strategically driving brand recognition on an entirely new stage. It showcased our ability to blend creative design, brand identity, and technical understanding, delivering a powerful statement for VGV at 200 miles per hour. ## Want to Go Further, Faster? Whether you're looking to elevate your brand, build multi-device digital experiences, or explore what’s possible with AI and Flutter, we’re ready to steer you in the right direction. Let’s build something bold together. [Contact us](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) or [learn more about our work](https://verygood.ventures/success-stories/) to see how we can help you accelerate your vision, with impact. ### Inside Fluttercon USA 2025: Innovation, Leadership & a Very Good Time in NYC Source: https://verygood.ventures/blog/inside-fluttercon-usa-2025-innovation-leadership-a-very-good-time-in-nyc/ [Fluttercon USA](https://www.flutterconusa.dev/) returned to New York City for its second year, once again running alongside Droidcon and welcoming hundreds of developers, product leaders, and innovators to celebrate everything Flutter. From groundbreaking announcements to community-driven initiatives, Fluttercon USA 2025 showcased the continued growth and momentum of the Flutter ecosystem across industries. If you couldn’t make it to New York this time, no worries—here’s a recap of what went down, who was there, and what Very Good Ventures (VGV) brought to the conversation. A Hub for Multi-Device Innovation  With over 500 attendees, 60+ speakers, and 3 jam-packed stages, Fluttercon USA 2025 reinforced the community’s excitement about building multi-device experiences with Flutter. The event was alive with technical deep-dives, networking, and future-focused conversations. Major highlights included sessions from the Google Flutter team, as well as compelling stories of how leading brands like Universal Destinations & Experiences, eBay, Blade, and others are scaling Flutter for modern, cross-platform user experiences. From best practices in architecture to cultural transformation strategies, the event captured the energy of a thriving, fast-growing ecosystem. ## Very Good Ventures: Sharing, Shaping, and Supporting VGV was proud to once again play a central role at Fluttercon USA, showing up in full force with our team of engineers, product leaders, and strategists. Our presence reaffirmed our mission: to advance the future of Flutter through open collaboration, technical leadership, and community empowerment. ![VGV team and booth at Fluttercon USA 2025 in New York City](/assets/images/blog/inside-fluttercon-usa-2025-innovation-leadership-a-very-good-time-in-nyc/body-0.jpeg) *The VGV team & booth* Our engineers were on hand at the VGV booth connecting with attendees, sharing best practices, and handing out some *very* good unicorn swag. They also brought strong technical insights to the Fluttercon stage. Here are a few standout sessions and roundtables we were proud to lead: **\[Session\] Adopting Flutter at Scale: Challenges, Wins, and the Road Ahead** In a panel moderated by VGV CEO David DeRemer, Lawrence Wisne and Mikal Wessel from Universal Destinations & Experiences shared lessons from adopting Flutter at enterprise scale, balancing technical and cultural considerations. **\[Session\] Mastering Scalable Theming for Custom Flutter Widgets** Mauricio Miguez, Senior Engineer at VGV, explored patterns and tooling for creating consistent, scalable, and maintainable theming systems. **\[Session\] Mastering CustomPainter: Going Beyond Widgets in Flutter** Sofía Rey, Mobile Engineer at VGV, showcased advanced techniques with CustomPainter for highly customized, performant UIs. **\[Lightning Talk\] Server-Driven UI in Flutter** Jordan Nnabugwu, Software Engineer at VGV, explained how server-driven architecture can decouple content and logic to power more flexible releases. **\[Roundtable\] Multi-Platform Development with Native Feature Integration in Flutter** Sam O’Brien, Software Engineer at VGV, and Flutter Developer, Matt Carroll, shared strategies for seamlessly blending native functionality with Flutter’s cross-platform flexibility. ![Sam O'Brien and Matt Carroll leading the multi-platform roundtable](/assets/images/blog/inside-fluttercon-usa-2025-innovation-leadership-a-very-good-time-in-nyc/body-1.jpeg) *Sam & Matt leading the roundtable* **\[Roundtable\] Flutter Best Practices: State Management & Scalability** Trevor Davis, Software Engineer at VGV, and Shorebird Founding Operations Lead, Tom Arra,  offered practical advice on architecting apps that scale, diving deep into patterns that balance maintainability and performance.  All of the sessions and roundtables reflected VGV’s commitment to advancing thoughtful, real-world Flutter practices. ## Building Community: From T3 to the Flutter Leaders Dinner Beyond Fluttercon itself, VGV hosted and sponsored two exclusive experiences: **The T3 Engineering Leadership Summit** On June 24, the day before Fluttercon kicked off, senior engineering leaders gathered to discuss agentic AI, multi-platform development, and leadership at the intersection of technology and culture. **Flutter Leaders Dinner**  On evening of June 24, we hosted a curated dinner reception bringing together decision makers from Universal Destinations & Experiences, eBay, Blade, SKIMS, GEICO, the Google Flutter team, and others to share how they’re evolving their digital experiences with Flutter. ![Flutter Leaders Dinner hosted by VGV with industry decision makers](/assets/images/blog/inside-fluttercon-usa-2025-innovation-leadership-a-very-good-time-in-nyc/body-2.jpeg) *Flutter Leaders Dinner* These events created valuable space for deeper relationship building and sparked conversations that will continue to shape the future of the community. ## Team Impressions from Fluttercon NYC “What truly stood out at Fluttercon USA this year was the people," said Mauricio Miguez. "The energy was high, the conversations meaningful, and the in-person meetups with VGV teammates added something special. The sense of camaraderie and shared purpose made it all worth it. Chris Sells’ keynote in particular was a standout moment—offering a thoughtful vision for how the Flutter community can continue growing in an AI-driven world.” Sofía Rey: “The energy at Fluttercon USA 2025 was truly contagious—both from the global Flutter community and our own VGV team. Giving my first talk in English was a big milestone, and demoing a real-world app we’ve been building for months made it even more meaningful.” Fluttercon USA 2025 once again proved that the future of Flutter—and multi-device innovation—is bright, collaborative, and very *good*. ### How Etsy’s Mobile Team Built a Feature-Rich Seller App with Flutter Source: https://verygood.ventures/blog/how-etsys-mobile-team-built-a-feature-rich-seller-app-with-flutter/ In the ever-evolving world of mobile development, velocity and quality often feel like opposing forces. But what if you could have both? [In this episode of “Build to Succeed,” we sat down with Landon Robinson, Staff Engineer at Etsy](https://verygood.ventures/podcasts/landon-robinson-etsy-from-platform-silos-to-product-unity-a-new-era-of-app-development/), to explore how his team built a powerful, feature-rich mobile experience for millions of Etsy sellers—without ballooning team size or compromising on design. The secret? Strong architecture, cross-functional collaboration, and a product-first mindset. ## Building a Modern Mobile Stack  Landon’s journey from native development to Flutter mirrors a larger trend in the mobile space. While skeptical at first, he was won over by Flutter’s rendering engine, flexibility, and tooling, eventually helping lead Etsy’s transition from a shared native codebase to a standalone Flutter app for sellers. Today, Etsy’s Flutter-based Seller app is maintained by a focused team of just eight engineers, yet it supports rich functionality and rapid iteration. > “We’ve been able to build a lot of features in a short amount of time… resulting in a feature-rich app that can really help our sellers manage their shops on the go,” Landon shares.  One of the key drivers behind that speed was intentionally designing for developer experience from the start. From abstraction layers to documentation and onboarding guides, the team built not just a product, but a platform others could plug into. > "Early on, we thought a lot about the developer experience," he adds. "Our goal was not only to speed up development, but also to allow other teams to come in and build." ## The “Away Team” Model: Cross-Functional Collaboration That Works One of the most innovative aspects of Etsy’s development model is what Landon calls the “away team” approach. This allows engineers—often from web or full-stack backgrounds—to embed with the Seller app team temporarily to build specific features directly in Flutter. Backed by strong internal docs, Codelabs, and mentorship, these away teams are able to ship production-ready code in weeks, all within a well-governed architecture that ensures quality and consistency. “People start focusing on the product itself, as opposed to being confused by ‘what can we do on Android or iOS?’ It’s more of a product-driven mindset…and teams reorganize around product domains,” Landon reaffirms.  That cultural shift—from platform silos to product teams—has enabled Etsy to scale not just code, but shared ownership. It’s an example of how the right combination of tech and process can empower teams to deliver faster and more collaboratively. ## Why Flutter Made the Difference For Etsy’s team, choosing Flutter was about more than code reuse. It was about removing the friction between platforms, between teams, and even between engineering and design. Because Flutter allows pixel-perfect design implementation and cross-platform deployment, the team rarely had to “dumb down” experiences to meet technical constraints. Combined with a modern mobile architecture and strong QA practices, this enabled Etsy to ship more with less while still meeting the expectations of millions of sellers. ## Looking Ahead: The Role of AI in Developer Productivity While Etsy’s Seller app team hasn’t yet deeply integrated AI, Landon is optimistic about the possibilities. With tools like GitHub Copilot already in use by some engineers, the team sees AI as a potential force multiplier, especially for repetitive tasks or infrastructure work. “If AI can help us be force multipliers of ourselves, why wouldn’t we use it? We’re definitely excited to see how we can layer it into our already smooth process,” he concludes.  As AI continues to evolve, the goal isn’t to replace engineers—it’s to amplify their impact. That vision aligns perfectly with the team’s foundational choices: tools and practices that unlock creativity, ownership, and focus. ## Engineering With Purpose, Grounded in Impact and Trust Through all the talk around technology, Landon is clear about what really matters—building solutions that make sense for the business. This mindset permeates Etsy’s Seller app strategy: from platform decisions to team structure, every move is grounded in real, measurable outcomes, not just engineering experimentation. On the other hand, when asked about what strong leadership looks like, Landon emphasizes trust, communication, and clarity. Empowering engineers to do their best work and then supporting them with the right structures is key to sustaining both morale and momentum. And that empowerment goes both ways. By building a team culture rooted in shared goals and open collaboration, Etsy has created a development environment where engineers from different disciplines—mobile, web, and platform—can come together and build something better, faster, and smarter. ## Listen to the Full Episode In a world where tools and frameworks evolve rapidly, Etsy’s experience is a reminder that the foundations of great software are timeless: clear goals, strong teams, and a deep understanding of the people you’re building for. If you’re leading a mobile team, scaling cross-functional collaboration, or evaluating the impact of Flutter and AI in your org, this conversation is a must-listen. Enjoy! ### Vibe Coding with AI Agents: Building Figma Plugins in with Chat UI Source: https://verygood.ventures/blog/vibe-coding-with-ai-agents-building-figma-plugins-in-with-chat-ui/ At this year’s AI Demo Day, I shared how I’ve been using AI IDE agents—specifically Cursor and GitHub Copilot—to build custom Figma plugins for the community, VGV, and even for our clients like Toyota. These tools give me the ability to write complex code, quickly validate ideas, and automate repetitive parts of my design workflow, despite not knowing a whole lot about code.  Being able to build your own plugins is a total game-changer. It gives us control over our workflow, helps us move faster, and unlocks entirely new capabilities. Plugins aren’t just for designers, either—FigJam, Dev Mode, and even Figma Slides support plugins now. That means everyone on a team can benefit from tailored tooling. ![Overview of AI-powered Figma plugin development with Cursor and Copilot](/assets/images/blog/vibe-coding-with-ai-agents-building-figma-plugins-in-with-chat-ui/body-0.png) ## What Figma Plugins Actually Are In case you’ve never peeked under the hood: Figma plugins are just web apps built with JavaScript, HTML, and CSS. You use the Figma Plugin API (not to be confused with the Figma REST API) to read/write directly to a file, leveraging the user's own credentials (no API keys needed). It even gives you access to variables without requiring an enterprise plan! There are different types of plugins, too: - Simple single-use scripts - Menu-driven tools - Full interactive UIs Bottom line: if you can code it (or ask an AI to code it), you can build it into Figma. ![Figma Plugin API architecture: JavaScript, HTML, CSS accessing the file](/assets/images/blog/vibe-coding-with-ai-agents-building-figma-plugins-in-with-chat-ui/body-1.png) ## From Curious to Custom: My Plugin Journey My journey into plugins started with something called Very Good Flutter Styles—a plugin that exports Figma color and text styles into Dart code for Flutter themes. That plugin took me weeks to build manually; I used ChatGPT, hacked around, and honestly had little confidence about the best way to approach the implementation. But I managed to get it working! And adding Dev Mode support made it helpful for both designers and devs. It automated a tedious task and made our handoffs faster and more accurate. Since then, I’ve built and released a bunch of plugins, everything from variable renaming, calculating, and matching values across modes. And the key to this breakthrough? AI agents. ![Timeline of plugins built, from Very Good Flutter Styles to later AI-assisted ones](/assets/images/blog/vibe-coding-with-ai-agents-building-figma-plugins-in-with-chat-ui/body-2.png) ## AI + Figma Plugins = Superpowers for Designers With tools like Cursor and Copilot, I’ve gone from fumbling through JS tutorials to shipping useful, production-grade plugins in as little as a few hours. Let’s look at some recent examples: ### Very Good Aliasing (4 hours to POC) Often in design systems, there are raw color values related to a brand (e.g., blue or red), and separate semantic roles (e.g., primary or secondary) that all appear in design tokens. For complex products, making sure the brand color and its variants stay synced and updated across all of their semantic roles can be tedious. This plugin bulk-assigns variables to their semantic usage in seconds, providing a quick way to configure a theme. It replaces hundreds of clicks down to a few for each color. It’s a game changer. Using Cursor, I even built a variant that re-aliases based on values instead of names in about an hour. ### Very Good Flutter Variables (4 hours to POC) A follow-up enhancement to the Very Good Flutter Styles plugin, this one exports Figma variables and modes as Dart code, which can generate light and dark themes in a single export. I’d actually tried and failed at this plugin before, but AI made it happen in hours. ### Toyota Icon Lint (2 hours to POC) We’ve been working with Toyota to scale their powerful design system used in their in-vehicle center console. I built this plugin to validate icon formatting rules for their internal asset pipeline, to check the settings for things like fill-rule, orientation, color, and name. It flags dozens of rules across hundreds of icons in seconds instead of requiring manual spot checks, which could take minutes per icon. Now it’s used by global teams, giving time back to work on more meaningful features. ![Toyota Icon Lint plugin validating icon formatting rules in Figma](/assets/images/blog/vibe-coding-with-ai-agents-building-figma-plugins-in-with-chat-ui/body-3.png) My AI Agent Workflow (No Engineering Degree Required) I’ve developed a simple, iterative workflow that’s worked for me. It allows you to quickly start with an idea and evolve it through small requests. This method allows you to gain a better understanding of how the code works, making it easier to identify where issues are arising. 1. Start with a clear vision: I write a quick doc in Notion with plugin goals, key data structures, and examples of input/output. 2. Use Cursor in Agent + Auto mode: It writes and updates code for me—hands off! It also picks the best model for the job. 3. Keep requests small and focused: Too much context can cause issues that take time to fix. Focused steps = success. 4. Provide examples: When I don’t know the right jargon, I show the AI my expected inputs and outputs. 5. Use version control: The agent will break your code eventually. I use GitHub Desktop to save and explore safely. 6. Ask for help! The agent can log values, debug, and explain errors, just ask. It can often fix issues based solely on the logs. ## The Hardest Part? Not What You Think. Most of the actual plugin logic was easier than I expected. But the real challenge? Making it usable. Things like: - Validating edge cases - Preventing user errors - Handling weird Figma property values (like rotation 0 = -0.000003e12 😩) ![Edge case challenges including Figma rotation values and property quirks](/assets/images/blog/vibe-coding-with-ai-agents-building-figma-plugins-in-with-chat-ui/body-4.png) ## My Big Takeaways 1. **Everyone should try this.** You don't need to be an engineer. These tools let you test, build, and explore in ways that were never possible before. 2. **AI opens new doors.** I now look at problems differently: not as constraints, but as opportunities for automation using new processes and tools. 3. **We can build better, more personal tools - fast.** Whether it’s internal workflows, client handoffs, or even just reducing frustration, these plugins improve how we work. 4. **AI agents can do way more than plugins.** Cursor can easily scaffold and build a Flutter app—components, themes, interactions—all through chat. If you’re curious about building Figma plugins, or even what you might be able to create with a little help from an AI agent, I hope this shows you what’s possible. And if you ever want to chat about plugins, Figma, or Cursor, I’ll be happy to talk about it, so feel free to [reach out](https://www.linkedin.com/in/murphyorourke/)! ### Merging Culture, Codebases, and Care: How Headspace Rebuilt With Flutter Source: https://verygood.ventures/blog/merging-culture-codebases-and-care-how-headspace-rebuilt-with-flutter/ When you think of a technical migration, you might picture lines of code, build systems, or platform APIs. But for Pierre Benz, Engineering Manager at Headspace, the real work of merging two mental health apps into one went far beyond the code. It was about people, purpose, and building the kind of technology that can truly support someone’s well-being, especially when they need it most. [In the latest episode of Build to Succeed](https://verygood.ventures/podcasts/pierre-benz-headspace-merging-culture-codebases-and-care-with-purpose/), Pierre walks us through how his team navigated one of the most ambitious transformations in Headspace’s history. Along the way, he opens up about leadership, lessons learned from failure, parenting, and why sometimes the hardest part of change is letting go of what used to work. ## A Career Built on Empathy and Experimentation Before leading engineering at Headspace, Pierre’s career had taken him through design agencies like AKQA, startups in IoT and consumer apps, and even early explorations in game development. His technical roots are deep, but what stands out most is his focus on human behavior. That focus began at the University of Cape Town, where Pierre blended his computer science studies with human-computer interaction and psychology. It’s a thread that has continued throughout his career, shaping how he approaches product development, team leadership, and user experience. Surrounded by technically brilliant peers early in his career, Pierre chose to focus on a different strength: understanding the why behind technology use, not just how to build it. ## The Merger That Changed Everything Pierre joined Headspace just as the company merged with Ginger, a behavioral health platform offering coaching and therapy. The result? Two different apps, two different codebases, and one big challenge: how to bring everything together in a single product experience without losing momentum or user trust. At first, they approached the problem the only way they could: by patching it. A shared Flutter SDK was built and embedded into the existing native apps, a move Pierre jokingly refers to as their “Frankenstein moment.” It worked, but it wasn’t scalable. Maintaining multiple stacks slowed development, and the team knew something had to change. ## Making the Case for Flutter That’s when the real decision-making began. After a round of internal research and heated debate, the team faced a turning point: go all-in on Flutter, or continue to juggle three partial implementations across iOS, Android, and web. The final decision came from Headspace’s CTO, who saw the bigger picture: a single codebase would allow the company to scale faster, reduce costs, and deliver a more unified experience across platforms. It wasn’t just a tech decision, it was a mission decision. “Life’s gonna change. Let’s ramp up experimentation. Let’s scale things more. And we now have one platform where we can address those changes,” he recalls.  For Pierre and many native developers, it was a tough pill to swallow. Not everyone was on board. Some engineers left. Others took time to upskill in Dart and Flutter. But for those who stayed, it sparked a mindset shift—from resistance to curiosity to momentum. “The ecology of devices is why we chose Flutter. People use multiple devices interchangeably. We can get an app out that can cover all those devices,” Pierre reflects. ## Building While Migrating (and Not Burning Out) One of the hardest parts of the migration was that they didn’t pause feature development: instead of stopping everything to rebuild, the team adopted an incremental approach, slowly replacing key surfaces with Flutter while continuing to ship updates. It wasn’t the cleanest method, but it was realistic, and it allowed the team to learn in motion. Pierre speaks candidly about the complexity of this strategy, from keeping analytics intact to migrating UI components without breaking existing features. But he also highlights the unexpected wins, like how quickly new developers got up to speed and how Flutter exceeded design expectations. ## Centralizing the Mission: One Team at a Time Eventually, the team learned that spreading migration responsibilities across too many squads wasn’t working. So they pulled together a dedicated migration team: 27 engineers focused solely on rebuilding the app’s foundation. That centralized effort not only accelerated progress but also helped create a shared sense of ownership and clarity. Once the migration is complete, those engineers will return to their respective teams as Flutter champions, mentoring others and helping maintain quality across the newly unified codebase. ## Doing the Hard Things Right At VGV, we believe technology should be built for people, and this story from Headspace proves exactly why that matters. It’s a rare and honest look at what it means to transform systems, cultures, and teams without losing sight of the human impact. This is a blueprint for doing the hard things well: with empathy, clarity, and a willingness to rethink everything for the sake of delivering real value. We would love to hear your story and help you start your Flutter journey. [Get in touch with our team today!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) ### Smart AI App Dev Without the Chaos Source: https://verygood.ventures/blog/smart-ai-app-dev-without-the-chaos/ At Very Good Ventures (VGV), speed and architecture are always key priorities. But when it comes to integrating AI into the software development process, the risk of sacrificing quality for velocity is real, especially in the era of “vibe coding.” At a recent internal AI Demo Day hosted by VGV, the goal was to inspire experimentation, spark learning, and deepen our collective fluency in generative AI. It wasn’t just about showing off cool demos, but also about understanding how AI is reshaping the way we build, think, and work.  During this exciting remote event, I had the opportunity to explore a big question we’re all facing as developers: Can you actually use AI to build a real app fast, without ending up with unmaintainable code? The results were surprising in all the right ways, and it didn’t stop with the code. I’m not going to spoil the ending, but let’s just say it worked better than I expected, and it gets even more interesting when you see what I was able to build into the app once the foundation was solid. ### The Challenge: Build an MVP App with AI Without Sacrificing Structure We’ve all seen what AI can do with code: fast prototyping, fast generation, fast everything. But the tradeoff is usually a mess—scattered logic, unclear architecture, and technical debt from day one. At VGV, we can’t afford that. Our apps need to scale, evolve, and be passed between engineers without causing pain. So I built this experiment around a core hypothesis: Can structured prompting actually guide AI to produce clean, maintainable code that follows good architecture, fast enough to matter in a real product cycle?  For this experiment, I used Gemini 2.5 Pro and Flutter to build an MVP for a travel itinerary planner app. The idea was simple: take in user preferences (location, dates, interests, travel style, etc.) and generate a personalized travel plan, complete with suggested activities, links to bookings, and the ability to save and revisit itineraries. The kicker? I built almost the entire app part-time over 1.5 weeks, using AI to assist with code generation, planning, and iteration. This app relied on Gemini and OpenRouter for all AI-driven functionality. ## The 5-Step AI Prompting Framework To bring structure to the process, I started with some [excellent prompts](https://www.jointakeoff.com/courses) created by [Mckay Wrigley](https://www.mckaywrigley.com/) and adopted them for Flutter. This gave me a five-step framework that mirrors how we’d typically approach development, just with AI taking on a bigger role at each phase. The cool thing about Mckay’s prompts is that they’re designed to work as a chain, with the output from one prompt serving as an input to the next. ### 1. App Idea → Project Request Prompt Start with a short paragraph describing what you want to build. Then iterate with the AI using the Project Request prompt until it fully understands the goal. ### 2. Project Request → Tech Spec Prompt Take the Project Request from the previous step and feed it into the Tech Spec prompt to generate a technical spec. The spec includes such details as project structure, database schema, class definitions, and overall data flow. ### 3. Tech Spec → Coding Plan Prompt Here’s where things get *very* detailed. Using the tech spec as input, you can use the coding plan prompt to produce a full step-by-step implementation plan. The plan is very detailed—if I had printed it, it would’ve been 40+ pages long. ### 4. Coding Plan → Code Generation Now I've moved into actual code. I followed the plan and had Gemini generate code step by step, constantly reviewing to avoid architectural drift. ### 5. Code Context & Refinement: Windsurf + Manual Review At the end, I used contextual prompts and Windsurf to help refine and clean up the code. This structured prompting system gave me control over the process, made it easy to course-correct when things were going off-track, and let me build something that wasn’t just “done”, but it was maintainable. ## Results: Speed and Quality Here’s what I ended up with: - Speed: The MVP came together in about 1.5 weeks of part-time work. - Structure: The code stuck to VGV’s layered architecture surprisingly well. - Maintainability: It wasn’t perfect, but the final codebase is absolutely something I’d be happy building on or handing off. Yes, I did need to do some cleanup, especially formatting, organization, and trimming over-commented sections, but the major architectural pieces were pretty good right out of the AI. [Structured prompts](https://www.jointakeoff.com/courses) prevented major architectural drift. The foundation was solid. #### AI Inside the App The real surprise? Because AI let me move so quickly, I actually had time to add AI-powered features into the app experience itself. This resulted in an AI-enhanced travel itinerary planner. Here’s how it works: - Users submit where they’re going, dates, interests (e.g., food, nature, culture), who they’re traveling with, and their pace/budget. - The AI analyzes those preferences, recommends activities, and builds a personalized itinerary. - It even tells users when to book tickets in advance and links them to booking sites. - They can save plans, tweak them, and revisit past itineraries, all in the app. #### AI Tech Stack I Used Even though I wasn’t familiar with all these tools, I was able to integrate them quickly, thanks to clear prompts and strong planning up front. - Supabase – Auth, itinerary storage, and Edge Functions - OpenRouter – LLM endpoints - Gemini 2.5 Flash (Preview) – Core LLM for suggestions and itinerary logic - Flutter – Multi-platform UI, of course #### What I’d Do Differently Next Time I was happy about how this process worked, but there are a few things I’d tweak to make it even better next time: - Define the UI design system early: If it’s a client demo, nail down the look and feel before coding. - Reduce AI verbosity: Some prompts generated way too many comments. I’ll ask for cleaner output next time. - Include more VGV-specific guidance: Folder structure, DI, DTOs. I’ll bake that into the tech spec prompt from the start. - Slow down between steps: It’s tempting to rush into code, but reviewing each phase carefully made a huge difference in quality. ## What This Means for VGV—and Everyone Out There  I was able to create a working MVP built in about a week and a half (part-time), with code that largely followed VGV’s layered architecture. Even better, the efficiency gave me time to build actual AI-powered features into the app itself. This wasn’t just a cool internal project; it’s a sign of where our workflows can go. We can use this approach for faster prototyping, internal tooling, and POCs, and we can also show real, working software earlier to our clients, to build trust and show them what we can do. AI helps us move faster without compromising how we build, and it doesn’t have to be chaotic. With the right structure, it can be one of the most consistent developers on the team. We’re not using AI to cut corners, we’re using it to build better software, faster. And this is just the beginning. If you have questions or ideas to improve this process, or just comments, feel free to reach out to me on [LinkedIn](https://www.linkedin.com/in/mark-winters-62bb205/)! ### Google I/O 2025: It’s a Great Time To Be a Technologist! Source: https://verygood.ventures/blog/google-i-o-2025---its-a-great-time-to-be-a-technologist/ I must start with a confession: never in my life have I felt so excited about technology and the endless possibilities it brings to humanity. Advancements in cloud computing, artificial intelligence, and multidisciplinary digital devices—whether they’re phones, glasses, vehicles, or the Las Vegas Sphere—make this particular moment a pivotal one in history, where we can solve more complex and sophisticated problems than ever at a fraction of the cost. The conclusion of Google I/O 2025 left me with one clear thought: it’s a great time to be a technologist! ## Flutter: The *Best* Way to Build Digital Experiences in 2025 Across Multiple Devices and Operating Systems For years, I’ve avoided using the word best in the context of technology, especially when building user interfaces. The reason? Technology can be assessed from multiple points of view, and what brings me joy as a developer may be in conflict with the user’s joy. This, I believe, is the reason why HTML5-based tools didn’t fully succeed in the past. However, Flutter, year over year, has proven to satisfy the needs of businesses, developers, and customers on any single device. Whether you’re building for phones, kiosks, flying taxis, or smart scooters, with the power of a single toolkit and a common set of tools, you can build and adapt your digital experiences anywhere your customers are—without sacrificing velocity, quality, or performance. From fintech and e-commerce to automotive and entertainment, brands like Nubank, Toyota, Alibaba, BMW, and Universal Destinations & Experiences, among many more, are turning to Flutter to power their most ambitious digital products. Google I/O 2025 marked a new era of maturity and production readiness for Flutter and Dart—they no longer need to announce support for new platforms, as they now support all platforms. Anywhere there’s a screen, you can run Flutter. Now, they can focus on refining existing pain points while addressing major competitive advantages that continually enhance the quality and performance of the framework. ![Flutter adoption stats showing nearly 30% of new free apps at Google I/O 2025](/assets/images/blog/google-i-o-2025---its-a-great-time-to-be-a-technologist/body-0.jpeg) *Image taken by Flutter’s GDE Anna Leushchenko at Google I/O 2025 during Craig Labenz’s presentation.* According to Apptopia, Flutter now powers almost [30% of new free apps](https://medium.com/flutter/dart-flutter-momentum-at-google-i-o-2025-4863aa4f84a4). That list includes some major brand names, including Universal Destinations & Experiences (in their newest theme park, Universal Epic Universe in Orlando, which Very Good Ventures helped to deliver); [GEICO](https://www.geico.com/techblog/flutter-as-the-multi-channel-ux-framework/); Google (NotebookLM & Google Cloud); and GE Appliances, just to name a few in the last year! Furthermore, companies like LG and Canonical are using it to build the frontends for their operating systems, elevating the possibilities of what we can do and the experiences we can power with Flutter. ### Is Dart The Most Effective Programming Language Out There? ![Dart language evolution timeline from Google I/O 2025](/assets/images/blog/google-i-o-2025---its-a-great-time-to-be-a-technologist/body-1.jpeg) Dart language evolution. Image obtained from [*Dart & Flutter momentum at Google I/O 2025*](https://medium.com/flutter/dart-flutter-momentum-at-google-i-o-2025-4863aa4f84a4) Dart’s goal is to offer the most productive programming language for multi-platform development, paired with a flexible execution runtime platform for app frameworks. While null-safety was released more than 5 years ago, we continue receiving amazing updates that, while they might look small in size, are huge in value. With **null-aware elements,** it’s now possible to include elements in collection only when the value at runtime is non-null. ![Dart null-aware elements syntax example for conditional collection items](/assets/images/blog/google-i-o-2025---its-a-great-time-to-be-a-technologist/body-2.png) On the other hand, with the **dot shorthand** syntax under current development, we’ll be able to remove duplicates (e.g. no need to overstate *enum* types) on our code based on context, without sacrificing meaning, *alá Swift!*  ![Dart dot shorthand syntax removing enum type duplication](/assets/images/blog/google-i-o-2025---its-a-great-time-to-be-a-technologist/body-3.png) Dart continues to improve its syntax with each release, maintaining a minimal learning curve that has enamored developers worldwide, without sacrificing developer productivity, and convincing teams and companies of all kinds to trust Dart for their frontend and backend needs. ### Seamless Interop: Access Native APIs With Zero Effort At Very Good Ventures (VGV), we’ve helped more than a hundred companies successfully execute their adoption (or transition) to Flutter. One of the biggest questions we see companies asking is, “Will a particular SDK I need to use have support for Flutter? How can I adopt new native functionality as soon as possible?”*.*  Flutter’s response to this is [method channels](https://docs.flutter.dev/platform-integration/platform-channels). They even built tools like [Pigeon](https://pub.dev/packages/pigeon) to automate and generate as much code as possible, but there was still a significant amount of work for every platform that you needed to integrate. [Seamless interop](https://medium.com/flutter/flutters-path-towards-seamless-interop-4bf7d4579d9a) (still on Early Access Phase), will allow developers to access native APIs synchronously, will support tree-shaking (that is, removing unused code at compilation, reducing the final bundle size of the application), and will allow more data to live in the platform later by relying on codegen solutions based on [FFIgen](https://dart.dev/interop/objective-c-interop) and [JNIgen](https://dart.dev/interop/java-interop). ### You Landed With The Right Team The VGV team has seen firsthand how Flutter and Dart can transform the way teams design, develop, and ship digital products across platforms. If you're exploring how to bring that same efficiency, flexibility, and performance to your next app, don’t hesitate to [reach out](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411)! ### Building with Conviction: Deep Tech, AI, and Long Bets Source: https://verygood.ventures/blog/building-with-conviction-deep-tech-ai-and-long-bets/ [In this episode of the *Build to Succeed* podcast](https://verygood.ventures/podcasts/david-goldman-celesta-capital-investing-in-deep-tech-that-shapes-the-future/), Very Good Ventures’ (VGV) CEO & Founder, David DeRemer, speaks with David Goldman, Partner at Celesta Capital, to explore what it means to invest in deep tech, why conviction matters in venture capital, and how AI can drive meaningful societal change when applied with purpose -not just for businesses, but for society at large. ## Investing in Technology That Lasts Celesta Capital is no ordinary VC firm. They focus on what they call deep tech: companies rooted in real technical innovation, not hype. They work with startups at the earliest stages, backing founders who are thinking years ahead and building toward breakthroughs that can reshape industries. Their goal is to support companies with strong, lasting innovations from the start. Celesta’s model isn’t about fast returns or chasing the next big trend—it’s about commitment and standing behind teams for the long haul, through market shifts, macro changes, and hard decisions. When they invest, they’re signing up to be part of a journey that could last a decade or more. It’s about backing companies that are built on real technology and long-term vision. And that requires conviction, both from investors and from founders. Goldman encourages teams to focus on building things that matter, not just following trends. He adds that clarity and grit, especially in uncertain markets, can be a competitive advantage in itself. ## The Power of Conviction In an era where capital can chase trends and exits, Goldman is looking for founders who bring something deeper: clarity of purpose and long-term grit. He notes that the most successful startups don’t just survive chaos, they move through it with focus and adaptability. Founders and leaders need to be able to focus through the noise, especially in chaotic moments. That’s how they make smart decisions, avoid knee-jerk reactions, and keep moving forward when others get stuck. And that same focus applies to investors: real innovation requires more than capital. It demands a commitment to backing bold ideas before they’re widely accepted—when the road ahead is still foggy, but the potential is clear. Multidisciplinary collaboration, organizational cohesion, and leadership integrity are key.  ## Rethinking the Role of AI Goldman is bullish on AI, but not for the usual reasons. While many companies focus on cost savings or replacing labor, he encourages teams to think bigger and more responsibly: “I’m very broadly optimistic about AI. Anything we can do to improve productivity and make healthcare, housing, and education more accessible to people is going to make \[their\] lives a lot better.” He challenges founders and leaders to be intentional: “Ask yourself: Where can we create the most value for our customers using AI, or creating our own AI? And what is the threat if someone recognizes the upside before us?” This kind of thinking, he argues, moves companies out of the cycle of reaction and into a more strategic mindset, one that creates real, defensible value. ## What’s Next: Opportunity Beyond the Infrastructure Layer Right now, most AI investment is concentrated on foundational models and infrastructure (what Goldman calls “the supply layer” of AI). But the next wave of growth, he says, will come from vertical applications that deliver real-world impact. Think: healthcare tools, construction productivity, or education solutions that scale access and improve outcomes. These aren’t hypothetical moonshots—they’re practical, necessary, and closer than we think. What’s missing isn’t the tech. It’s the clarity of purpose to apply it meaningfully. ## Think Bigger, Stay Grounded As AI evolves and deep tech reshapes industries, Goldman believes the companies that succeed will be the ones that focus on their core values, avoid distractions, and maintain organizational clarity. It’s not just about building fast, it’s about building what matters. Whether you’re a startup founder navigating an uncertain market or a product leader exploring what AI can do, the message is the same: stay grounded in purpose and build with conviction! ### Why Mobile-First Fintech Companies Are Betting On Flutter Source: https://verygood.ventures/blog/why-mobile-first-fintech-companies-are-betting-on-flutter/ Since its official launch in 2017, Flutter has seen exponential growth in usage, accounting for 30%^1^ of new apps in the Play and App Store. Its high-performing capabilities, modern widget-based UI development, and a mature ecosystem of plugins and packages make it the perfect choice for complex environments where time to market, customer satisfaction and innovation are at the center of the business. Choosing a hybrid or multi-platform solution should come from a thoughtful evaluation of your product’s needs. At Very Good Ventures (VGV), we believe the right approach depends on the project, which is why we work closely with clients to identify the best tools for their unique goals. Whether it’s a full Flutter build or a hybrid architecture using native plugins, we guide product teams through smart, future-ready decisions that support flexibility, scalability, and long-term success. But when it comes to Flutter for fintechs, there's a common myth we still hear: that native development is the only way to go because of performance or hardware features. Luckily, we’re here to set the record straight. We know that security, performance, and reliability are everything for fintechs—and we know that Flutter’s right for the job. Read on to see how far your fintech can go with the power of Flutter. ## Multi-Platform Powers Here’s the truth: the idea that only native development can handle fintech-level demands is outdated. Today’s multi-platform tools—especially Flutter—can power secure, robust, feature-rich applications without sacrificing usability or visual quality.  As the leading Flutter agency, we build high-performance, scalable apps from a single codebase, reducing costs while expanding reach. Our close partnership with Google ensures we push Flutter’s boundaries to deliver seamless, future-ready apps.  Leading financial institutions like Tide (business banking), Google Pay, VISA, GEICO, and Credit Agricole Bank Polska didn’t choose Flutter because of hype; they chose it because it delivers on the three pillars of fintech success: speed, performance, and scalable efficiency. From our proven experience, the end-user can’t tell the difference between a well-built native app and a Flutter app. When it comes to using device-specific capabilities like Face ID, biometrics, Apple Pay, and NFC, Flutter has robust plugin support. And for features not supported out of the box, you can always build custom native plugins while still maintaining the speed and flexibility of a Flutter codebase. ## One Codebase, Limitless Opportunities In a fast moving, security-focused industry like fintech, choosing the right technology is a strategic decision. Flutter stands out by combining native performance, design flexibility, and faster development into a single, scalable platform. With one codebase, fintech teams can deliver high-quality apps for iOS and Android, accelerate time to market, and maintain consistency across platforms. Unlike frameworks that rely on bridges or native components, Flutter compiles directly to machine code, ensuring smooth performance and reliability. Its robust tooling and modular architecture empower teams to iterate quickly and manage design systems effectively, without compromising on security or user experience. In a nutshell, this is why Flutter works for fintech: - Accelerated development with a single codebase for iOS and Android - Consistent, customizable UI with pixel-level control - Lower development and maintenance costs - Secure integrations like Face ID, NFC, Apple Pay, and Google Pay - Built-in testing tools for efficient QA and delivery - Scalable architecture built for long-term growth ## Real Flutter Success Stories In Fintech  Here are some proven outcomes from industry leaders embracing multi-platform development. Check them out:  ### Dow Jones: MarketWatch Virtual Stock Exchange Faced with a tight 3-month deadline, Dow Jones needed to bring their Virtual Stock Exchange to mobile while reducing development costs. Using Flutter, VGV delivered a high-performance, multi-platform app 50% faster and at half the cost of native. The result: an engaging, scalable, education-focused product that’s ready to grow. > “Thanks to Flutter and VGV, we delivered on our goal of building a fun, engaging, and performant app for all screens and mobile devices in significantly less time and cost than if we built it natively,” - Clarence Kwei, SVP Consumer Technology, DOW JONES [Read the complete success story here](https://verygood.ventures/success-stories/marketwatch-stock-market-game/). ### Betterment: Scaling Architecture for a Mobile-First Future As Betterment shifted to a mobile-first strategy, Flutter offered a flexible path. Instead of a full rebuild, VGV integrated Flutter into their existing native codebase. With hands-on training, the internal team quickly gained confidence, enabling a smooth transition toward scalable mobile development. > “We really just liked the experience of Flutter(…)the libraries that came out of the box, the widgets, the component sets (…) it accelerated our development,” - Sam Moore, VP of Architecture at Betterment. [Get the full story here on our podcast.](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) ### Nav Technologies: Enabling Mobile-First for Small Business Owners Nav wanted to modernize their mobile app for time-strapped business owners. With VGV’s partnership, they updated to the latest Flutter version, rebuilt mobile architecture, and rolled out new features at a consistent two-week cadence. Nav saw their registered mobile user base double, while empowering other teams to build on mobile. > “Where the app is, we can really accelerate the kind of features that we want. We are now called the Mobile Foundations Team. We are creating the basis for all the other vertical teams to start building on mobile. Now we are populating our skills to other teams, and they will start building directly on the mobile app,” - Zain Khan, Head of Product at Nav. [Learn more about their Flutter journey here.](https://verygood.ventures/success-stories/nav-fintech-flutter-app/)  ### Berkheimer: Modernizing Tax Filing for Millions of Users As Pennsylvania’s largest tax authority, Berkheimer needed a mobile solution that balanced usability, performance, and security. With Flutter, VGV delivered an intuitive e-filing app, complete with secure cloud infrastructure and advanced document scanning integrations, extending Flutter’s capabilities even further. [Discover their transformation story here.](https://verygood.ventures/success-stories/h-a-berkheimer/) ### How Nubank and Global Fintechs Are Winning with Flutter Nubank, Latin America's largest fintech bank, adopted Flutter to scale mobile development for over 100 million users, tackling inefficiencies in native development that slowed innovation and feature delivery. Coupled with their use of Server-Driven UI (SDUI), Nubank built a modular, scalable architecture that allows real-time updates and continuous delivery without relying on app store releases. The combination of Flutter and SDUI has enabled Nubank to accelerate iteration, streamline collaboration across teams, and deliver seamless experiences at a massive scale. > "Imagine if every time you needed to build a new screen, you had to touch mobile, backend, database, and infrastructure. That’s not scalable. What if instead, you had high-level platforms that abstract away all the complexity?" - [Thiago Ghisi, Director of Engineering at Nubank.](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/) [Explore their full story here on our podcast.](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/) ### Let’s Talk Fintech + Flutter At Very Good Ventures, we specialize in multi-platform fintech solutions built with Flutter where you can invest once, innovate everywhere. Whether you're modernizing a legacy app or launching something brand new, we’re here to help you do it faster, smarter, and with confidence.  Have a project in mind? VGV is the partner to make it happen. [Contact us!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) ### 7 MCP Servers Every Dart & Flutter Developer Should Know Source: https://verygood.ventures/blog/7-mcp-servers-every-dart-and-flutter-developer-should-know/ At Very Good Ventures (VGV), we’re embracing a new wave of AI-powered integrated development environments (IDEs) like [Claude Code](https://code.claude.com/docs/en/overview), [GitHub Copilot](https://github.com/features/copilot), [Cursor](https://www.cursor.com), and [Windsurf](https://windsurf.com/editor), to boost code writing efficiency while staying true to our rigorous engineering standards and strong best practices. The latest generation of AI agents goes beyond simple suggestions: they can edit code, run tests, verify outputs, and re-edit code if they made a mistake—all without requiring developer interaction. One recent development that’s pushed these agents further is the introduction of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP). Introduced by [Anthropic](https://www.anthropic.com/), the company behind the powerful Claude models, MCP is a standard protocol for AI agents to talk to servers so they can do more things. Each MCP server is like a tool belt you give to an agent, filled with tools that it can use to accomplish tasks. > “MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.”\* The best part? You can use MCP servers with any client that supports them. That includes GitHub Copilot, Cursor, Windsurf, and Claude desktop. They’re easy to install with a quick configuration change, and most run locally with Docker, NPM, or PIP. There are already [dozens of great servers out there](https://github.com/modelcontextprotocol/servers) to interface with many of your favorite services and tools.  Here are a few that are particularly helpful for Dart and Flutter developers: (Last Updated: February 2026) ## [**Dart and Flutter MCP**](https://github.com/dart-lang/ai/tree/main/pkgs/dart_mcp_server) That’s right: The Dart team has been working on an MCP server that allows the agent to run Flutter commands in your workspace. This means it can potentially add packages, run the formatter, query the analyzer, capture screenshots, trigger hot restart, and more! Unfortunately, it’s still a work in progress at the time of writing, so use at your own risk. However, we’re excited to see how this will enhance our workflows! ## [**Git MCP**](https://github.com/modelcontextprotocol/servers/tree/main/src/git) This server enables your agent to read branches, diffs, and statuses, as well as create new branches and staging and committing changes. This means you never have to type git commands in your terminal again! Just request the agent to run them for you with natural language. ## [**GitHub MCP**](https://github.com/github/github-mcp-server) It’s no secret that GitHub hosts many of our favorite repositories, including Flutter itself. With the official GitHub MCP server, you can read and write issues, branches, pull requests, and more—all by just chatting with your AI agent of choice. This is incredibly useful because you can link an entire end-to-end workflow for development.  When combined with the Git server, you can use the agent to read an issue, help implement the changes needed, commit them to a new branch, and open a pull request for those changes, all within the same environment (no context switching and minimal input required). ## [**Atlassian MCP**](https://github.com/sooperset/mcp-atlassian) Many of the projects we support rely on Jira, and this MCP server makes it possible to read, create, and edit Jira tickets using natural language. Similar to how the GitHub server enables issue reading, this integration gives the agent complete access to ticket context, helping it execute changes more effectively. The server also supports reading and writing Confluence pages, allowing agents to pull in relevant documentation or update specs (like HTTP service details) without manual intervention. ## [**Figma MCP (by Framelink)**](https://github.com/GLips/Figma-Context-MCP?tab=readme-ov-file) This server allows the agent to read Figma's designs just from a link to a selection. This can greatly speed up the time to develop UIs in Flutter apps because it will make a decent first pass on the initial run. Admittedly, in our testing, it didn’t always implement pages correctly the first time, and it definitely required us to add some instructions to follow our best practices. Even with that, it can be super helpful getting UI components developed in minutes instead of hours. [\[Video Demo\]](https://youtu.be/6G9yb-LrEqg) ## [**iOS Simulator MCP**](https://github.com/InditexTech/mcp-server-simulator-ios-idb) This server makes it easy to manage your iOS simulators, from creating new servers, capturing screenshots, mocking GPS location, launching deeplinks, and more. This helps keep you in the flow longer because you don’t need to dive deep into a settings menu to change the simulator's location —just ask your agent to do it for you. ## [**Fetch MCP**](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch) This server enables you to fetch information from any URL for agent generation. For example, you could fetch documentation from flutter.dev, a REST API’s online schema, or best practices from [Very Good Engineering](https://engineering.verygood.ventures/). This is a super flexible tool that truly enables your agent to understand and make publicly available content as long as you give it a URL. ## Bonus Servers We couldn’t stop at just 7. Here are a few rapid-fire bonus servers that can be useful. - [Slack](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/slack): Read and write messages from Slack channels. Useful for reading comments left by colleagues or having the agent ask a complicated question from the codebase. - [Notion](https://github.com/makenotion/notion-mcp-server): Search, read, and write documentation and/or tickets stored in Notion. - [Sentry](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/sentry): Read issues from Sentry, including all the context they contain. Super helpful for asking the agent to diagnose issues automatically. - [Google Drive](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/gdrive): Search for and read files stored in Google Drive. Great for referencing documentation automatically. - [GitLab](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/gitlab): Very similar features to the GitHub server, including reading/writing branches, issues, and merge requests. It’s great if you’re already using GitLab. - [Firebase](https://github.com/gannonh/firebase-mcp): Query and write data to Firestore, manage users in Auth, and upload files to storage, all with natural language in your agent. Finally, it’s worth seeing if your favorite tools have created an MCP server. New ones are being created every week. Here’s a [list created by the protocol maintainers](https://github.com/modelcontextprotocol/servers), and here’s a [community maintained list of awesome servers](https://github.com/punkpeye/awesome-mcp-servers). If you don’t see one listed, you can [build one yourself](https://modelcontextprotocol.io/quickstart/server). In fact, there’s a new [experimental package from the Dart team to build them in Dart](https://pub.dev/packages/dart_mcp).  These things change rapidly, so we encourage you to start diving into these MCP Servers if you haven’t already! ### Future-Proof Customer Loyalty: How VGV and Braze Are Building Next-Gen Engagement Source: https://verygood.ventures/blog/future-proof-customer-loyalty-how-vgv-and-braze-are-building-next-gen-engagement/ In today’s crowded digital landscape, brands need more than just touchpoints—they need meaningful connections that create loyalty, drive revenue, and deliver measurable results. That’s why Very Good Ventures (VGV), the world’s leading multi-device design and development agency, is proud to announce a new strategic partnership with [Braze](https://www.braze.com/), the premier customer engagement platform for today’s most ambitious brands. Together, we are redefining what it means to deliver premium, human-centered customer experiences across every screen, channel, and interaction point. **Solving Complex Engagement Challenges with Precision and Scale** As Braze’s premier digital experience partner, VGV is uniquely positioned to help brands implement, optimize, and scale the full potential of Braze’s powerful platform across North America, Europe, and LATAM. Our specialized teams—comprised of elite technical and strategic experts—work at the intersection of AI-driven personalization, cross-platform development, and data-informed decision making. The result: faster time to market, deeper engagement, and experiences that feel intuitive, intelligent, and unforgettable. “Our mission is to help brands deliver seamless, impactful digital experiences, no matter the medium,” said Billie Loewen, EVP, Solutions at Very Good Ventures. “From kiosks to mobile apps, in cars to in-person interactions, we enable brands to create exceptionally connected customer journeys. Partnering with Braze enhances our ability to do just that, combining our technical expertise with their best-in-class customer engagement platform. Together, we’re helping businesses connect with users in more personalized, intelligent, and cost-effective ways—while accelerating time to market.” **A Strategic Advantage in a Noisy Digital World** Executives today know that customer loyalty is earned through moments that feel authentic, timely, and tailored. Yet too often, organizations struggle to activate their engagement technology in ways that truly move the needle. That’s where our partnership makes the difference. VGV brings an agile, highly strategic delivery model to help brands maximize every interaction powered by Braze, across mobile, web, in-app, connected devices, and more. We meet complexity with clarity, ensuring your investment drives growth, improves retention, and increases lifetime value. **Future-Proofing Customer Experience for the Enterprise** As marketing and technology leaders look to future-proof their customer engagement strategies, they need partners who can deliver both technical excellence and strategic foresight. Very Good Ventures and Braze together offer a rare combination of innovation, efficiency, and real-world execution that helps brands scale smarter and connect with customers more meaningfully. If you’re ready to create experiences that not only stand out—but truly stand apart—now is the time to act. Reach out to learn how VGV and Braze can help elevate your brand’s engagement strategy for today, and tomorrow. ### All Roads Lead To AI: Learnings From Google Cloud Next 2025 Source: https://verygood.ventures/blog/all-roads-lead-to-ai-learnings-from-google-cloud-next-2025/ Google Cloud Next 2025 wrapped up on Friday, April 11 in Las Vegas, and as official event sponsors, the Very Good Ventures (VGV) team had front-row seats to the innovation unfolding across the cloud, product development, and enterprise tech ecosystems. ![__wf_reserved_inherit](/assets/images/blog/all-roads-lead-to-ai-learnings-from-google-cloud-next-2025/body-0.jpeg) *Unicorns at GCN 2025* From our booth, to private networking events, to immersive tech demos with partners like Universal Destinations & Experiences and Trackhouse Racing, we connected with forward-thinking teams ready to elevate their digital products. Here are a few of our top takeaways from this year’s event: ## AI is Delivering (and Becoming More Accessible) Unlike past trends that faded into hype cycles, AI is moving fast and breaking real ground. No longer just a talking point, generative AI is showing real impact and becoming part of everyday work. From intelligent document processing to real-time agentic interfaces, the pace of development at Google Cloud means even today’s limitations feel temporary.  Perhaps the most exciting shift is that AI is no longer just for developers. Google’s focus on building no-code and low-code pathways inside tools like Google Workspace is making it possible for non-technical users to design agentic apps. This democratization is accelerating adoption and allowing every team member to contribute to meaningful AI-powered experiences. For developers, this means new expectations—and new opportunities—to build intelligent systems that are intuitive, human-centered, and deeply embedded into business operations. The message from GCN was clear: this isn’t just another wave of tech, it’s a tide change. ## Google’s AI Advantages Google is creating a deeply integrated AI ecosystem that’s increasingly difficult to match. It owns “the chips” ([TPUS](https://cloud.google.com/tpu)), the infrastructure ([Google Cloud](https://cloud.google.com/)), the tools ([Vertex AI](https://cloud.google.com/vertex-ai), [Gemini](https://gemini.google.com/), [Workspace integrations](https://workspace.google.com/)), and the data ecosystem (Search, Gmail, Maps, Photos, Docs). Its true strength lies in the seamless integration across products and platforms.  That being said, we can acknowledge that Google isn’t just enabling AI-powered applications; it’s building an integrated foundation that makes developing them faster, easier, and more secure than ever before. The real competitive edge? Owning the inputs, the platform, and the delivery layer. ## Flutter & Gemini: The Dream Team For AI-Powered Apps Flutter is already the gold standard for building high-performance, multi-device applications from a single codebase. Now, with the Gemini Dart SDK in beta, it’s easier than ever to bring generative AI into those apps. At GCN, we shared how Flutter and Gemini enable teams to: - Build fast, scalable AI experiences across mobile, desktop, web, and beyond - Leverage natural language input, summarization, and content generation - Iterate quickly with hot reload and intelligent interfaces - Keep control of data and user interactions through Google’s privacy-first cloud model AI might be the engine, but UI is still the driver, and Flutter remains the best way to bring intelligence to life on every screen. [Learn more about how you can integrate generative AI into your Flutter app here!](https://verygood.ventures/blog/integrating-gemini-ai-with-flutter/)  ## A VGV Vision: Introducing the Concept of Generative UI in the Agentic Space Generative UI (Gen UI) has the potential to fundamentally reshape how users interact with agentic applications. Today’s agentic experiences are largely text-based: you type a prompt, and the AI replies in text. While effective, it's not always the most human-friendly approach. Sometimes, we just want to see it.  The real power emerges when AI generates dynamic, visual interfaces on the fly based on unique user needs. Instead of asking users to endlessly refine text prompts, the agent intelligently predicts what information is needed next, and generates visual components like sliders, dropdowns, calendars, or even real-time maps to collect it. Can you imagine booking a trip not through a chat thread, but through a branded, fully interactive, AI-composed interface that adapts in real time? That’s Gen UI. Now, picture this functionality built with Flutter. ![__wf_reserved_inherit](/assets/images/blog/all-roads-lead-to-ai-learnings-from-google-cloud-next-2025/body-1.jpeg) *Gen UI in the Agentic Space by VGV* At Very Good Ventures, we're already prototyping this vision with our partners: fully interactive assistants that blend conversational power with dynamic, branded UI experiences. Gen UI toolkits, especially when paired with Flutter, make this not only possible but also scalable, performant, and delightful. We believe Gen UI will define the next era of UX for intelligent digital products. It’s a shift from bots that talk to bots that show, adapt, and respond visually. ## Looking Ahead The digital experiences of the future will be radically different—designed with actual people at the center, not just abstract personas. They’ll be AI-native from day one, with generative capabilities deeply embedded to enhance functionality and creativity.  These experiences will be platform-agnostic, seamlessly running across every screen and device. And most importantly, they’ll be agentic—empowering users to take action, accomplish goals, and engage meaningfully, rather than just consume content passively. At Very Good Ventures, this is the future we’re building: one project, one partnership, and one great digital experience at a time. Whether you're exploring how to bring AI into your apps, evaluating Flutter for your next product, or looking to scale with a modular architecture, [we’d love to help!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) ### From Native Code Limitations To Unified, Scalable Delivery For 40M+ Users Source: https://verygood.ventures/blog/from-native-code-limitations-to-unified-scalable-delivery-for-40m-users/ If your business is the size of Wendy’s, with thousands of physical locations and millions of digital users, your mobile experience isn't just another product—it's the front door of your business. Brian Abston, Senior Director of Digital Technology at The Wendy’s Company, knows this well. With over two decades in software development and platform architecture, Brian helped lead a major shift at Wendy’s: replacing their fragmented mobile infrastructure with a powerful, unified Flutter app. In our recent Build to Succeed podcast episode, he shared a behind-the-scenes look at Wendy's mobile transformation. Here we dive into the journey, lessons, and lasting impact of one of the most ambitious mobile replatforming efforts in the QSR space--and why Flutter became the tool of choice. ## The Hidden Cost of Maintaining Two Native Apps Before the transformation, Wendy’s maintained two separate native codebases for iOS and Android. Over time, those codebases were passed between different development partners, leading to inconsistencies, technical debt, and declining team confidence. The apps lacked automated testing, were difficult to scale, and required double the effort to deliver every new feature. And the limitations weren’t just technical. Teams were split across platforms, QA was duplicative, and product releases were increasingly difficult to synchronize. It wasn’t just slowing down engineering, it was hurting business momentum. ## The Solution: Betting on Flutter Faced with the option to either refactor the legacy apps or start fresh, Brian took a calculated risk. He initiated a six-month proof of concept using Flutter, bringing on two Flutter developers to build a working prototype alongside the existing native teams. The impact was undeniable: what had taken weeks in native was achieved in days. Developer onboarding was faster. Components were reusable. Most importantly, the business saw real progress. > “Building a functioning app is easy. Getting it production ready is hard. Scaling it for 40 million users is another thing entirely,” Brian reflected.  Flutter enabled Wendy’s to combine their mobile teams into one unified group, streamline QA, and drastically improve design consistency through a shared design system. ## Why Flutter Made It Work Flutter brought several key advantages to Wendy’s mobile journey: - One Unified Codebase: Write once, run anywhere for both iOS and Android. - High-Velocity Turnaround: Faster feature development cycles and shorter time to market. - Robust Tooling: Hot reload, easy testing, and high test coverage out of the box. - Strong Partner Ecosystem: Rapidly growing support for integrations and third-party SDKs. - Improved Developer Experience: Easier onboarding, modern language (Dart), and consistent patterns. ## Managing Risk with Feature Flags and Automation Mobile apps at this scale can’t afford downtime or failed releases. Brian's team implemented automated pipelines, feature flagging, and staggered rollouts. This approach enabled faster experimentation while minimizing risk. If an issue appeared in production, they could isolate it without affecting all users. The team also leaned heavily into testing, reaching nearly 100% test coverage, and built a new design system that ensured UI consistency and faster iteration. [Discover how we achieve full code coverage at VGV, and why 100% matters.](https://verygood.ventures/blog/road-to-100-test-coverage/) ## Business Outcomes: From Recovery to Acceleration The impact went beyond engineering. Business stakeholders who had grown skeptical of the app’s reliability saw a clear turnaround. Wendy’s could now deliver promotions, loyalty features, and seasonal campaigns with confidence. The team’s delivery cadence shifted from one release a month to multiple per sprint. They weathered critical integration deadlines, successfully rebranded with minimal effort, and built a platform that could scale well beyond its original intent. >  “We think of all our 6,000 stores as data centers we have to be able to communicate with. There’s some uniqueness to that; it requires a lot of custom development,” Brian explained.  ## What Tech Leaders Should Know Before Rebuilding An App Brian offers simple, seasoned advice for other digital leaders: - Start small: A proof of concept can build the case for change. - Build trust: Results, not promises, are the best persuasion. - Find a champion: Having leadership support is critical. - Focus on fundamentals: Clean architecture, automated testing, and developer experience matter. ## A Model for Modern Mobile Success Wendy’s Flutter journey isn’t just a tech story—it’s a blueprint for digital transformation at scale. It shows what’s possible when a team commits to modern tools, smarter workflows, and a shared vision. From engineering efficiency to business agility, the impact is clear: one codebase, countless wins. Start your Flutter journey with confidence. [Get in touch with our team today!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) ### Why Write Everything Twice? Lessons from Leaders on Successful Flutter Transformations Source: https://verygood.ventures/blog/why-write-everything-twice-lessons-from-leaders-on-successful-flutter-transformations/ For today’s digital-first organizations, delivering exceptional mobile experiences is no longer optional–it’s business-critical. But many engineering leaders are caught in a cycle of inefficiency: managing separate iOS and Android teams, dealing with inconsistent user experiences, battling feature parity lag, and struggling with slow development cycles. As Shorebird founder and former Director of Engineering at Google, [Eric Seidel](https://verygood.ventures/podcasts/harnessing-innovation-to-shape-the-future-of-cross-platform-development/), noted on the [*Build to Succeed*](https://verygood.ventures/podcasts/) podcast, it often feels like you’re writing everything twice–at double the cost, and half the speed. ## Enter: Flutter So, how to address this? Look no further than [Flutter](https://flutter.dev/), Google’s open-source framework.  While many multi-platform solutions have long been pursued for their promise, Flutter has rapidly matured into a powerful framework adopted by major brands like BMW, Nubank, Betterment, Keller Williams, The Wendy’s Company, and the PGA of America. (Check out their stories on [*Build to Succeed*](https://verygood.ventures/podcasts/)!) But adopting Flutter (or any transformative technology) isn't just about swapping code. It's a strategic shift that requires careful planning, leadership buy-in, and a focus on both people and processes. ## The Case for Flutter: Beyond Code Reuse The initial appeal is obvious: write code once, deploy natively to iOS and Android (and potentially Web, Desktop, and Embedded, as [David Chen did at Agtonomy](https://verygood.ventures/podcasts/david-chen-building-autonomous-systems-for-real-world-challenges/)). But the leaders we spoke with highlighted deeper benefits: 1. **Sustainable Developer Productivity:** Flutter's excellent Developer Experience (DX), including hot reload, robust testing frameworks, and top-notch tooling, significantly speeds up [iteration cycles](https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/). It allows teams to focus more on *what* [they're building](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/), not just *how,* across multiple platforms. 2. **Cross-Platform Consistency:** Flutter’s efficiencies extend beyond managing beyond the code. With one codebase, brands can achieve visual and functional [parity across platforms](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/) more easily, [reducing design drift](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/), simplifying QA, and streamlining product and design cycles.  3. **Scalable, Flexible Teams:** Flutter's gentle [learning curve](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/) (especially for those with Java/C#/JS backgrounds) allows broader [team contribution.](https://verygood.ventures/podcasts/david-chen-building-autonomous-systems-for-real-world-challenges/) You're hiring "product engineers," not just siloed iOS/Android specialists, enabling more [flexible staffing](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/) and knowledge sharing. 4. **True Multi-Platform Potential:** Flutter future-proofs your investment by supporting mobile, web, desktop, and even [embedded devices](https://verygood.ventures/podcasts/david-chen-building-autonomous-systems-for-real-world-challenges/), with the [same codebase](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/).  5. **Extended capabilities with Generative AI:** [AI is transforming the way businesses operate](https://verygood.ventures/podcasts/dan-hou-eskridge-driving-high-impact-solutions-with-ai-native-workflows/), innovate, and engage with customers. [When paired with Flutter](https://verygood.ventures/podcasts/teresa-wu-how-ai-and-flutter-are-shaping-the-future-of-development/), it helps engineers accelerate development, test bold ideas faster, and create smarter, [more intuitive user experiences.](https://verygood.ventures/podcasts/kyle-crouse-transforming-ux-the-power-of-design-technology/) ## Navigating the Transformation: Lessons from the Trenches Switching to Flutter isn’t just a framework change–it’s a business transformation. Based on our *Build to Succeed* conversations with engineering executives who’ve led successful transitions, here’s a proven four-phase approach: ### Phase 1: Strategic Alignment - Defining the Why - **Identify the Business Drivers:** Whether it’s speed to market, development cost, or improving user experience, [clearly define](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) what you’re solving before [choosing the solution](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/). - **Listen to Your Team:** Understand the [pain points](https://verygood.ventures/podcasts/patricia-roller-the-intersection-of-design-and-technology/) with the current tech stack from the people building your product. What works? What doesn’t? [Their insights](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) will shape the success of the initiative.  - **Define With Data:** [Evaluate Flutter](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/) (and alternatives) methodically against your specific needs and criteria, and build small Proofs-of-Concept (POCs) to get buy-in. BMW’s rigorous evaluation began with skepticism, but ended in conviction, thanks to data-driven experimentation and measurable success criteria.  - **Pilot With Purpose:** Clearly map out [what “good” looks like](https://verygood.ventures/podcasts/patricia-roller-the-intersection-of-design-and-technology/) by establishing clear, [measurable criteria](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) for the pilot phase.  ### Phase 2: Executive Buy-In – Getting Started Right - **Build Leadership Trust:** Leaders [need to trust](https://verygood.ventures/podcasts/innovation-vs-operation-striking-the-balance/) the engineering team's assessment and provide air cover for the necessary investment and potential short-term slowdowns. As Keller Williams’ [Chris Synan](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/) put it, “Sometimes to go faster, you have to start by going slower.” - **Phased Investment:** To de-risk the decision, propose a limited-scope pilot with clear objectives, resources, and timelines. Betterment’s initial [Flutter project](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) (their signup flow) proved value early, minimizing risk while building internal momentum.   - **Leverage Expertise:** Don't reinvent the wheel. Bring in [external experts](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/) or [internal champions](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) to guide the pilot and establish best practices early.  ### Phase 3: Organizational Adoption – Leading Change at Scale - **Address the Human Element:** Migrations impact identity. It’s important to acknowledge fears, [communicate openly](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/), and create a clear [path forward](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/). - **Invest in Training:** Don't just throw your team into the deep end–plan dedicated training time. Structured training programs and hands-on collaboration (like Betterment’s internal [design system sprint](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/)) foster team buy-in and build foundational skills.   - **Iterative Rollout:** Don't try to switch everyone overnight. [Roll out the Flutter adoption](https://verygood.ventures/podcasts/brian-abston-the-wendys-company-redefining-qsr-tech-with-quality-at-the-core/) team by team, using [early adopters and experts](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) to coach subsequent teams. - **Embrace the Duality:** Accept that for a time, teams will work in [both the old and new stacks](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/). This is necessary to keep [delivering business value during the transition](https://verygood.ventures/podcasts/brian-abston-the-wendys-company-redefining-qsr-tech-with-quality-at-the-core/).  ### Phase 4: Full Realization – Capturing the ROI - **Don't Leave Value on the Table:** Many teams stop short of full migration, leaving legacy systems in place. As [Sam Moore](https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/) said, “everyone wants to go to the party, but nobody wants to stay and clean up.” This adds long-term complexity and undermines ROI.  - **Prioritize Cleanup:** Actively plan and resource the removal of the old native codebases and processes. This is where the long-term gains of efficiency, maintainability, and agility truly materialize. ## Beyond the Code: Culture is Key Nearly every guest on *Build to Succeed* emphasized that [technology alone](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/) doesn't guarantee success. The [underlying culture](https://verygood.ventures/podcasts/innovation-vs-operation-striking-the-balance/) and [shared values](https://verygood.ventures/podcasts/harnessing-innovation-to-shape-the-future-of-cross-platform-development/) paramount, and the most successful companies cultivate: - **Trust & Empowerment:** Teams need to feel safe to experiment, learn, and even fail during a transition. Teams that [feel more trusted](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/) are more likely to [take ownership](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/) and innovate.   - **Alignment Around Outcomes:** Empowered teams that [own their outcomes](https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/) are more likely to drive success. Driven by the PGA of America’s Michael Gyarmathy, the [product triad](https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/) is a perfect example of empowered teams collaborating together for innovation.  - **Customer Obsession:** The ultimate goal is [better products](https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/) for end-users. [Technology is the enabler](https://verygood.ventures/podcasts/teresa-wu-how-ai-and-flutter-are-shaping-the-future-of-development/), not the outcome.  ## Is a Flutter Transformation Right for Your Organization? Flutter is more than just a technical evolution–it’s an [opportunity to rethink](https://verygood.ventures/podcasts/teresa-wu-how-ai-and-flutter-are-shaping-the-future-of-development/) how you build, deliver, and scale mobile and multi-platform experiences. But realizing its full potential requires clear business alignment, a strong change management strategy, and investment in both technology and the team. As the leaders on *Build to Succeed* have shared, organizations that embrace this approach aren’t just reducing costs; they’re accelerating innovation, increasing developer engagement, and delivering better products faster. **Ready to stop writing everything twice?** Now’s the time to lead the transformation. [Contact us](https://verygood.ventures/contact-us/) to learn more. ### My Journey From Barista To Google Developer Expert Source: https://verygood.ventures/blog/my-journey-from-barista-to-google-developer-expert/ I’m thrilled to share that I’ve officially joined the [Google Developer Experts (GDE)](https://developers.google.com/community/experts) program for Flutter and Dart! Becoming a GDE has been a long-time goal of mine, and looking back on the path that led here, I’m both grateful and amazed. At Very Good Ventures (VGV), we’re constantly encouraged to take the next step in our professional growth. And to deliver the best outcomes for our clients, our team actively pursues certifications from leading global organizations. This time, I’d love to share a bit about my own journey. ## Where It All Started: A Coffee Shop In Montana My journey in software development started in an unexpected place: a small coffee shop in Billings, Montana. In 2015, I began studying computer science at Rocky Mountain College, a tiny liberal arts school in a town of just over 100,000 people. The computer science department was small—just three professors—but it gave me my first deep dive into programming. At that time, I worked part-time as a barista at City Brew Coffee. To my surprise, what started as a job to help pay tuition became a huge influence on my career. After transferring to Montana State University, I grew tired of just studying theory and data structures; I wanted to build something real. That summer, I vowed to learn Android development and apply those skills to an outdated process of manually recording daily sales with pen and paper. I built a simple app to automate the report, generate a PDF, and deliver it using email. While the app never officially launched, it caught the attention of the corporate team. That led to an internship, followed by another internship at Foundant Technologies, where I worked in QA. Balancing full-time studies and two internships was tough, but the hands-on learning was invaluable. I’ve learned that when life throws exciting but scary opportunities your way, it’s often best to dive in before they pass you by. Eventually, City Brew offered me the role of Director of Technology, where I led digital transformation efforts across the company, from POS systems and security cameras to launching a new website and loyalty program. One major project was our first-ever mobile app, built using [Flutter](https://verygood.ventures/solution/build-an-app/). ## Discovering Flutter & Finding VGV I first explored Flutter in 2017 as a way to turn my Android-only app into a cross-platform tool. When it was time to build City Brew’s mobile app, Flutter was the clear choice. But leadership wanted "adult supervision," so I searched for help, and ended up finding Very Good Ventures. At the time, VGV was small. I connected with David DeRemer, Founder & CEO at VGV,  and soon began working closely with the team who mentored me through the development process. That partnership helped me level up fast. City Brew’s app became the first client project built with Very Good Start, VGV’s Flutter starter kit. (If you have an app idea, I recommend checking out the [Very Good Start eBook](https://verygood.ventures/whitepapers/ebook-introducing-very-good-start/)—it offers a practical look at how VGV’s proven best practices can help you launch your project fast and on the right foot.) By 2022, after years of balancing school, work, and family life (we had our first child during the pandemic!), I finally graduated with a CS degree. We moved back to Billings, bought a house, and shortly after, I joined VGV full-time as a Senior Software Engineer. Since then, I’ve worked with six amazing companies building world-class Flutter apps. I was later promoted to Engineering Lead, where I mentor a team of six awesome folks. I’ve spoken at events, earned Google Cloud certifications, written blog posts, and contributed to [VGV’s open source software.](https://verygood.ventures/resources/open-source-packages/)  ## Becoming A Google Developer Expert All this brings me to today, becoming a Google Developer Expert. It’s something I’ve dreamed about for years. Sure, I still feel impostor syndrome—many GDEs are developers I’ve long admired—but I also know how much work, support, and persistence went into this. Google Developer Experts (GDEs) are highly skilled developers who have a deep understanding of Google technologies and are passionate about sharing their knowledge with the community. To become a GDE, you must be a recognized expert in your field, have a strong track record of community involvement, and be committed to helping others learn and grow.  You must also be referred by an existing GDE or Google employee, pass a few eligibility checks, and finally meet with a subject matter expert at Google to verify your expertise in your field. Reflecting on the past 10 years, from selling coffee to leading teams at VGV, I recognize the immense amount of mentorship and support I received. I’m deeply grateful to everyone who helped me grow into the leader I am today. ## What’s Next? Over the next 10 years, I hope to use this GDE platform to give back to the community by mentoring, teaching, and helping others grow with Flutter. If there’s anything my story shows, it’s that your path doesn’t have to be traditional to lead somewhere amazing.  I’ve always been inspired by this quote from Steve Jobs: > “You can't connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.” Opportunities often come from unexpected places. With the right support, hard work, and a little trust, you can go further than you ever imagined. Let’s keep learning and building together. If you’re on your own Flutter journey, [I’d love to connect!](https://www.linkedin.com/in/mtwichel/) ### How the PGA of America Innovates with Customer Obsession Source: https://verygood.ventures/blog/how-the-pga-of-america-innovates-with-customer-obsession/ [In the latest episode of Build to Succeed](https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/), Michael Gyarmathy, Staff Software Engineer at the PGA of America, shares his journey from consulting to leading digital product development in the golf industry. With a strong focus on customer obsession, iterative development, and scalable technology, Michael discusses how his team is transforming the golf experience through digital innovation. Michael has leveraged his engineering mindset— and transformed from a non-golfer to a fully dedicated player— to better understand the customer journey. His team’s work on PGA Coach, a platform designed to help teaching professionals modernize their business operations, showcases how digital tools can streamline workflows and enhance customer engagement.  ## Customer Obsession: The Foundation of Great Products Michael emphasizes that building impactful digital experiences starts with deeply understanding the customer. At the PGA of America, his team took the time to study how golf coaches manage their businesses, leading to the development of PGA Coach—a platform that simplifies scheduling, payment collection, and communication. At the same time, Michael draws insightful parallels between software engineering and golf, emphasizing that both demand continuous improvement. Just as golfers refine their swings, engineers must constantly adapt their skills to evolving technologies and user needs. A key element of the team’s success is self-immersion in the customer journey. Even though not all team members are golfers, they actively engage with the sport to better understand their users. By experiencing golf firsthand, attending coaching sessions, and interacting with the PGA members, they gain valuable insights that directly inform product development. This deep connection to the customer experience ensures that their digital solutions genuinely meet the needs of the community they serve. ## Moving from Consulting to Product Ownership Michael’s background in consulting gave him a broad foundation in problem-solving across industries, but he found himself wanting greater ownership over the products he built. This desire led him to the PGA of America, where he could directly shape the digital experiences being created. The PGA of America leverages Flutter to create multi-platform applications that ensure consistent, scalable experiences for users across devices. This approach reduces development redundancy and accelerates feature deployment.  The PGA Coach team also follows an iterative, hypothesis-driven approach to building features: instead of investing months into development, they run experiments and prototypes to validate ideas before committing significant resources. They embrace a [Shape Up-inspired methodology](https://www.productplan.com/glossary/shape-up-method/) that prioritizes outcomes over rigid processes. This approach fosters an empowered engineering culture, where teams self-organize and take ownership of their work. "At the core of a product development squad is a product triad—product manager, designer, and engineer. Together, they identify opportunities, define solutions, and execute on development" - Michael Gyarmathy ### Building a Startup Culture in a Legacy Organization Despite working for a 100+ year-old organization, Michael’s team operates with a startup mentality, using experimentation, rapid iteration, and cross-functional collaboration to drive product innovation. ### Reach Out Whether you're an engineer, product leader, or business strategist, this conversation is packed with valuable takeaways on how to build user-focused, scalable digital products. At Very Good Ventures (VGV), we believe in building technology that empowers businesses. Michael’s insights reinforce the importance of customer-driven development, multi-platform solutions, and lean experimentation—all core principles that we apply to our work. ### Creating Seamless Multi-Device Experiences Source: https://verygood.ventures/blog/creating-seamless-multi-device-experiences/ At the recent [GDS Digital Innovation Summit](https://gdsgroup.com/events/physical-summit/digital-innovation-na-mar-25/) in Boston, I had the opportunity to discuss a critical challenge in modern digital product development: creating seamless, adaptive multi-device experiences.  As the digital landscape expands, users interact with brands across a growing number of devices—from desktops and mobile phones to wearables, kiosks, and even smart home appliances. Delivering cohesive and frictionless experiences across these touchpoints is no longer a luxury, but a necessity. Here are the top insights I shared to help brands create the necessary experiences their customers now demand. {% video url="https://www.youtube.com/watch?v=0bTYXGy9SZU" title="Video" %} ## Meet Your Users Where They Are Today’s consumers expect real-time access to services and products that seamlessly integrate into their lives. They switch between devices effortlessly, whether they’re booking a flight on a desktop while in the office, checking in on their mobile device as they get ready in the morning, or interacting with digital signage at the airport. Companies that fail to meet users where they are risk losing engagement, loyalty, and, ultimately, revenue.  Moreover, multi-device experiences are becoming increasingly important as digital access expands. U.S. households own 60% more connected devices today than they did five years ago.^1^  The blurring lines between physical and digital experiences—often referred to as "endpoint sprawl"—make it imperative for businesses to adopt a strategic, multi-device approach. ## Why Most Companies Struggle Despite this clear demand, many companies struggle to deliver unified digital experiences. The main reason? A reliance on outdated development methodologies and tools, resulting in organizational complexity and slower time to market. Your users feel the pain of your strategic decisions with divided teams working across web, iOS, Android, and other platforms (often operating independently), leading to inconsistent user experiences and inefficient development cycles. One striking example from the summit highlighted a company managing 17 different agile teams just to deliver a single digital experience. This level of fragmentation slows innovation and creates bottlenecks in decision-making. Product teams are left with a difficult trade-off: either launch incomplete experiences quickly, or wait multiple cycles to deliver fully integrated solutions. And what's more, they often miss key learning opportunities either way. ## Challenges With the Current Delivery Approach While platform specialization may be needed to deliver certain experiences, it comes with significant challenges: - Inconsistent user experiences across device types. - Increased team complexity, leading to higher coordination costs and slower innovation cycles. - Higher development costs due to operating multiple teams - Difficult scope decisions as product teams struggle to balance cost and ROI. ## The Solution: Adaptive Platforms & Generative AI To address these challenges, I introduced a new paradigm: adaptive platforms powered by cross-platform development frameworks and generative AI.  1. Cross-Platform Development: Instead of maintaining separate codebases for each device, modern frameworks like Flutter allow teams to write once and deploy across multiple devices. This approach reduces development costs, speeds up iteration cycles, and ensures design consistency. I strongly recommend VGV’s ebook [Enterprise Tech Teams and Flutter](https://verygood.ventures/whitepapers/ebook-enterprise-tech-teams-and-flutter/) to learn more about how Flutter helps high-performing apps and organizations. 2. Generative AI for Personalization: AI-driven data insights can dynamically tailor experiences based on user context. Imagine an e-commerce app that adapts its interface depending on whether a user is on a smartphone, smart TV, or voice assistant—automatically optimizing the experience for each modality. Pretty cool, right? 3. Real-Time Data Synchronization: Businesses must ensure that data flows seamlessly across devices. For instance, a user pausing a video on one device should be able to resume on another instantly, mirroring best practices seen in platforms like Netflix. ## Emerging Best Practices for Multi-Device Delivery With the right frameworks in place, brands can see incredible benefits: - Use a single codebase to eliminate the need for platform-specific teams - Leverage features like hot reload, pre-built widgets, and automated testing to accelerate development - Focus on declarative UI programming and efficient state management to improve the developer experience - Strategically use plugins and packages to create near-native experiences And that’s just the beginning. By switching to a multi-device model, brands see: - Faster time to market (up to 62%!)^2^ - Scalability across product features and organizations - Reduced turnover from happier development teams To discover how multi-platform frameworks like Flutter can accelerate digital product delivery and drive business impact, be sure to check out [The Business Value of Flutter whitepaper here](https://verygood.ventures/whitepapers/business-value-of-flutter/). ## A Shift in Organizational Thinking To succeed in a multi-device world, companies must restructure their product and engineering teams. Instead of organizing teams by device type (iOS, Android, web), businesses should focus on the user journey. Teams should be aligned around solving user problems rather than just delivering software for a specific platform. [Learn more about How Flutter Can Transform Engineering Team Culture in this blog post.](https://verygood.ventures/blog/flutter-can-transform-engineering-team-culture/)  Additionally, companies need to slim down their tech stacks and streamline customer data management. Many organizations collect vast amounts of data but fail to leverage device-specific insights. Incorporating contextual device usage into customer datasets allows businesses to deliver truly personalized experiences. ## Rapid Prototyping & Experimentation Another crucial takeaway from my talk was the importance of rapid prototyping and iterative testing. From VGV’s perspective, organizations should: - Maintain a hardware feature matrix to document device capabilities and constraints. - Invest in a concept lab with real-world devices and environments enables businesses to: - Test across actual devices instead of relying solely on emulators. - Enhance digital product usability. - Validate experiences in context to ensure seamless interactions. - Deploy AI-driven analytics to detect pain points and improve UI/UX based on user behavior. By adopting these strategies, companies can move beyond static, one-size-fits-all designs and create experiences that adapt dynamically to user needs. ## The Future of Digital Engagement Delivering adaptive, multi-device experiences is more than a technological challenge—it’s a strategic imperative. Businesses that prioritize seamless, integrated digital experiences will not only enhance user satisfaction but also gain a competitive edge in an increasingly fragmented digital landscape. As I concluded in my talk: “Consumers don’t care about our internal complexities. They just want seamless experiences—anytime, anywhere.” The question now is: Are you ready to build for a multi-device world? [Contact our team to get started!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411)  If you have ideas or feedback about this topic, I’d love to connect with you—[feel free to reach out!](https://www.linkedin.com/in/tonyfigola/) ### Top Open Source Packages Every Flutter Developer Should Know in 2025 Source: https://verygood.ventures/blog/top-open-source-packages-every-flutter-developer-should-know-in-2025/ Flutter continues to be a go-to framework for building multi-platform applications, thanks to its extensive library of open-source packages. [In 2025, the Flutter ecosystem continues to thrive](https://star-history.com/#flutter/flutter&Date), with developers leveraging community-driven tools to save time and enhance functionality. See the chart below: ![Flutter GitHub star history chart showing continued growth through 2025](/assets/images/blog/top-open-source-packages-every-flutter-developer-should-know-in-2025/body-0.png) If you're looking for the best tools for state management, databases, animations, API handling, and [our documented Flutter engineering practices](https://engineering.verygood.ventures/), VGV's expert engineering team has put together a list of essential packages to help you build high-performance, scalable Flutter applications. Whether you’re developing a simple app or a complex enterprise solution, integrating these libraries can streamline your workflow and enhance efficiency.  Take some notes:  ## State Management & Business Logic This is vital for any Flutter application that intends to deliver a seamless user experience and clean, maintainable code. Flutter\_bloc and Riverpod stand out as powerful state management tools, offering both flexibility and scalability.  ### 1. Flutter\_bloc [Flutter_bloc](https://pub.dev/packages/flutter_bloc) provides a predictable state management solution using the BLoC (Business Logic Component) pattern. This approach ensures a strong separation between UI and business logic, allowing teams to work more efficiently.  With extensive documentation, a focus on testability, and a strong community, flutter\_bloc is ideal for developers looking to implement a well-structured, maintainable state management strategy for their applications. Learn more about [why at VGV we use Bloc here!](https://verygood.ventures/blog/why-we-use-flutter-bloc/) ### 2. Riverpod [Riverpod](https://pub.dev/packages/riverpod) is a modern “reactive caching and data-binding framework” designed to improve dependency injection, state management, caching, and overcome limitations in older solutions like Provider (Riverpod’s predecessor). Developers appreciate Riverpod for its ability to structure state management in a compile-time safe manner, making it easier to maintain complex applications over time. Some of its advantages include: - It’s fully testable - App state is decoupled from the widget tree - Providers and UI widgets can “watch” other providers and react when their values change - It’s scalable for large projects ## Networking ### 3. Dio Efficient API handling is a key component of any modern application. [Dio](https://pub.dev/packages/dio) is a powerful HTTP client for Dart, making API calls easier and more efficient with robust networking features. It supports interceptors, global configuration, and file uploading, enabling developers to streamline their networking logic. Dio also provides built-in support for request cancellation and retries, ensuring a reliable experience for users even in unstable network conditions. Compatible with RESTful APIs, Dio is a go-to solution for managing network requests in Flutter applications. ## Database & Local Storage For applications that require persistent data storage, choosing the right database solution is critical. Drift and Isar are two high-performance NoSQL databases optimized for Flutter, each catering to different development needs. ### 4. Drift [Drift](https://pub.dev/packages/drift) is a powerful and flexible reactive persistence library for Flutter and Dart, built on top of SQLite. It allows developers to write type-safe SQL queries while leveraging Dart’s declarative power. With its automatic query generation, efficient caching, and support for migrations, Drift is an excellent choice for applications that require structured data storage with SQL capabilities. ### 5. Isar [Isar](https://pub.dev/packages/isar) is another high-performance NoSQL database that’s optimized for Flutter. It offers superfast read and write operations, making it ideal for applications requiring high-speed data processing. With support for schema migration and the ability to work across mobile and desktop apps, Isar is a great option for developers looking for a scalable and efficient database solution. ## Animations & UI Enhancements Creating smooth, engaging animations enhances the user experience, and two standout tools for animations in Flutter are Rive and Lottie. ### 6. Rive [Rive](https://pub.dev/packages/rive) allows developers to create interactive, real-time animations that are seamlessly integrated into Flutter applications. It enables rich, dynamic UI animations with support for state machines, ensuring fluid transitions and animations that feel natural. The efficient rendering engine in Rive is optimized for Flutter, allowing animations to run smoothly without compromising performance. ### 7. Lottie [Lottie](https://pub.dev/packages/lottie) is another essential tool for animation in Flutter applications. It enables high-quality vector animations, reducing file sizes compared to traditional GIFs while maintaining smooth playback. With support for Adobe After Effects animations, Lottie allows developers to create visually stunning motion graphics that enhance user engagement. ## Linting & Validation Maintaining clean, high-quality code is essential for any development team. Very Good Analysis, Very Good Workflows, and Flutter Lint are tools that help enforce best practices and streamline development. ### 8. Very Good Analysis [Very Good Analysis](https://pub.dev/packages/very_good_analysis) is a comprehensive linting package that enforces best coding practices for Flutter and Dart projects. It ensures that teams maintain a consistent and high-quality codebase by catching potential issues early in the development process. ### 9. Very Good Workflows [Very Good Workflows](https://workflows.vgv.dev/) provides a set of CI/CD workflows optimized for Flutter projects, automating tasks such as testing and deployment. By integrating these workflows, teams can improve efficiency and maintain a smooth development pipeline. ### 10. Flutter Lint [Flutter Lint](https://pub.dev/packages/flutter_lints) is another valuable tool that helps developers maintain consistent, high-quality code by enforcing linting rules specific to Dart and Flutter projects. These tools collectively contribute to a cleaner and more efficient development experience. ## Dart in the Backend For developers looking to expand their Flutter applications into full-stack solutions, DartFrog and Serverpod provide powerful backend capabilities. ### 11. DartFrog [DartFrog](https://pub.dev/packages/dart_frog) enables developers to build robust backend applications using Dart, simplifying API development and backend logic. ### 12. Serverpod [Serverpod](https://pub.dev/packages/serverpod), on the other hand, offers a structured backend framework with built-in support for authentication, database integration, and more. Together, these tools empower Flutter developers to create seamless, full-stack applications using the Dart ecosystem. ## Localization & Accessibility ### 13. Intl To make applications accessible to a global audience, proper internationalization and localization are essential. The [Intl](https://pub.dev/packages/intl) package provides developers with the tools needed to format dates, numbers, and currencies based on user locale. Supporting translations for multiple languages, Intl ensures that applications can cater to diverse markets without compromising usability. ## Testing ### 14. Patrol [Patrol](https://patrol.leancode.co/) is an open-source framework for automated UI testing built specifically for Flutter apps. It's the only tool that lets developers write end-to-end tests in pure Dart while also interacting with native elements that Flutter normally can’t reach. With custom finder system, hot-restart support, and device-farm compatibility, Patrol makes UI testing more reliable and practical for real apps. The framework has been created and maintained by [LeanCode](https://leancode.co/) since 2022, with new features and improvements released regularly. ### 15. Mocktail Ensuring software reliability through effective testing is crucial for delivering a polished product. [Mocktail](https://pub.dev/packages/mocktail) simplifies mocking in unit tests, providing an easy-to-use API for creating test doubles. With no manual setup required for mock objects, Mocktail integrates seamlessly with Flutter's testing framework, improving test reliability and making the development process more robust. ## Code Generation For developers looking to streamline their workflow, Freezed and Build Runner offer powerful code generation capabilities for immutable data classes and efficient state management. ### 16. Freezed [Freezed](https://pub.dev/packages/freezed) simplifies immutable data modeling, reducing boilerplate code and improving maintainability. By generating structured and predictable data models, Freezed enhances the performance and reliability of Flutter applications. ### 17. Build Runner [Build Runner](https://pub.dev/packages/build_runner) complements Freezed by automating the generation of code, ensuring that data models remain consistent and easy to manage. ## Learn more These open-source packages are indispensable tools for Flutter developers in 2025, providing solutions that enhance state management, data handling, UI, performance, and development workflows. At VGV, we’re committed to supporting Flutter developers at every stage—from launch to scale—through our open-source software and best practice resources. We’re passionate about sharing our knowledge, and we hope our engineering insights and experiences help elevate your Flutter projects. ### How Businesses Can Successfully Integrate AI Source: https://verygood.ventures/blog/how-businesses-can-successfully-integrate-ai/ AI is transforming the way businesses operate, innovate, and engage with customers. But how should companies approach AI to maximize its impact? [In our latest episode of Build to Succeed](https://verygood.ventures/podcasts/dan-hou-eskridge-driving-high-impact-solutions-with-ai-native-workflows/), Dan Hou, Founder and Partner at Eskridge, shares his expertise in integrating AI into operations and digital products.  With a career spanning Microsoft, Motorola, Huge, and Amazon, Dan has spent years identifying technological paradigm shifts. Now, he helps mid-market businesses deploy AI strategies that drive high-impact results. From streamlining internal processes to transforming user experiences, AI adoption varies across businesses. Dan explores where AI is driving impact, the challenges of adoption, and why companies must embrace it now to stay ahead. And we want to spill all the details for you. ### Where Should Businesses Start?  When it comes to AI adoption, businesses typically focus on two key areas: internal efficiency and product innovation. Internally, AI is streamlining workflows, automating repetitive tasks, and enabling data-driven decision making. On the product side, AI is revolutionizing search, dynamic personalization, and intelligent automation to improve user experience. However, not all AI applications are equal—companies must prioritize high-impact use cases that meaningfully affect cost, revenue, or productivity. Dan highlights that one of the biggest mistakes companies make is over-optimizing before they even get started. Many hesitate due to uncertainty, but he emphasizes that waiting too long puts companies at risk of falling behind.  ## How AI is Reshaping Digital Products and User Experience While AI is already enhancing backend efficiency, its impact on digital products and UX is just beginning. Companies are exploring how AI can create seamless, intuitive, and more personalized user experiences. However, one common misconception is that chat interfaces will be the future of UX. Dan argues that chat-based interactions often create more friction than value. Users struggle with open-ended prompts, leading to confusion about what to ask or expect. He emphasizes: “If you have to take a course to learn how to use your product, you’ve done something horribly wrong.”  Instead, AI should enhance, not replace, traditional UI elements. A hybrid approach—combining interactive UI with AI-driven personalization—offers the best of both worlds. For example, AI-powered search is already reshaping e-commerce by helping users filter and find products in a more intuitive way. Beyond interfaces, multimodal AI is set to transform mobile experiences. AI’s ability to process real-time images, voice, and context could revolutionize brand interactions, making digital experiences more dynamic and personalized. ## Breaking Through AI Adoption Barriers Despite AI’s potential, businesses face governance, legal concerns, and internal resistance when trying to implement AI. Large enterprises often introduce AI review committees that slow down progress due to compliance concerns. To move forward, Dan suggests that companies: - Educate leadership on AI capabilities to overcome hesitation. - Prioritize high-impact areas instead of attempting AI everywhere. - Adopt an experimental mindset: start small, iterate fast. - Balance risk with opportunity, understanding that AI’s governance will evolve over time. AI adoption is as much a cultural shift as a technological one. Companies that embrace AI today will be better positioned for long-term success. ## A Roadmap for AI Success For companies looking to integrate AI effectively, Dan recommends a 3-6 month action plan: 1. Educate leadership and teams through workshops or AI-focused training. 2. Identify key problem areas where AI can provide measurable benefits. 3. Set up a dedicated AI team to experiment and iterate. 4. Test AI-powered workflows without disrupting existing operations. 5. Scale AI adoption strategically based on early success. Dan reflects that the key is to start now, even if the first implementation isn’t perfect. AI’s capabilities will continue to evolve, and companies that take action today will gain a competitive advantage tomorrow. He adds: “There’s a whole new world of AI optimization that has barely scratched the surface, that is like the new search engine optimization.” ## The Future of AI in Business Dan reinforces that the conception of AI is no longer an emerging trend; it’s a fundamental shift in how businesses operate. Whether it’s enhancing efficiency, improving UX, or unlocking new revenue streams, AI offers companies a competitive edge. AI success isn’t about perfection—it’s about iteration. Businesses that start small, experiment, and refine their AI strategy will be best positioned for the future. Reach out Very Good Ventures transforms digital strategy into action, helping businesses harness AI to drive real impact. Our expert team specializes in AI-powered innovation, from crafting visionary roadmaps to deploying production-ready solutions. [Contact us now](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) to learn how we can help your business innovate, scale, and stay ahead with AI-driven solutions. ### VGV at Google Cloud Next 2025: Powering Multi-Platform Digital Experiences Source: https://verygood.ventures/blog/vgv-at-google-cloud-next-2025-powering-multi-platform-digital-experiences/ We’re heading back to Las Vegas for [Google Cloud Next 2025](https://cloud.withgoogle.com/next/25) from April 9-11! Very Good Ventures (VGV) is an official event sponsor, and we’re excited to connect with innovative companies enhancing their product development capabilities with multi-platform, AI-powered, and scalable technology. We invite everyone to join us at the booth #3460 for in-depth discussions on Flutter, UI for AI, and cutting-edge enterprise solutions. Grab exclusive swag and experience a unique demo from VGV’s expert team showcasing our work with Universal Destinations & Experiences! ## VGV at Google Cloud Next Google Cloud Next is the premier destination event for technical leaders, partners, and customers seeking to enhance their cloud infrastructure, optimize product strategies, and implement cost-effective, scalable solutions. As a Google Cloud Partner, VGV is positioned to help businesses build seamless, future-proof digital experiences.  Over the years, we’ve pioneered multi-platform engineering, leading the way as the global leader in Flutter application development. Our expertise enables companies to build adaptive, unified experiences across mobile, desktop, web, and embedded devices—all from a single codebase. Exceptional developer productivity, implementation efficiency, and scalable quality-centric engineering standards enable companies to ship faster and safer to create exponential business value. At GCN 2025, we will be sharing what’s next for multi-device experiences, connecting with industry leaders, and showcasing how we’re building the future of digital applications.  Our booth will be packed with exciting surprises and interactive experiences for everyone at the event to enjoy. Here’s a sneak peek of what to expect:   ### Innovative UI for AI & User-Centered Design Flutter is a highly efficient solution for creating delightful user experiences for AI on all devices—and we’re showcasing how we integrate UI for AI into modern applications. See real-world applications of AI-powered design systems, intelligent interfaces, and next-gen interactions that streamline developer workflows and enhance user engagement. ### Having fun with Universal Destinations & Experiences  Want to feel the rush of real adrenaline merged with cool tech innovations? We can’t spill all the details, but we’ll be unveiling some of our most exciting -and work with Universal Destinations & Experiences. Live tech demos will be displayed for you to discover how Flutter powers immersive experiences across mobile, web, kiosk, and beyond while equipping companies with development velocity that is a competitive advantage. Stay tuned for more! ## Exclusive Networking Event VGV is also hosting an invite-only networking event the night before Google Cloud Next kicks off! You can expect an evening of thought-provoking conversations, industry insights, and high-impact networking with: - Industry leaders driving the next wave of digital transformation - Google Cloud teams shaping the future of cloud-native applications - Visionaries and key contributors to the Flutter, Firebase, and Cloud communities - Prospective partners and clients looking to innovate with multi-platform and AI-driven technology We’ll also be showcasing some of our most exciting work in collaboration with some of our biggest clients, revealing how Flutter is enabling immersive experiences across mobile, web, kiosks, and beyond. If you're interested in participating, [drop your contact details here](https://verygood.ventures/contact-us/), and we'll reach out to you shortly. Seats are limited, so RSVP early to secure your spot! ## Let’s Build the Future of Digital Experiences Together At VGV, we’re not just developing apps—we’re crafting the next generation of digital experiences that seamlessly scale across all devices and platforms to create enduring business value. If you’re looking to integrate AI experiences into your digital products or expand your mobile and multi-platform capabilities, we look forward to speaking with you. Meet us at Booth #3460, grab some magical swag, and [schedule time to talk with our team](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411). I’d love to connect and discuss multi-platform engineering, Flutter’s limitless potential, unified experience strategy, and our outcome-driven, quality-focused approach. [Let’s set up a time to chat!](https://www.linkedin.com/in/davidderemer/)  See you in Las Vegas at Google Cloud Next 2025! ### Building for 100M Users: Nubank’s Flutter Journey Source: https://verygood.ventures/blog/building-for-100m-users-nubanks-flutter-journey/ In the [latest episode of our Build to Succeed podcast, host David DeRemer talks with Thiago Ghisi, Director of Engineering at Nubank.](https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/) With nearly two decades of experience across industry giants like Apple, American Express, and ThoughtWorks, Thiago brings a wealth of knowledge on mobile engineering, leadership, and innovation. Here are some of our favorite moments from their conversation. ## Navigating From Mobile Engineering to Leadership Thiago started by sharing his career evolution, from starting with Java and JavaME, working across web and QA, to eventually focusing on mobile engineering. His journey has been shaped by his belief in being a “glue person”—someone who fills gaps in teams and connects different functions to drive progress. He emphasizes the importance of full-stack engineering, advocating for a well-rounded skill set that balances specialization with broad problem-solving abilities. ### The Shift to Flutter at Nubank One of the major themes of the conversation in this episode is Nubank’s adoption of Flutter, a decision driven by the need to scale mobile development effectively. Before adopting Flutter, Nubank faced increasing bottlenecks, with native development slowing down innovation and product delivery. By transitioning to Flutter, the team was able to: - Reduce code duplication across platforms - Improve developer experience and onboarding - Scale their engineering teams more efficiently - Maintain feature parity without overburdening the mobile team Thiago explains that after evaluating various options, Flutter emerged as the optimal choice due to its superior developer experience, performance, and seamless integration with native code. "Imagine if every time you needed to build a new screen, you had to touch mobile, backend, database, and infrastructure. That’s not scalable. What if instead, you had high-level platforms that abstract away all the complexity?" he reflected.   Read more about [The Enterprise Advantages of Flutter Development and how companies are reaching quality while delivering faster in this whitepaper.](https://verygood.ventures/whitepapers/building-enterprise-apps-with-flutter/)  ### Revolutionizing Mobile Development with Server-Driven UI Beyond Flutter, Thiago delves into Nubank’s journey with Server-Driven UI (SDUI), a cutting-edge approach that allows mobile applications to be dynamically driven by the backend. This methodology enables: - Dynamic UI updates without requiring app releases - More modular and scalable mobile architecture - Continuous delivery and faster iteration cycles - More efficient governance of mobile experiences Thiago explains that while SDUI does come with challenges—including managing large payloads, maintaining UI consistency, and balancing backend and frontend processing–it’s ultimately been a game changer for Nubank, allowing them to iterate quickly and serve over 100 million customers seamlessly. At Nubank, they’ve played extensively with SDUI, achieving a very successful implementation over several years. The process wasn’t built overnight—it required multiple iterations, leveraging Flutter, Material Design, and a robust SDK to create a decoupled system that allows everything to be sent over the network. They even developed DSLs in Clojure on the backend, using Protobuf for structured communication between the backend and client, ensuring strong contracts and governance. Thiago explains that one of the key challenges of Server-Driven UI is the additional layer of runtime processing. Unlike traditional UI frameworks that optimize at build time, SDUI must handle dynamic permutations of components at runtime, often requiring additional optimizations. Nubank experimented with different styles of SDUI, including: - Checking for updates at startup and loading new versions in the background - Full interpreter-based approaches, where every interaction is fetched from the server dynamically - High-level abstraction platforms to streamline product development and reduce engineering overhead The business case for SDUI is that it enables continuous delivery, removes dependencies on app store release cycles, allows for rapid bug fixes, and accelerates product iteration without requiring users to constantly download new app versions. With a 2,000+ engineer organization, Nubank needed a solution that empowered teams to build and iterate autonomously, reducing dependencies between mobile and backend engineering teams. ### Wrapping Up Thiago’s insights reinforce the idea that technology choices aren’t just about frameworks and languages—they’re about empowering teams, optimizing workflows, and enabling faster, more innovative product development. While Nubank is redefining mobile banking with cutting-edge engineering practices, their engineering-first approach positions them at the forefront of mobile fintech innovation. Interested in learning more about Very Good Ventures and how we can help companies build world-class digital experiences? ### When to Choose Native Development: Key App Strategy Insights Source: https://verygood.ventures/blog/when-to-choose-native-development-key-app-strategy-insights/ At Very Good Ventures (VGV), we believe that selecting the right technology for a mobile application is about finding the best fit for each team’s unique needs, capabilities, and long-term goals. While multi-platform frameworks like Flutter provide significant advantages in efficiency and productivity, there are still cases where native development can be the right path forward to fully leverage platform-specific features and optimizations. For us, the focus isn’t on choosing one approach over the other but on understanding when and how each technology best supports specific business and technical needs. The most successful digital products are built on tailored solutions, not strict technological preferences. As our offerings and clientele have expanded over the years, we recognized the need to provide comprehensive, end-to-end solutions tailored to each project’s unique requirements. To meet this demand, our team’s expertise also excels in native development services built on the same [publicly documented engineering standards](https://engineering.verygood.ventures/) that have made VGV an industry leader in multi-platform development. Whether native or multi-platform, our goal remains the same: high performance, delightful user experience, and long-term scalability.  ## Growing Our Native Capabilities Last year, I joined VGV at an exciting time of growth, as we expanded our service offerings to bring the "VGV Way" to even more areas of our clients' technology ecosystems. Since then, our native mobile capabilities have continued to grow, and we've worked on exciting projects that showcase our deep expertise in the field.  [Learn more about why VGV is expanding into native app development in this blog post.](https://verygood.ventures/blog/vgv-expands-technology-leadership-with-top-android-expert-toffer-jakobsen/) If your app requires a lot of deep platform integrations, has specific hardware performance requirements, or must reflect the latest native look and feel guidelines, a native approach may be worth the additional investment in order to deliver the experience you want for your users. In this article, I’ll highlight some common scenarios where you might want to consider native development—and yes, at VGV, we’ve also got you covered there.  ### 1. Direct Access to Platform APIs One of the most significant advantages of native development is immediate and unrestricted access to all native platform APIs. When Apple or Google releases new system features or optimizations, native applications can instantly leverage them without waiting for third-party framework support. Examples of applications that can benefit from this include: - Apps that require deep integration with device hardware, such as cameras, GPS, biometrics, or augmented reality (ARKit/ARCore). - Specialized platform experiences, like Android Launchers and WatchOS applications. ### 2. In-App Performance Optimizations In many cases, multi-platform solutions are able to match the in-app performance of a native application. However, for apps that require high-performance rendering, complex animations, or real-time data processing, you might start to see a difference. Building natively allows for platform-specific optimizations that can help minimize latency and maximize responsiveness—crucial for experiences such as: - Video editing or AR applications with intensive rendering needs. - Apps that handle large amounts of real-time data or computational processing. - Low-latency games. ### 3. A Native Look & Feel for Each Platform If your app needs to perfectly match the UI standards of the target platform, native development allows you to use the full suite of platform-specific UI components to create an engaging and integrated experience. This ensures that your app follows the latest design patterns, interactions, and behaviors expected by users on each OS. This is important for: - Apps that must behave and feel like a natural extension of iOS or Android - Ensuring platform consistency across all devices and versions of the platform-specific ecosystem (i.e., be consistent across all Apple devices). ### 4. Devices with Limited Resources Not all devices are created equal. Wearables, smart TVs, and embedded systems often have limited CPU, RAM, and storage, making efficiency a top priority. Native development allows you to: - Minimize the app size for faster downloads and better battery life. - Optimize memory and CPU usage to prevent crashes or slowdowns.  This is highly relevant for: - Wearables, IoT devices, or any application running on constrained hardware. - Apps targeting regions with expensive mobile data costs, where download size impacts adoption. ### 5. Legacy Codebases & Existing Infrastructure For teams with large existing native codebases, transitioning to a multi-platform approach may not always be feasible due to timing, cost (training, tooling changes, etc.), or compatibility concerns. In these cases, native development ensures continuity and stability while still allowing for future hybrid or cross-platform integrations. Native is often considered for the following scenarios: - Businesses with significant investments in native technology. - Apps requiring gradual modernization without a complete rewrite. - When there are significant amounts of pre-existing platform-specific code and libraries that must be re-used - Apps requires usage of third-party services that only have native SDKs However, even in these cases, we strongly recommend conducting an assessment to identify opportunities for embracing Flutter in the future. ## Let’s Find the Right Strategy for Your Business At VGV, we apply the same engineering principles, best practices, and performance-driven approach to native mobile development as we do in our industry-leading multiplatform work. Whether you’re building a fully native app, optimizing an existing codebase, or exploring a hybrid approach, our team ensures you get the best possible performance, scalability, and maintainability—regardless of the platform. If you have any questions or are looking for advice to help make the right technology choice, [I’d love to connect and discuss!](https://www.linkedin.com/in/christoffer-jakobsen/) ### Unicorn Passport: Celebrating Journeys & Achievements at VGV Source: https://verygood.ventures/blog/unicorn-passport-celebrating-journeys-achievements-at-vgv/ At Very Good Ventures (VGV), we take pride in being more than just a workplace: we’re a global community of innovators, creators, and problem-solvers. With team members from all over the world, we embrace diversity, talent, and the unique stories each unicorn brings to our team. To celebrate this journey, we’ve introduced the Unicorn Passport—a fun and meaningful way to capture the experiences that make working at VGV truly special. ## What is the Unicorn Passport? We wanted to create an engaging way to both document our teammates’ journeys at VGV and celebrate their milestones.  Enter: The Very Good Passport.  From groundbreaking projects we've worked on, to exciting global travels to meet clients and teammates, to key career milestones, the passport memorializes each achievement with our designer-created stickers. The Unicorn Passport follows each employee's journey, ensuring that every achievement—big and small!—is recognized and remembered. ![__wf_reserved_inherit](/assets/images/blog/unicorn-passport-celebrating-journeys-achievements-at-vgv/body-0.png) ## How It Works Every VGV employee receives a passport and stickers, delivered directly to their door. Sticker designs include: - Countries our unicorns have visited or worked with teammates from - Milestones to celebrate anniversaries - Projects completed (both for clients and internal initiatives!) New stickers will be introduced regularly to highlight additional accomplishments, memorable experiences, and significant moments. ![__wf_reserved_inherit](/assets/images/blog/unicorn-passport-celebrating-journeys-achievements-at-vgv/body-1.png) *Some very cool stickers and stamps* ## Worldwide Adventurers  At VGV, we believe that the best teams are built with the best people—and those people come from all over the world. We’re proud to have team members in 21 states and 11 countries across North America, LATAM, and the EU. Given our global roots, we love to travel, and whenever possible we organize offsite meetings and in-person work events to strengthen our bonds beyond the screen.  Now, when teammates meet in person, they can sign each other’s passports, exchange stickers, and create lasting memories along their VGV journey. ## Join the Magic We’re dedicated to creating an environment where our team members feel valued, inspired, and part of something truly special. The Unicorn Passport is a small but powerful way to bring our community closer together and make every milestone memorable. Our People team is always on the lookout for amazing talent, so keep an eye on our [careers page](https://verygood.ventures/careers/) for exciting opportunities to join VGV! ### Simple Solutions for Complex Problems at Scale–A Must-Listen! Source: https://verygood.ventures/blog/simple-solutions-for-complex-problems-at-scale---a-must-listen/ For the latest episode of our podcast, Build to Succeed, we had the privilege of sitting down with [Jorge Coca, Head of Engineering at Very Good Ventures](https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/).  Jorge’s career spans over a decade of innovative contributions to the Flutter ecosystem and mobile engineering. His journey from Spain to the U.S., his time at BMW, and his leadership in engineering have all shaped his perspective on scalable and user-friendly engineering practices. Here's just a sampling of our favorite moments in the episode. ## Cooking, Music, and Coding? It’s All Related If you know Jorge, his humor and storytelling skills are unmistakable. It was no surprise, then, when he began the conversation by drawing intriguing parallels between cooking, music, and coding. He likened software development to jazz improvisation, where a strong foundation enables creativity and innovation. “Once you understand the foundations, you can go and explore uncharted territories and try to innovate. In a way, those activities—though sometimes stressful—can actually be very relaxing”, he explained.  ## The Beginning of a Long Journey  Jorge’s path to the U.S. began with the Erasmus program, an initiative that allows European students to study abroad. What started as a one-year academic opportunity turned into a transformative career move. He landed his first job in an advertising agency, where he delved into Ruby on Rails and web development, eventually paving his way into mobile engineering. Later, at BMW, Jorge played a pivotal role in scaling Android and Flutter development. He was the first Android hire tasked with delivering a mobile app in just six months while building and upskilling a team. BMW’s digital footprint extended across multiple brands—BMW, Mini, Rolls Royce, and even collaborative projects with Toyota. The challenge was immense, as they needed to maintain 49 different applications for various brands and regions, each with unique requirements and integrations. By 2017, BMW faced a pressing issue: the disparity between iOS and Android app development. While 70% of BMW’s customers used iOS, 30% relied on Android, yet investment in Android development lagged behind. The company needed a way to ship features simultaneously across platforms and markets. This led to the formation of an internal task force dedicated to identifying the best technology for future mobile development. After extensive research and testing across various frameworks—including Ionic, Cordova, and even native development—BMW’s engineering team identified Flutter as the optimal choice. Despite its early-stage maturity at the time (it wasn't even in beta yet), Flutter’s promise of multi-platform consistency, performance, and scalability made it the most compelling solution. ## Overcoming Stigma & Embracing Innovation BMW’s transition to Flutter wasn’t without challenges. There was internal skepticism about multi-platform development due to past struggles with tools like PhoneGap and Xamarin. However, through rigorous data-driven decision-making and leadership support, the team took a calculated risk. Switching to Flutter had its challenges.  A defining moment came during a pivotal “go/no-go” meeting. After months of research and testing, the team was still hesitant—until a senior architect firmly declared, “We are technologists, and if the data supports Flutter, we have to try.” With the leadership’s backing and a step-by-step approach, BMW successfully transitioned to Flutter, leading to faster development cycles, improved app quality, and, ultimately, higher customer satisfaction. If you want to gain more insights into the journey of adopting Flutter in companies like BMW and Tide, [watch our 'Maximizing Organizational Success' webinar here](https://verygood.ventures/webinars/maximizing-organizational-success/). ## Fostering a Culture of Ownership and Leadership Jorge emphasized the importance of leadership in fostering ownership within engineering teams. At BMW, leadership provided clarity and direction while allowing engineers to take responsibility for their work. This created an environment where individuals felt empowered rather than pressured, ultimately leading to better decision-making and more innovative solutions. “If you get that sweet spot with just a few but critical ideas, everyone has that ‘aha’ moment. That’s how you can get 300 people on board and drive value for the organization,” he added.  Jorge noted that companies like BMW, unlike modern tech giants, were not inherently software-driven. This presented a unique challenge—transforming a 100-year-old hardware company into an agile, software-driven organization. The key to success lies in balancing complexity with simplicity, ensuring that scalable solutions do not introduce unnecessary complications. Learn more on [how Flutter can transform engineering team culture in this blog post](https://verygood.ventures/blog/flutter-can-transform-engineering-team-culture/)!  ## Scalability in Engineering Jorge described scalable code as code that’s not only maintainable, but also accessible to new developers without extensive onboarding. In large organizations, engineers spend the majority of their time understanding existing code rather than writing new features. Simplifying and organizing code structures enhances productivity, enabling teams to focus on innovation rather than untangling technical debt. BMW’s experience demonstrated that scalability isn’t just about handling more users or increasing infrastructure but about ensuring that development teams can efficiently build and iterate on features. The key was to strike a balance between scalability, simplicity, and long-term maintainability. [Learn VGV’s best practices for a healthy, efficient, and maintainable codebase.](https://verygood.ventures/blog/building-better-software-the-importance-of-rigorous-code-reviews/)  ## Measuring Success in Engineering For Jorge, success in engineering is context dependent. For startups, success might mean finding product-market fit quickly, while for larger organizations, it could mean stabilizing a product and reducing technical debt. At BMW, success was defined by the ability to ship faster and more reliably, ensuring that new features added business value without introducing instability. Investing in scalable solutions, fostering an engineering culture that encourages risk-taking and experimentation, and leveraging the right tools were essential factors in achieving long-term success. Jorge pointed out that simply increasing developer headcount is not a sustainable solution—efficiency and automation are key to long-term scalability: “Success is about shipping faster and safer—delivering value while maintaining business stability.” ## The Next Technological Revolution: AR, Wearables & Cloud Simplicity Looking ahead, Jorge sees augmented reality (AR) and wearable technology as potential game-changers. Companies are experimenting with different approaches, from Meta’s smart glasses to Apple’s Vision Pro. While the ultimate direction of these technologies remains uncertain, they represent a new frontier in user interaction and software development. He also highlighted the complexity of cloud computing, noting that despite its promise of simplicity, the proliferation of services and options has made cloud infrastructure increasingly difficult to navigate. Jorge sees an opportunity for innovation in making cloud services more accessible and user-friendly without sacrificing scalability and power. ## Building for the Future Jorge’s journey from BMW to Very Good Ventures underscores the importance of embracing risk, fostering a culture of innovation, and leveraging scalable technologies. His experience serves as a blueprint for engineering leaders looking to drive transformation within their organizations. With Flutter continuing to revolutionize multi-platform development and new technologies on the horizon, the key takeaway is clear: success in engineering is about making bold decisions, balancing complexity with simplicity, and creating an environment where innovation thrives. ## Reach Out If you want to learn more about Jorge and his background, check out his [Behind the Unicorn Magic](https://verygood.ventures/blog/behind-the-unicorn-magic-get-to-know-the-vgv-team-3/) spotlight. You can also follow him on [LinkedIn](https://www.linkedin.com/in/jorgecocaramos/)! If you’re looking to build scalable, high-quality mobile solutions and want expert guidance from industry leaders, Very Good Ventures is here to help. Our team has deep experience in Flutter and modern engineering best practices, ensuring your apps are built to succeed. [Contact us now!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946411) ### 10 Reasons To Choose Flutter for Your Mobile App Source: https://verygood.ventures/blog/10-reasons-why-to-choose-flutter-for-your-mobile-app/ **Discover why Flutter is the ultimate choice for your next mobile app. From cross-platform development and stunning UI design to fast performance and cost-effective solutions, this Google-backed framework empowers developers to create beautiful, high-performing apps for iOS, Android, web, and beyond.**  Choosing the right framework can be the difference between success and falling short. Flutter, Google’s open-source UI toolkit, has emerged as a popular choice for building high-performance, cross-platform mobile applications. At Very Good Ventures (VGV), we’ve seen firsthand how Flutter empowers businesses to deliver exceptional products efficiently. Here are 10 compelling reasons why Flutter should be at the top of your list for your next mobile app project. ## 1. Cross-Platform Development Flutter allows you to write a single codebase that runs seamlessly on mobile (iOS, Android), web, and desktop (Windows, macOS, Linux). This significantly reduces development time and costs while ensuring consistency across platforms. At VGV, we specialize in creating unified solutions using Flutter, enabling our clients to reach their audience faster. ## 2. Fast Development with Hot Reload Flutter’s hot reload feature enables developers to see the impact of code changes instantly without restarting the app. This speeds up development and allows for real-time experimentation—a game-changer for developers looking to iterate quickly and deliver high-quality products. [See how this feature allowed our team to launch the Super Dash game in just six weeks.](https://hubs.ly/Q034nv7K0)  ## 3. Beautiful UI with Custom Widgets Flutter’s rich set of customizable widgets ensures that your app looks and feels great on any device. With Material Design and Cupertino widgets, you can achieve platform-specific aesthetics effortlessly. VGV leverages Flutter’s UI capabilities to design stunning, user-friendly applications tailored to each client’s needs. [Check out the incredible designs we brought to life in this project—all made possible with Flutter!](https://verygood.ventures/success-stories/flutter-news-toolkit/) ## 4. High Performance Flutter code compiles directly to native ARM or Intel machine code, as well as JavaScript, eliminating the need for a bridge between the app and the platform. This ensures fast startup times, smooth performance, and an optimized user experience on any device. Our engineering team at VGV optimizes Flutter apps to ensure they deliver a flawless user experience. [Learn how Universal is building next generation experiences with Flutter](https://flutter.dev/showcase/universal-studios) ## 5. Strong Community and Ecosystem The Flutter community has grown exponentially, providing a wealth of open-source packages, plugins, and tools. From state management to API integration, there’s likely a package for your needs. VGV actively contributes to the Flutter ecosystem, sharing open-source tools and best practices on development processes ensuring our clients benefit from cutting-edge solutions. Last year, David DeRemer, CEO and founder of VGV, introduced the idea of a community-driven commercial roadmap. This initiative encourages the community to set shared objectives, align priorities, and inspire collective efforts to strengthen and accelerate Flutter's growth. [Learn how you can contribute to it!](https://verygood.ventures/blog/flutters-future-a-call-for-community-driven-commercial-roadmap/)  ## 6. Google-Backed Framework As a Google product, Flutter is actively developed and maintained by a dedicated team. It’s also used in production by Google itself for apps like Google Pay, ensuring reliability and innovation. Partnering with VGV means tapping into a team with deep expertise in this robust framework. ## 7. Rich Animation Capabilities Flutter simplifies the process of creating animations with its built-in tools and libraries. From simple transitions to complex motion effects, you can deliver engaging user experiences with ease. Our team integrates creative animations to bring your app to life. ## 8. Cost-Effective Development By reducing the need for separate development teams by platform, Flutter lowers overall project costs. The unified codebase also simplifies maintenance and updates. At VGV, we help clients achieve their goals efficiently without compromising quality. ## 9. Leading Brands Are Choosing Flutter For most organizations, mobile applications are a critical part of an overall customer experience. For others, mobile applications are one touchpoint among many others, including web, desktop applications, and even embedded systems, such as smart homes and smart cars. But for almost all businesses and organizations today, software applications are a necessary element of how they do business and a critical part of their overall customer experience.  By 2025, Flutter has become the framework of choice for many leading companies and brands. From BMW to Alibaba, early adopters are seeing the value Flutter brings to their business.  Flutter’s influence extends beyond large corporations to include startups, small and medium-sized businesses, and independent developers. These groups benefit from its user-friendly development process, extensive documentation, and robust community support. This widespread adoption highlights Flutter’s flexibility and ability to meet the needs of projects of all sizes. ## 10. Future-Proof Technology Flutter’s popularity and adoption continue to rise, fostering a vibrant ecosystem with long-term support. Since its 1.0 release in 2018, the Flutter team has conducted quarterly user surveys to gather developer feedback. In Q1 2023, the 19th survey collected 13,378 responses in just 14 days, covering topics like Flutter’s growth, API breaking changes, and deep linking. An impressive 93% of respondents reported being satisfied with Flutter, with 55% being very satisfied—a sentiment that has remained steady since August 2022. Notably, this high satisfaction level persisted even as Flutter's user base grew by 24% over the same period. Investing in Flutter means staying ahead of the curve in app development trends, and at VGV, we ensure your apps are built to last—with scalability, performance, and innovation at their core. [Learn more about the Business Value of Flutter in this whitepaper.](https://39662796.fs1.hubspotusercontent-na1.net/hubfs/39662796/WhitePapers/VGV-Whitepaper-ROI-of-Flutter.pdf)  ## Start your Flutter journey   Flutter offers a winning combination of speed, flexibility, and cost-effectiveness, making it an excellent choice for building mobile applications. Whether you’re a startup looking to bring your idea to life or an enterprise seeking a scalable solution, Flutter provides the tools and capabilities to meet your needs. At VGV, we build successful experiences for all. Ready to explore what Flutter can do for your next mobile app? [Check out our approach to app development](https://verygood.ventures/blog/benefits-of-flutter-for-cross-platform-app-development/), or [contact us](https://hubs.ly/Q034nx1w0) to start your journey right away! ### The Hard Thing About Hard Things: Macros in Dart Source: https://verygood.ventures/blog/the-hard-thing-about-hard-things-macros-in-dart/ The Dart team recently [announced the discontinuation of their work on macros](https://medium.com/dartlang/an-update-on-dart-macros-data-serialization-06d3037d4f12). At Very Good Ventures, we see this decision as not only rational, but beneficial for the long-term success of both the Dart language and the Flutter ecosystem.  ## A Strategic Shift, Not A Setback When evaluating the decision to stop investing in macros, it’s essential to recognize the underlying reasoning. Google, and especially in the case of the Dart team, doesn't have unlimited resources. Yes, it is one of the most important companies in the world, but as any other team, they have to balance multiple (and sometimes conflicting) priorities: advancing the capabilities of the language, increasing performance, responding to the community’s feedback, fixing bugs, and so on. And, on top of that, they work in public: their [roadmap](https://github.com/flutter/flutter/blob/master/docs/roadmap/Roadmap.md) (and therefore, their strategic bets) are available to everyone.  As of today, we can say all their major bets were highly successful… but macros proved to be more challenging than initially expected. This level of public scrutiny is a very healthy exercise of prioritization with and in front of the community—but it can be used also for those that aren’t Flutter and Dart promoters to use this as a detractor of the framework, when the reality is that [it powers more than 30% of new apps worldwide](https://medium.com/flutter/flutter-in-production-f9418261d8e1). The Dart team, in their official announcement, outlined that macros had significant technical challenges, making them difficult to implement in a way that aligned with Dart’s design goals. Rather than persisting with a solution that introduced complexity and potential instability, the team chose to pivot towards other improvements and features that can bring more immediate benefits. Rather than lament this, we could praise the team for making the very difficult decisions, since they certainly are aware of the public relations ramifications. ## Prioritization Enables Progress Successful software development (and especially in the case of open source development) is about trade-offs, and the Dart team’s choice reflects a strategic prioritization of their time and energy. Instead of spreading themselves thin over a feature that has proven to be more expensive than the value it can provide, they can now focus on areas that provide greater value to developers. Better data serialization, stronger code generation mechanisms, enhanced developer tooling…the work on macros won’t be wasted, and some of those learnings will highly influence how developers use Dart in the years to come. These investments will ultimately benefit Flutter and Dart developers, ensuring an even more pleasant and stable framework. ## Dart And Flutter Thrive On Simplicity And Performance One of Flutter’s core strengths is its ability to deliver high-performance applications with a simple and expressive developer experience. The introduction of macros could have added another layer of complexity, making the language harder to reason about and maintain. By stepping away from macros, the Dart team is doubling down on what makes Flutter appealing: a fast, efficient, and easy-to-use development experience that can have an impact today and years to come. ## The Hard Thing About Hard Things I’m sure this decision was not easy to make. As a [Google Developer Expert (GDE) myself](https://developers.google.com/profile/u/101582559357979771564), I can say that the Dart team has been in constant communication with their most active and influential contributors, taking their feedback very seriously, and guiding us along the way in this journey until they found it might be a dead end. Instead of isolating themselves from the community and its leaders, the Dart team has done an excellent job communicating and explaining their thinking process on this matter. The community has felt supported at all times, and their feedback has been taken very seriously. This case should be considered a *gold standard* when it comes to managing relations between an open source project and its community.  As an engineering leader, I applaud their humility, transparency, and healthy ambition. We all knew that the work on macros was a big bet and a big effort—Dart’s simplicity comes with a series of design constraints that make certain features a gigantic challenge behind the scenes. Very few languages out there support JIT & AOT compilation, tree-shaking, null-safety, FFI…but Dart does. In order to support all these, tough decisions were already made (e.g. lack of support for reflection) that make other potential features, like macros, almost impossible to develop. All things considered, I’d rather work with a language like Dart today than with a language bloated with half-baked features that, over time, make the development experience worse just so they can claim shipping a new feature. A lot of people dream of starting a new and successful open source project, but the brutal reality is that, when that happens, you are under constant scrutiny. That’s the [hard thing about hard things](https://a16z.com/books/the-hard-thing-about-hard-things/).  ## Dart & Flutter Will Fuel The Future Of UI Development Far from being a sign of concern, the reprioritization away from macros is a move towards a more focused and efficient future for Dart and Flutter. By making thoughtful trade-offs, the Dart team is ensuring that developers have a more stable, performant, and maintainable ecosystem. Instead of lamenting this decision, we should embrace it as a positive step towards a better development experience. Flutter is, in fact, evolving in a way that prioritizes long-term success over short-term complexity. ### Building a Strong Tech Culture & Transforming Real Estate Source: https://verygood.ventures/blog/building-a-strong-tech-culture-transforming-real-estate/ ***In our latest podcast episode, we dive into the tech culture transformation at Keller Williams with Chris Synan, a key figure in driving innovation at the company. With years of experience in leading large teams and managing complex system overhauls at industry giants like Dell, AMD, CA Technologies, and General Motors, Chris offers invaluable insights on how technology and culture can intertwine to foster groundbreaking change. Here's a recap of his journey at Keller Williams and the lessons learned along the way.**** * **A Vision for Change Amid Crisis **When Chris Synan joined Keller Williams just before the COVID-19 lockdown, the company faced significant challenges in adapting its technology to meet rapidly changing market demands. Like many other businesses, Keller Williams found itself burdened by tech debt, slow release cycles, and a rigid system that stifled innovation. However, Chris and his team were determined to not just weather the storm but to transform the business into a more agile, data-driven, and innovative organization. *“We’ve really transformed in so many ways”* Chris reflects, highlighting how the company has evolved since he joined. **From Pain to Progress: The Key Transformation **In the early days of the transformation, Keller Williams managed only one or two releases per quarter—a stark contrast to their new, rapid deployment capabilities. The ability to ship new features with the click of a button was a game-changer, not just for the tech team but for the entire business. As Chris explains, *"The confidence in knowing that we could release that rapidly was such a massive shift."* It’s a sentiment that resonates across many industries: the ability to act quickly, take risks, and iterate faster is often the key to staying competitive. One of the turning points came when business leaders asked the tech team to slow down their releases, a request that was initially surprising. *"That just killed me"* Chris laughs, adding that it felt like a major milestone. This sudden shift demonstrated how far the team had come in terms of predictability and execution speed. **The Culture of Trust **The success of Keller Williams’ innovation and transformation wasn’t just about the technology—it was about trust. Chris attributes much of the success to the strong relationships and transparency within the team. He emphasizes that it’s essential for leadership to trust their teams to take risks, even when it means slowing down to build a more solid foundation. *"Sometimes, to go faster, you have to start by going slower.",* says Chris. By taking the time to clean house and streamline processes, the team was able to build the infrastructure needed to scale quickly. This level of trust—between leaders and team members, as well as between departments—was key to the success of their overhaul. As Chris points out, not every organization is equipped to foster this level of collaboration. Without that trust, the transformation might not have been possible. You can learn more about [how Flutter can transform engineering team culture here.](https://verygood.ventures/blog/flutter-can-transform-engineering-team-culture/)  **The Power of Leadership **One of the critical lessons Chris shares is the importance of leadership in driving change. For any company facing similar challenges—slow tech deployment, high tech debt, and frustration from business leaders—the advice is clear: start with leadership commitment. *“Leadership has to be the first to commit to excellence*”, Chris advises. The willingness to take risks, especially when it comes to making bold decisions like overhauling core systems, is crucial. But it also requires clear communication, trust, and a well-thought-out plan. The work done at Keller Williams wasn’t just about solving technical challenges; it was about resetting the company’s culture. *"We’re opening the doors, we’re ready"* says Chris. Now, instead of being constrained by old systems, the business is embracing new ideas and innovations, creating new growth opportunities. For more insights on creating transformative business value through innovative strategies, explore our [webinar on maximizing your business value with Flutter](https://verygood.ventures/webinars/maximizing-your-business-value-with-flutter/). Perhaps the most rewarding part of this transformation for Chris has been watching his team rise to the challenge. He speaks passionately about the joy of seeing team members grow, take risks, and become leaders in their own right. *“You believe in people, you tell them, 'I believe in you. I think you're a superstar. I want to give you everything you need'"* Chris says, emphasizing the importance of empowering others to succeed. When employees feel supported and trusted, they can go on to achieve remarkable things—whether they started as skeptics or not. The journey at Keller Williams proves that when leadership invests in their team and fosters a culture of trust and empowerment, the results can be transformational. **Tools Are Important, But Culture Drives Success **In technology, tools like React Native or Flutter often dominate conversations. While these tools do matter, Chris emphasizes that it’s the underlying culture that truly drives success. *“Tools matter, but what matters more is leadership culture, trust, and the ability to stick with it when you make the choice”* Chris explains. The technology is just a part of the equation; how an organization uses it and the people behind the tools ultimately determine the success of a transformation. **The Future of Keller Williams **Looking ahead, Chris sees exciting opportunities for Keller Williams. The company is no longer playing catch-up; instead, it’s embracing the entrepreneurial spirit that was previously stifled by tech debt and slow processes. *“Bring it on”* Chris says, looking forward to the challenges and innovations that lie ahead. As the organization continues to scale, the team at Keller Williams is ready to take on new opportunities and continue driving growth with the same agility and confidence that have defined their recent transformation. **Want to Learn More? **To dive deeper into Keller Williams' transformative journey, go listen to our complete [podcast episode “Transforming Real Estate Through Cutting-Edge Tech”](https://hubs.ly/Q0347f4m0) and take notes on key insights you can now apply to your business. ### How to Beat the Market by Making the Best App in 2025 Source: https://verygood.ventures/blog/how-to-beat-the-market-by-making-the-best-app-in-2025/ In today's competitive digital world, creating a standout app requires more than just a unique idea. It demands a strategic approach that prioritizes quality, scalability, and user engagement. At Very Good Ventures (VGV), we’ve developed a proven methodology that consistently delivers high-performing applications and sets them up for success. Here's how to leverage VGV’s approach to create an app that stands out in the market. ## Choosing the Right Framework for Your Project’s Needs Staying ahead of the curve often means embracing cross-platform development. At VGV, we excel in building apps that work seamlessly across multiple platforms, with [Flutter](https://verygood.ventures/our-services/)–Google’s open-source framework–as our primary tool. Flutter enables developers to create natively compiled mobile, web, and desktop applications using a single codebase, allowing for a streamlined development process that reduces time-to-market and costs. While our expertise in Flutter has empowered clients from various industries to deliver high-performance, scalable apps, we recognize that there are scenarios where native development may be preferable. For example, when integrating with the latest Android or iOS APIs, or when app size is a critical factor—such as for devices with limited storage or in markets where data costs are high—native solutions may offer distinct advantages. At VGV, we are committed to leveraging our Flutter expertise to deliver consistent, beautiful design and functionality across devices. At the same time, we remain open to native development when it best meets our client’s needs, ensuring flexibility and the optimal user experience for every project. If you’re unsure about the best technology to develop your app, [we’re here to help!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946553) Our Concept & Technology team specializes in thorough research and in-depth analysis to determine the ideal solution for each unique project. ### Implement a Robust Application Architecture A well-structured architecture is the backbone of a successful app. VGV employs a consistent layered architecture that reduces complexity and enhances maintainability. Key components include: - Modularity: Breaking down the app into distinct, manageable modules facilitates easier updates and feature additions. - Scalability: Designing the architecture to accommodate future growth ensures the app can handle increasing user demands. - Testability: Implementing comprehensive testing protocols to identify and address issues promptly, maintaining app reliability. This approach allows for efficient scaling of both code and development teams, ensuring long-term success. ### Prioritize User-Centric Design An app's success is heavily influenced by its user experience, making it essential to create an engaging and intuitive interface. This involves conducting thorough user research to understand the target audience's needs, preferences, and pain points, which informs design decisions.  [Focusing on accessibility and digital inclusion](https://verygood.ventures/blog/exploring-accessibility-and-digital-inclusion-with-flutter/) ensures that everyone can use the app, which helps reach a larger audience. Additionally, gathering user feedback at every opportunity we have and iterating on the design helps enhance user satisfaction and retention, ensuring the app remains relevant and effective over time.  VGV's design team emphasizes creating cohesive and engaging user experiences that resonate with users. Curious to see some of the cool designs we've created for our clients? [Check out this great example!](https://verygood.ventures/success-stories/hespress-news-app/) ### Why QA and Testing are Critical for Your App Delivering a stable and performant, high-quality app is crucial for building trust and credibility. At VGV, we implement rigorous testing procedures, including automated tests to quickly identify issues during development and thorough manual testing to assess the app's functionality, usability, and performance. We also incorporate continuous integration and continuous deployment (CI/CD) pipelines to streamline updates and maintain consistent quality. By adding tests and CI/CD, we ensure that future iterations of the project behave as expected, making it easier to track changes and identify bugs, thus reinforcing the app’s reliability and overall performance. Analytics play a vital role in understanding user behavior and enhancing their experience. By leveraging data, you can gain insights into how users interact with your app, which features are most used, and where friction points may exist. This information helps inform decisions on improving functionality and fine-tuning the user journey for greater satisfaction and engagement. VGV’s commitment to quality assurance ensures that apps perform optimally across all platforms. ### Plan for Post-Launch Support and Iteration VGV's approach includes long-term support and collaboration to ensure sustained app success. Launching the app is only the starting point. To stay competitive, it’s crucial to monitor performance metrics such as user engagement and retention rates to gauge the app's success. Actively responding to user feedback by addressing concerns and suggestions helps improve the app and build user loyalty. Additionally, regularly updating the app with new features and enhancements keeps existing users engaged and attracts new ones, ensuring the app remains relevant and valuable in a dynamic market. Additionally, we emphasize the importance of clear strategies for growth and monetization. Every app should either generate revenue or reduce operational costs, but many companies fail because they mix these objectives. Having a clear focus and alignment on your growth and cost-saving goals ensures your app’s long-term success. ## We Can Help You Build the Best Mobile App At Very Good Ventures, we’re with you every step of the way to find the best solutions for your business. From initial concept and project management to app development, deployment, and top-tier UI design with strict QA protocols, we’ve got you covered. By adopting Very Good Ventures' comprehensive approach to app development, you can create a high-quality, scalable, and user-friendly application that stands out in the market, driving success and growth for your business. [Explore some of our most impactful projects across various industries.](https://verygood.ventures/success-stories/) If you're considering adding an app to your business, [feel free to reach out!](https://verygood.ventures/contact-us/?hsCtaAttrib=140727946553) ### Flutter Beyond Mobile: VGV Builds the First Flutter Boombox Source: https://verygood.ventures/blog/flutter-beyond-mobile-vgv-builds-the-first-flutter-boombox/ At Very Good Ventures (VGV), we’re always pushing the boundaries of what’s possible with Flutter. While most know Flutter for its mobile app capabilities, its versatility enables it to function seamlessly as a multi-device powerhouse. We showed off the Flutter Boombox project at the recent #FlutterInProduction virtual event—here’s everything you need to know about this innovative showcase of Flutter’s potential. Enter: a 1984 Sanyo portable radio and cassette player. An iconic staple of the boombox era, this nostalgic go-to got an innovative modern makeover by VGV. The revamped Flutter Boombox was showcased by none other than Weezer frontman Rivers Cuomo—who, fun fact, is also a Flutter developer—alongside Andrew Brogdon, Staff Developer Relations Engineer at Google. The highlight? Watching Cuomo and Brogdon bond over the Boombox while vibing to Cuomo’s own hits before unveiling this impressive innovation. How cool is that? ![Rivers Cuomo and Andrew Brogdon showcasing the Flutter Boombox on stage](/assets/images/blog/flutter-beyond-mobile-vgv-builds-the-first-flutter-boombox/body-0.jpeg) As Andrew pointed out, Flutter officially supports six platforms iOS, Android, Linux, macOS, Windows, and Web—but developers in the community are constantly experimenting to see what else they can run Flutter on. And that’s exactly what we did! We will dive into how our developers used Flutter to seamlessly integrate with embedded systems and APIs (like Raspberry Pi and Spotify) to create a unique and immersive experience. Ready? Let’s go! ## Say Anything: The Magic Behind the Boombox For this updated trip down memory lane, we took a Raspberry Pi 4B running Linux, paired it with Spotify Connect software, and used MQTT for event handling.  First, we removed the cassette deck and replaced it with a 5-inch touchscreen. Then, we added our Flutter-based UI that mimics a retro cassette tape. The UI dynamically changes with the music, showcasing real-time song data pulled from the Spotify API. The result? A modern spin on an old-school device, with Flutter at its core. {% video url="https://www.youtube.com/watch?v=ozR3LzRZmRs" title="Video" %} VGV’s Boombox Project handles the complex task of rendering the UI on a custom device while seamlessly integrating with Spotify’s API. But it doesn’t stop there. The project opens up a world of possibilities for developers beyond app screens and web browsers, demonstrating the real-world potential of Flutter in embedded systems.   With a growing number of businesses looking to create unified experiences across devices, Flutter offers an opportunity to deliver consistent, high-quality UIs on platforms well beyond the smartphone. ## #FlutterInProduction: Key Takeaways  Missed the #FlutterInProduction virtual event? No worries! We've summarized the key highlights for you, and you can also watch the full event here: [https://www.youtube.com/watch?v=AEXIrThTgb0](https://www.youtube.com/watch?v=AEXIrThTgb0) In a nutshell, the event highlighted the evolution, achievements, and future of Flutter as a versatile framework for building high-quality, multi-platform apps. The event celebrated how Flutter empowers teams to create innovative, unified digital experiences, setting the stage for even greater possibilities in the future. **Flutter's Journey** What started as an experimental project a decade ago –yes, it all started 10 years ago and was initially called Sky– has now grown into a powerful framework, driving over a million apps with hundreds of millions of users worldwide. Pretty mind-blowing, right? **Cross-Platform Leadership** Developers can share up to 90% of their codebase, drastically reducing development costs and timelines. With a thriving community of over a million active developers and vibrant meetups in 64 countries, Flutter’s future looks brighter than ever. **Flutter = Innovation** [Flutter 3.27 introduced exciting updates](http://goo.gle/3VAJedw), including improvements to the Material Design library, accessibility enhancements for the web, and performance boosts through technologies like Impeller and WebAssembly. **Enterprise Success** Leading companies like LG Electronics, MGM Rewards, and SNCF Connect have leveraged Flutter to enhance app quality, scalability, and accessibility, all while reducing time to market. **The Production Era** Flutter is now focused on fulfilling its promises with enhanced tools, improved rendering, and a seamless developer experience, cementing its role as the go-to framework for high-quality apps on any device. ## Get Involved The Flutter Boombox is just an example of how deep we can dive into the future of Flutter in spaces beyond mobile devices, and we’re committed to investing in demos and prototypes that showcase what this framework can do in non-traditional environments. With Flutter, you can build high-quality, beautiful UIs anywhere. The possibilities for creating rich, interactive experiences on non-mobile platforms are endless.Stay tuned for more innovation—and if you’re a developer looking to experiment with Flutter beyond mobile, we encourage you to get started! Read more about how we can shape the future of Flutter together, and explore the community-driven roadmap in this blog covering [David DeRemer’s keynote talk at Fluttercon USA](https://verygood.ventures/blog/flutters-future-a-call-for-community-driven-commercial-roadmap/) earlier this year. ### Starting using Dart: A Seamless Transition For Developers Source: https://verygood.ventures/blog/starting-using-dart-a-seamless-transition-for-developers/ **The Very Good Ventures team has been busting myths about Flutter and its performance. Today, we’re diving deeper into Dart, Flutter’s programming language. Stick around to see why it’s a piece of cake for devs to pick up!**  While Dart might be new to some developers, it’s intentionally designed to be easy to learn. For those familiar with JavaScript, Kotlin, Java, or Swift, Dart offers a smooth learning curve, enabling developers to get up to speed quickly. Its syntax and structure are accessible, especially for anyone experienced with [C-style languages.](https://en.wikipedia.org/wiki/List_of_C-family_programming_languages) Let’s explore some code examples to highlight these similarities! ## 1. Creating Variables You will see that there is not much difference when creating variables. Let’s see how to create: ### Dart Dart uses var, final, and const to declare variables. The final keyword is for immutable variables, while const is for compile-time constants. ```dart // Type inferred var name = "Alice"; // Explicit type String city = "Paris"; // Immutable final age = 30; // Compile-time constant const pi = 3.14; ``` ### Kotlin Kotlin uses val (immutable) and var (mutable). ```kotlin // Type inferred var name = "Alice" // Immutable, explicit type val age: Int = 30 // Immutable constant val pi = 3.14 ``` ### Swift Swift uses var (mutable) and let (immutable). ```swift // Mutable variable var name = "Alice" // Immutable, explicit type let age: Int = 30 // Immutable constant let pi = 3.14 ``` ## 2. Creating Classes When creating classes, there are a few more differences, but you will see that it is very easy to switch from Kotlin or Swift to Dart. ### Dart Classes in Dart are defined using the class keyword. ```dart class Person { const Person({ required this.name, required this.age }); final String name; final int age; void greet() { print('Hello, my name is $name.'); } } ``` ### Kotlin Allows you to declare and initialize class properties directly in the primary constructor, avoiding boilerplate code. ```kotlin class Person(val name: String, val age: Int) { fun greet() { println("Hello, my name is $name.") } } ``` ### Swift Swift classes use the class keyword and require initializers unless default values are provided. ```swift class Person { var clothes: String var shoes: String init(clothes: String, shoes: String) { self.clothes = clothes self.shoes = shoes } } ``` ## 3. Defining Functions The functions are very similar in all three languages. Let's find out the differences. ### Dart Dart functions are defined using void for no return type, or the specific type for return values. ```dart void greet() { print('Hello!'); } int add(int a, int b) { return a + b; } ``` ### Kotlin Functions in Kotlin use the fun keyword. Return types are specified after a colon. ```kotlin fun greet() { println("Hello!") } fun add(a: Int, b: Int): Int { return a + b } ``` ### Swift Functions in Swift use the func keyword. Return types are specified with ->. ```swift func greet(person: String) -> String { let greeting = "Hello, " + person + "!" return greeting } func add(a: Int, b: Int) -> Int { return a + b } ``` ## 4. Control Flow (If-Else) The if/else in Kotlin is exactly the same way, and in Swift it is by removing the (). ### Dart ```dart if (age > 18) { print('Adult'); } else { print('Minor'); } ``` ### Kotlin ```kotlin if (age > 18) { println("Adult") } else { println("Minor") } ``` ### Swift ```swift if age > 18 { print("Adult") } else { print("Minor") } ``` ## 5. Asynchronous calls Asynchronous calls are a little different in each language, Dart and Swift are more similar, and Kotlin is a little more complex due to the use of coroutines, but even if your background is Kotlin, you will find that asynchronous calls in Dart are very simple. ### Dart  Dart uses Future to handle asynchronous operations. ```dart void main() async { var result = await fetchDataFromNetwork(); print("Result: $result"); } Future fetchDataFromNetwork() async { await Future.delayed(Duration(seconds: 1)); // Simulates a 1-second delay return 'Data fetched'; } ``` ### Swift Swift provides the async/await pattern to handle asynchronous operations in a simple and readable way. ```swift func fetchDataFromNetwork() async -> String { try? await Task.sleep(nanoseconds: 1_000_000_000) // Simulates a 1-second delay return "Data fetched" } @main struct MyApp { static func main() async { let result = await fetchDataFromNetwork() print("Result: \(result)") } } ``` ### Kotlin In Kotlin, coroutines are used to make asynchronous calls. ```kotlin fun main() = runBlocking { val result = async { fetchDataFromNetwork() // Simulated function that represents an async call } println("Result: ${result.await()}") } suspend fun fetchDataFromNetwork(): String { delay(1000) // Simulates a 1-second delay (like a network call) return "Data fetched" } ``` These examples highlight how straightforward the learning curve is, especially if you’re coming from Kotlin or Swift. The same applies if your background includes languages like Java, C#, or JavaScript. So, dive in with confidence—start your journey with Dart and Flutter today! Go check out more info about it here: - [Dart documentation](https://dart.dev/guides) - [Kotlin documentation](https://kotlinlang.org/docs/getting-started.html) - [Swift documentation](https://docs.swift.org/swift-book/documentation/the-swift-programming-language) ### Behind the Unicorn Magic: Get to know the VGV Team Source: https://verygood.ventures/blog/behind-the-unicorn-magic-kyle-crouse/ ## From Concept to Creation: Kyle’s Role at VGV Kyle joined VGV in March 2023, bringing over 20 years of expertise in leveraging technology to innovate and craft user experiences for digital interfaces, physical products, and ambient spatial experiences to the forefront of our Concept Technology team. His mission? To help identify the *right* solutions before reaching the traditional delivery process. By focusing on prototyping, rigorous user-centered design, and rapid insights gathering, Kyle ensures we approach every project with clarity and purpose. K: My role is about experimentation and innovation to later inform what’s worth building and validating ideas early, so we can create products that truly resonate with users. I love being that bridge between design and technology, always in service of great UX. ## Kyle’s Background  K: Before joining VGV, I spent over eight years at Frog, focusing on design technology for GE, Disney and more. I worked on prototyping solutions to answer tough design questions quickly, learning how to fail fast, uncover problems, and find opportunities. From there, I led the technology team at Method, collaborating with brands like McDonald’s, where I helped to transform their mobile app, kiosk experience, and in-restaurant digital systems. I then moved to Amazon, where I headed the Design Technology Team. My work focused on developing hardware and software concepts that were three to five years ahead of the product roadmap, prototyping and testing new ideas to pave the way for future innovations. **Can you describe a VGV project you're particularly proud of?** K: We’re working on a project that focuses on simplifying a highly data-driven and complex environment, specifically targeting audiences who may find it challenging to engage with or understand the information. Our approach centers on breaking down intricate data into intuitive, user-friendly formats that help guide users through the experience. By contextualizing the information in a way that’s accessible and engaging, we’re creating an environment where users can fully enjoy and connect with the experience, even if they’re entirely new to it. This makes the event or platform more inclusive and enjoyable for everyone, not just experts. While I can’t share too much yet, it’s been exciting to see how we’ve tackled such a data-heavy challenge with user-first solutions. ## Day-to-day challenges K: At VGV, one of my biggest challenges has been building out our Concept Technology offering. It’s a new initiative for us, and we’re working to win the work while uncovering users’ needs and pain points in novel, intuitive ways.  Ultimately, the hardest part is uncovering real user needs and finding intuitive ways to solve their pain points. It’s about simplifying the experience while ensuring technology gets out of the user’s way. **Are there any emerging technologies or trends you find particularly exciting?** K: I’m always interested in using the best tool for the job. That’s why I’m such a fan of Flutter—it allows us to deliver consistent, high-quality experiences across multiple platforms without sacrificing quality. I’m also intrigued by emerging trends like 3D technologies and open-source frameworks like Godot, which have the potential to elevate user experiences in new ways. I love exploring what’s coming next and figuring out how we can integrate it into future projects. ## Looking Forward **What aspirations do you have for the impact of your work at VGV in the future? **My goal at VGV is to bring our exceptional product delivery quality upstream—to help clients unlock product innovation before development even begins. By combining rigorous design practices with rapid prototyping, I want to ensure every product we create is not only successful but also loved by its users. ## Who is Kyle beyond VGV? - When he’s not leading innovation at VGV, Kyle is an avid sim racer, a hobby he’s pursued since 1999! - He also spends his time in Los Angeles with his two kids, balancing work, life, and plenty of racing practice! [Connect with Kyle on Linkedin >](https://www.linkedin.com/in/kylecrouse/) ### Flutter Myths: Clearing Up Common Misconceptions Source: https://verygood.ventures/blog/flutter-myths-clearing-up-common-misconceptions/ Myths can be a lot of fun (Sasquatch roaming the Pacific Northwest, anyone?). However, when it comes to popular technology like Flutter, those myths can sometimes cause hesitation or confusion for new users. Enter: Very Good Ventures. With years of hands-on experience, the VGV team has debunked the biggest Flutter myths to show you why it’s the top choice among developers and how it can unleash your company’s potential. ## The Truth Is Out There: Common Flutter Myths Explained ### Myth #1: *“Flutter isn’t as performant as Native solutions”* Cross-platform frameworks, including Flutter, often carry a reputation for lower performance, frequently being grouped with other frameworks and assumed to be slower or less efficient than native options. However, Flutter’s unique issues don’t typically mirror the problems faced by other frameworks, as Dart compiles directly to machine code and Flutter leverages [Impeller](https://docs.flutter.dev/perf/impeller) for rendering, which enables a smooth UI experience comparable to native. Thanks to its robust framework, Flutter supports high-performance applications with ease, making it a powerful solution for performance-driven cross-platform apps. ### Myth #2: *“**Flutter developers don’t need Native experience or knowledge**”* Flutter’s simplified, cross-platform codebase may seem to eliminate the need for native expertise. Yet, for tasks like integrating custom plugins, managing platform-specific dependencies, or working with native SDKs, some familiarity with native Android or iOS development is invaluable. Native experience isn’t strictly required, but handling more complex Flutter applications can be a big advantage. ### Myth #3: *“Flutter lacks plugin availability, making it hard to implement certain functionalities without custom code.”* In Flutter’s early days, plugin options were limited. But today, there are [numerous plugins](https://github.com/flutter/packages)\-like camera, file\_selector, google\_maps\_flutter, google\_sign\_in, etc., for nearly any functionality a project might need, with many [officially supported by the Flutter team](https://pub.dev/publishers/fluttercommunity.dev/packages), including share\_plus, sensors\_plus, among others.  When an existing plugin doesn’t meet a project’s needs, Flutter also offers the flexibility for developers to [create custom plugins](https://docs.flutter.dev/packages-and-plugins/developing-packages) by leveraging native code, ensuring they can extend functionality to fit any requirement. There are still some challenges to face, particularly for newer platforms such as Web, macOS, Windows, and Linux. In these areas, the plugin ecosystem may not yet be as comprehensive as it is for the more established iOS and Android platforms. But, despite these obstacles, the progress is undeniable. Over the past 3-5 years, plugin availability for iOS and Android has grown significantly, offering developers a much richer and more seamless experience than in the early days of Flutter. ### Myth #4: *“You’ll have to learn a whole new programming language”* While Dart (the programming language behind Flutter) may be new to some developers, it’s specifically designed to be easy to learn. Developers familiar with JavaScript, Kotlin, Java, or Swift often find that Dart has a smooth learning curve, allowing them to become productive quickly. ### Myth #5: *“Google might cancel Flutter”* There are concerns that Flutter might someday be left behind, especially given the history of other tech giants turning off projects. However, Flutter’s widespread use within Google–combined with its strong open-source community and growing adoption outside of Google–indicates a stable future. Even if Google’s focus were to shift, Flutter would likely continue to thrive as an open-source project supported by a robust community invested in its success. ### Myth #6: *“Flutter doesn’t work well for the Web”* While Flutter may not yet be ideal for SEO-focused sites or static text-heavy pages, it’s exceptional for building dynamic web applications like dashboards and interactive applications. With Flutter, developers avoid complex styling and CSS issues, enjoy streamlined animations, and benefit from cross-platform capabilities that easily extend from web to mobile and desktop. Upcoming support for WebAssembly promises to boost performance even further, solidifying Flutter as a versatile option for web apps. Want to see how VGV and the Flutter team successfully built projects that run seamlessly on the web? Check out the [I/O Pinball Game](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d) and the [Flutter Forward Holobooth](https://medium.com/flutter/how-its-made-holobooth-6473f3d018dd). You can also watch one of the main talks at Fluttercon USA 2024, ["Implications of Flutter Wasm"](https://www.droidcon.com/2024/10/17/implications-of-flutter-wasm/), by Kyle Finlinson, Senior Engineer at VGV. You'll get an expert's insights on best practices, benefits, and key implementation considerations for using WebAssembly (WASM) and get a deeper look at why this myth doesn't hold up. ### Unlock Flutter’s Full Potential  Flutter’s rapid evolution has fueled misconceptions about its limitations, many of which no longer apply. With strong performance, an extensive selection of plugins, and an engaged developer community, Flutter is a powerful and often advantageous choice for cross-platform mobile, web, and desktop development. Now that we’ve debunked these myths, are you ready to explore Flutter’s potential? If you’re interested in learning more about building with Flutter, [reach out](https://verygood.ventures/contact-us/)—let’s dive into the infinite possibilities together! ### Why Learn SwiftUI as a Flutter Developer? Source: https://verygood.ventures/blog/why-learn-swiftui-as-a-flutter-developer/ Since the birth of Very Good Ventures (VGV), we have helped our clients build beautiful and scalable Flutter apps. The advantages of Flutter are obvious, but in particular, we’ve seen it help our clients ship faster and more efficiently because you can target many platforms with the same code base. We truly believe that anytime you’re building a mobile or desktop app, you should seriously consider Flutter over native development. Unfortunately, there are many edge cases that Flutter doesn’t cover. Over the 10 years, Apple has continued to push iOS, iPadOS, and macOS forward by removing functionality from core app and adding it elsewhere so you can engage with it without ever launching the app. For example: - 2015 - watchOS: Allows you to interact with your app on a separate device - 2016 - watchOS complications: Allows you to see data from watchOS apps without launching the app. - 2018 - Siri Shortcuts: The ability to take actions in your app through automated “Shortcuts” instead of launching the app. They can also be triggered by voice with Siri. - 2020 - App Clips: Small portions of an app that can be used without installing an entire app. - 2020 - Home screen widgets: Small views on the home screen that displays data from the app without launching it. Brought to the iPad in 2021, to macOS in 2023, and made interactive in 2023 so you can take actions directly from the widget. - 2022 - Live Activities: A short-lived widget that lives in your notification center during a live event, such as a sports match or food delivery. - 2022 - Dynamic Island: Another home for live activities on top of the front-facing camera. It can have many views based on context. - 2022 - Lock screen widgets: Like home screen widgets, but smaller and live on the lock screen. - 2023 - Standby mode: A home for widgets and live activities while your iPhone is charging. - 2024 - Controls: Simple widgets that can appear in the control center, lock screen, and can be triggered by the action button. - 2024 - Apple Intelligence: An AI model that allows you to execute actions in your apps without launching them just by voice. And this isn’t even to mention older features like Now Playing, interactive notifications, SharePlay, iMessage stickers, Apple CarPlay, and more. More and more with each release, Apple users expect the apps they love to be deeply integrated into their operating systems and support all these incredible features. That leaves a Flutter developer working on an iOS app with a few options. For a few of these use cases, the Flutter community has developed rich plugins that allow you to integrate this functionality right into your Flutter app. But for many of them, especially ones with UI components, you’ll have to write native code. In modern native Apple development, that means Swift. Luckily for us, Swift is a wonderful language that is similar to Dart - they are both object oriented, fully managed memory, null aware, strongly typed, and have lots of convenient syntax for getting things done. Swift is also a fully open source language led by a steering committee at [https://swift.org](https://swift.org). Furthermore, all these additional targets support SwiftUI, Apple’s reactive UI framework written in Swift. When comparing the code to Flutter, you’ll be surprised how similar and intuitive they both are. ## Comparing SwiftUI and Flutter If you’re a Flutter developer, you’ll know the basic building block of and UI element is a Widget. The process of building an app is composing Widgets within other Widgets, such as Columns and Rows. These Widgets can also contain state, and can re-render when the state is changed. In Swift UI, the equivalent structure is a View. Views act a lot like Widgets - they can contain state and re-render in response to the state changing, they can be composed together, and there are dozens of built-in Views for building your Views. ```dart class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Text('Hello World'); } } ``` ```swift struct MyView: View { var body: some View { Text("Hello World") } } ``` You might notice that the syntax of Swift has a few weird differences from Dart. First, notice we’re using a struct instead of a class. Swift has classes, too, so what’s the difference? In Swift, an object created from a class is bound to a place in memory with a pointer, just like most object oriented languages. If it is assigned to another variable, it is copied *by referen*c*e*. By contrast, a struct is like a combination of primitive values (like Int, for example). When structs are reassigned, they are copied *by value*; in other words, structs copy all of their values they contain to an entirely new place in memory. You’ll see both classes and structs used in Swift code for different purposes, but for Views, we use struct. In this case, it’s a struct called MyView that extends View. You might also notice the var body: some View and be confused. In Swift, var is usually used to declare *mutable* variables (while let is used to declare *immutable* variables, like final in Dart). However, since this opens into a curly brace, we know this is a getter function. In Swift, type declarations are made after the variable name (just like Typescript), so declaring a variable that contains a string would look like var myVariable: String. The :some View just lets the compiler know that this function should return something that extends View. Finally, notice the lack of return keyword in the body of the getter. In Swift, when there is only one expression in a code block, it assumes you want to return and does it automatically. It is perfectly valid to add return there, but it is mostly omitted since it is optional. Put all together, if we converted this example super closely into Dart, it might look something like. ```dart /// Converting SwiftUI "Hello World" example 1-for-1 to Dart. /// Not valid Flutter code! class MyView extends View { View get body { return Text('Hello world); } } ``` ## State Management For state management, Flutter uses the StatefulWidget for widgets to keep track of properties and re-render when those properties change. SwiftUI has a very similar mechanism by using the @State property decorator. As you can see, there's no separate View component that needs to be labeled stateful as there is in Flutter. Simply modifying a state property is enough to tell the view to rebuild. So where in Flutter you may need to call SetState to render the screen and display the updated value, there are no additional calls to make in SwiftUI. ```dart class MyWidget extends StatefulWidget { // ... } class MyWidgetState extends State { int _count = 0; @override Widget build(BuildContext context) { return Column( children: [ Text('You have clicked the button $_count times'), ElevatedButton( onPressed: () { setState(() { _count += 1; }); }, child: const Text('Increment'), ), ] ) } } ``` ```swift struct MyView: View { @State private var count = 0 var body: some View { VStack { Text("You have clicked the button \(count) times") Button("Increment") { count += 1 } } } } ``` In these examples, you can also see the difference in string interpolation, as well as the VStack view, which is equivalent to the Column widget in Flutter. In fact, there are many Widgets in Flutter that have counterparts in SwiftUI, such as ![__wf_reserved_inherit](/assets/images/blog/why-learn-swiftui-as-a-flutter-developer/body-0.png) You might also notice our VStack doesn’t contain a list of Views, or even any parameters. Heck, it doesn’t look like a constructor is being called at all! This is mostly just syntactic sugar on the part of Swift. VStack takes a function (called content) that returns the content that it should stack vertically, this is called a ViewBuilder. Since it is the only argument in the VStack constructor that accepts a function, it can be split out without any labeling and Swift assumes you want to pass in the content parameter (you can see the same thing with Button as we’re passing in the callback action without any labeling there too). And, since there are no parameters explicitly passed into VStack, you don’t need the parentheses to call the constructor (even though it is being called). If you want to make it more explicit, you totally can. ```swift struct MyView: View { @State private var count var body: some View { VStack( content: { Text("You have clicked the button \(count) times"), Button("Increment") { count += 1 } } ) } } ``` When dealing with Views that can be passed data as well as update data, such as a TextField, SwiftUI uses an object called a Binding. You can think of a Binding as a getter and setter combined into one object. They can easily be created from using @State by just prefixing the variable with $. For example: ```swift struct MyView: View { @State private var textValue = "" var body: some View { TextField("Example text field", text: $textValue), } } ``` This means that any time the user types in the TextField, the variable is updated and anything else using that variable will be rebuilt. Additionally, if any other widget updates that variable, it will be updated inside the TextField. When we need to pass objects down the Widget tree in Flutter, we use the BuildContext . Using InheretedWidget, or packages like Provider, we can access objects like so: ```dart @override Widget build(BuildContext context) { final theme = Theme.of(context); final messagesRepository = context.read(); // ect... } ``` SwiftUI has a very similar mechanism called @Environment. It allows you to send objects into the view hierarchy. ```swift struct MyRootView: View { var body: some View { MyDetailedView() .environment(MessagesRepository()) } } struct MyDetailedView: View { @Environment(MessagesRepository.self) private var messagesRepository var body: some View { Text("detail view") } } ``` This is also used for many build-in items, such as detecting if the app is in light mode or dark mode ```swift struct MyView: View { @Environment(\.colorScheme) var colorScheme var body: some View { Text(colorScheme == .dark ? "In dark mode" : "In light mode") } } ``` ## Modifiers Finally, there is one rather big difference between Flutter and SwiftUI, and that is how Widgets/Views are modified. In Flutter, Widgets are generally customized in their constructors or wrapped with helper Widgets to build a more dynamic UI. For example, if you wanted to create some blue text with some padding around it, you would do something like this. ```dart class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.all(8.0), child: Text( 'Hello World', style: TextStyle( color: Colors.blue, ), ), ); } } ``` In SwiftUI, it looks just a little different. ```swift struct MyView: View { var body: some View { Text("Hello World") .foregroundColor(.blue) .padding(8.0) } } ``` These functions dangling off the Text View are called modifiers, and they are the primary way you customize Views in SwiftUI. You may have noticed before that we used the .environment in our RootView to provide an object in the environment. This is also just another modifier. ## To Wrap it Up Now that we’ve discussed why you might want to learn SwiftUI as a Flutter developer and what the experience could look like, we hope you’ll take away the idea that Flutter and SwiftUI aren’t so different after all. While picking up a new framework can seem daunting, getting started with SwiftUI is a lot easier than it seems, and with just a little bit of SwiftUI added to your existing Flutter app, you can greatly improve the user experience! ### Very Good Ventures Joins FlutterConf Latam 2024 As Silver Sponsor! Source: https://verygood.ventures/blog/very-good-ventures-joins-flutterconf-latam-2024-as-silver-sponsor/ FlutterConf Latam 2024 will take place in Arequipa, Perú, on October 29th-30th, gathering developers, tech leaders, and enthusiasts from across Latin America. The event will feature insightful talks, networking opportunities, and showcases of Flutter’s latest advancements. Very Good Ventures (VGV) will participate as a silver sponsor, offering a booth with swag and presenting talks by some of the unicorn industry experts.  ## What to expect at Flutterconf Latam 2024 We love seeing Flutter events happening around the world, and we do our best to attend and support the community whenever possible. Recently, we joined [Fluttercon USA in NYC](https://verygood.ventures/blog/fluttercon-usa-2024-key-insights-and-learnings/), where we connected with passionate american developers and industry leaders. Last year, we also had the incredible opportunity to attend the [first-ever large-scale Flutter event in Latin America](https://verygood.ventures/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/), held in Medellín, Colombia. This two-day event took place in October 2023, bringing together developers, designers, and enthusiasts from across the region and beyond. It was a fantastic experience filled with insightful talks, talented individuals, and the vibrant spirit that makes the Flutter community special. This month, the VGV team heads to Arequipa, Peru, for [Flutterconf Latam 2024](https://flutterconflatam.dev/en/)—a two-day event packed with workshops, lightning talks, and expert panels. The conference, hosted at the Cerro Juli Convention Center, will dive into the latest in Flutter development and trends within the growing community. But beyond the tech, it’s a perfect chance to explore Arequipa, one of Peru’s most beautiful cities, known for its culture, architecture, and delicious food. The event promises not only valuable learning experiences but also lively networking opportunities featuring contributions from global companies and local Flutter talent. ### Unicorns at Flutterconf Latam Make sure to stop by the VGV booth for exclusive swag and connect with our team. We are always looking to meet Flutter devs and tech enthusiasts, and the team will be all native Spanish speakers, so don’t worry if your English is not *very good*! A key highlight of VGV’s participation will be insightful talks from industry experts, who will take the stage to deliver exciting sessions. These presentations aim to empower developers, foster collaboration, and inspire innovation within the Flutter community. **Matías Leyba**, Engineer at Very Good Ventures, will dive into how Flutter apps can seamlessly connect with native platforms through plugins in his talk **“Plugins: Tu puente hacia el código nativo”.** Attendees get to explore into the inner workings of plugins and discover essential tools for developing their own. The talk will begin with foundational concepts and an overview of the correct architecture for a plugin. As a hands-on component, Matías will walk participants through creating a plugin for both Android and iOS, following best development practices. By the end of the talk, listeners will gain a solid understanding of how Flutter communicates with native platforms, unlocking new ways to extend app functionality. Don’t miss this opportunity to enhance your Flutter skills and deepen your knowledge of the Flutter ecosystem! **Sofía Rey**, Engineer at Very Good Ventures, will take the stage to introduce her talk **“Animaciones a otro nivel: Transforma tu app con Rive”**. Sofía will share how to elevate your Flutter apps with stunning, interactive animations using Rive. In this talk attendees will learn how Rive’s state-based animation capabilities can not only beautify UI but also enhance user experience in a dynamic and functional way. There is a way to simplify complex concepts and make applications more intuitive—without the need for overly complicated workflows. Rive also promotes seamless collaboration between designers and developers, offering powerful tools for both. In this talk, Sofía will dive into what Rive is, how it integrates with Flutter, and walk through a practical example to get you started with this amazing tool in your projects. **Mauricio Miguez**, Engineer at Very Good Ventures, will explore the significance of striving for 100% test coverage in Flutter projects and provide practical strategies for achieving and maintaining this goal in his talk **”Manteniendo el 100% de test coverage: Mitos, Estrategias y Mejores Prácticas”**. Mauricio will discuss how to integrate thorough testing into CI/CD pipelines to ensure consistent and comprehensive coverage over time. This talk will also address common concerns and misconceptions about aiming for full coverage. By debunking myths, it shows how every additional test contributes not only to code quality but also to early bug detection and better software architecture. Get ready to dive into best practices for making tests automated, independent, readable, and fast, with a focus on building effective and valuable unit tests. Also, in this talk the attendees will explore how adopting a disciplined approach to testing can transform development team’s culture, fostering a mindset of quality and shared responsibility. Join this session to discover how achieving and maintaining 100% code coverage is not only possible but also one of the most impactful practices for your Flutter projects. **Marcos Sevilla**, Senior Engineer at Very Good Ventures, will explore the creation of an API for generating Apple Passes using Dart Frog in his session **“Full Stack Dart with Apple PassKit, Dart Frog, and Flutter".** The API will connect with a Flutter app, which consumes the generated passes, offering a full-stack approach with Dart as the only language. The talk demonstrates how to streamline the pass-generation flow by sharing code between backend and frontend, showcasing Dart’s versatility across both layers. Additionally, Marcos will touch on native iOS integration with Apple PassKit, providing insight into how Flutter interacts seamlessly with platform-specific features. Join this session to discover the power of a unified Dart codebase for creating robust cross-platform solutions. ### Plan your visit and don’t miss a thing!  This two-day event is packed with exciting talks you won’t want to miss, featuring insights from top experts and community leaders. Make sure to plan ahead and catch the sessions that catch your interest! Explore the [full event agenda](https://flutterconflatam.dev/en/agenda) here. Find out more about [VGV's participation in events here](https://verygood.ventures/events/), and stay up-to-date by following us on [LinkedIn](https://www.linkedin.com/company/very-good-ventures/) and [X](https://x.com/VGVentures) for the latest news and announcements. We’re always on the lookout for new talent to join our team! If you’re interested, explore our [open roles here](https://verygood.ventures/careers/)—we’d love to connect with potential candidates. We look forward to seeing you there! ### Flutter Glossary: Your Go-To Essential Flutter Reference Terms List Source: https://verygood.ventures/blog/flutter-glossary/ Flutter is highly cost-effective due to its single codebase across platforms, reducing development efforts and accelerating time-to-market. As the top global Flutter consultancy, we at Very Good Ventures (VGV), strive to be a guiding voice in the Flutter community and to share knowledge, best practices, lessons learned, and guidance surrounding all things Flutter.  As a result, VGV has created this glossary of key concepts, terms and acronyms as a resource for those who wish to familiarize themselves with basic Flutter language and terms. If you also want to strengthen your foundational expertise, you can find this glossary as a go-to resource to better understand the framework and its potential. Take a look:  ## A - **Animation**: In Flutter, animations are used to enhance the user interface experience by providing visual feedback. Flutter provides built-in animation widgets and a powerful animation framework to create smooth transitions. - **AppBar**: A material design app bar that can include a title, icons, actions, and more. It's commonly used as the top bar in an app to provide navigation and actions. ![Flutter AppBar widget example with title, icons, and actions](/assets/images/blog/flutter-glossary/body-0.png) ## B - **BLoC:** Design pattern that helps the developer manage the app state and isolates the business logic from the UI layer with a clear separation of concerns, leading to a more maintainable app. The library flutter\_bloc facilitates the implementation of this pattern in Flutter with predefined components to easily manage events and states. - **BuildContext**: A handle to the location of a widget in the widget tree. It is used to obtain references to resources and widgets. - **Builder**: A widget that uses a callback to return a widget, useful for creating widgets based on changing state or conditions. ## C - **Column**: A widget that displays its children in a vertical array. It is commonly used for stacking elements vertically. ![Flutter Column widget displaying children stacked vertically](/assets/images/blog/flutter-glossary/body-1.png) - **Cupertino**: A set of iOS-style widgets provided by Flutter that conform to Apple's Human Interface Guidelines. ## D - **Dart**: The programming language used to write Flutter applications. Dart is optimized for fast apps on any platform. - **Debug Mode**: A mode in Flutter where the app runs with assertions enabled, helping developers identify errors and performance issues. - **Developer Experience**: A core Flutter value proposition, shown by using Dart, support for Hot Reload, the Flutter inspector, the plugin ecosystem, multi-platform capabilities, and more. - **Drawer**: A slide-in menu typically found on the left side of the screen, used for navigation in apps. ![Flutter Drawer widget showing a slide-in navigation menu](/assets/images/blog/flutter-glossary/body-2.png) ## F - **Flutter Engine**: It provides the low-level implementation of Flutter's core API, including graphics (through Impeller on iOS and coming to Android and macOS, and Skia on other platforms). - **FutureBuilder**: A widget that builds its UI based on the result of an asynchronous operation. ## G - **GestureDetector**: A widget that detects gestures such as taps, drags, and swipes. It's used to capture user interactions. - **GridView**: A widget that displays its children in a scrollable 2D array of rows and columns. ![Flutter GridView widget displaying items in a scrollable 2D grid](/assets/images/blog/flutter-glossary/body-3.png) ## H - **Hot Reload**: A feature in Flutter that allows developers to instantly see the changes made to the code without restarting the app. It helps in speeding up the development process by preserving the app state. ## I - **InheritedWidget**: A widget that provides state or data to its descendants, which can be accessed through the BuildContext. ## L - **ListView**: A scrollable list of widgets arranged linearly. It's commonly used for displaying lists of data. ![Flutter ListView widget displaying a scrollable list of items](/assets/images/blog/flutter-glossary/body-4.png) - **LayoutBuilder**: A widget that builds itself based on the parent widget’s constraints. It allows developers to make layout decisions based on the size of the parent. ## M - **MaterialApp**: A convenience widget that wraps several widgets commonly required for material design applications. It typically includes a navigator, theme, and home. - **Material Design**: A design system used by Flutter to create beautiful and consistent UIs. - **Mixin**: A way to reuse a class's code in multiple class hierarchies in Dart. Mixins are used to share a set of methods or properties across multiple classes. - **Multi-Platform**: The ability to write code once and run it on different platforms, such as Android, iOS, Web, Windows, Linux, macOS, and embedded systems. ## N - **Navigator**: A widget used to manage navigation between screens. - **Null Safety**: A feature in Dart that helps prevent null errors by ensuring that variables are not null unless explicitly stated. ## P - **Package**: A piece of code that can be reused and shared across apps. Flutter uses pub.dev to share packages with the community. - **Plugin**: A special kind of package that makes platform-specific functionality available to the app. - **Pubspec.yaml**: The configuration file for a Flutter project that specifies dependencies, assets, fonts, and other settings. - **Provider**: A popular state management solution for Flutter that uses the InheritedWidget mechanism to propagate state changes across the widget tree. ## R - **Reactive programming:** A broader paradigm used in development where the expectation is that the data will change over time and facilitate the propagation of those changes. Flutter uses Streams and StreamBuilders to apply in real-time those changes.  - **Row**: A widget that displays its children in a horizontal array. It's commonly used for arranging elements side by side. ![Flutter Row widget displaying children arranged horizontally](/assets/images/blog/flutter-glossary/body-5.png) - **Route**: A screen or page in a Flutter application that can be navigated using the Navigator. ## S - **Scaffold**: A top-level container that provides a basic visual structure for a material design app, including an app bar, drawer, and floating action button. - **State**: An object that holds information about a widget that may change during its lifecycle. Stateful widgets use state to manage dynamic content. - **State Management**: It refers to the process used to share the application state across different screens. - **StatelessWidget**: A widget that cannot change its state: once built, it remains immutable, and if any internal property needs to be updated the widget itself can't do it.  - **StatefulWidget**: A widget that can change its state over time. It can rebuild itself based on changes to its internal state or external data. - **Stream**: Asynchronous pipeline of data that can emit events allowing you to react in real-time to those.   - **StreamBuilder**: A widget that builds its UI based on the latest value of a stream. ## T - **Testing - Golden:** Used to compare the rendered UI of a widget with an image as a reference. In other words, this ensures that the UI does not change unexpectedly by capturing and comparing screenshots of widgets. - **Testing - Integration:** Code that tests the complete app or large parts of it. This includes UI and the interaction between multiple components in different layers.  - **Testing - Unit:** Code that ensures that a piece of logic works as expected without dependencies on other parts of the app. - **Testing - Widget:**  Code that verifies the behavior and appearance of UI widgets in different scenarios, ensuring they render and function correctly. - **Text**: A widget that displays a string of text with a single style. - **Theme**: A class that controls the visual appearance of the app. It allows for consistent styling across the app with colors, fonts, shapes, and more. ## V - **ValueNotifier**: A class that extends ChangeNotifier and provides a way to notify listeners about changes to a simple value. It is commonly used for simple state management. ## W - **Widget**: The fundamental building block of Flutter UI. Everything in Flutter is a widget, from layout structures to UI controls. Widgets are immutable and describe a part of the user interface. - **Widget Tree**: The hierarchy of widgets that defines the structure of the user interface. The widget tree is built from parent to child widgets. ## Z - **Zero to One**: A phrase used to describe the rapid development process in Flutter, where developers can quickly build functioning prototypes from scratch. **Get involved!** If you enjoyed this blog, feel free to share it with your network! Have suggestions for concepts we should add? Head over to our GitHub page and drop your ideas in the comments. We’d love to hear your feedback! At Very Good Ventures,  we work with global brands to design, build, and scale successful apps, and we’re #1 in Flutter. Our expert team is here to help you maximize your chosen framework by providing [code reviews](https://verygood.ventures/solution/flutter-code-assessment/) that enhance your app and get it production-ready. Additionally, we offer a series of [training remote workshops](https://verygood.ventures/solution/flutter-training/) tailored to teach engineering teams our proven app and UI development practices. You can learn all about our [Open Source Tools, best practices and resources here](https://github.com/VeryGoodOpenSource). If you want to know more about our services, don’t hesitate to [talk to our team](https://verygood.ventures/contact-us/)! ### Supercharging Your Development Team With Dart Frog and AWS Source: https://verygood.ventures/blog/supercharging-your-development-team-with-dart-frog-and-aws/ With its performance and customizability, Dart Frog offers Flutter teams a [great way to speed up development](https://verygood.ventures/blog/seamless-full-stack-development-using-flutter-dart-frog/) by introducing Dart on the backend. At Fluttercon USA 2024, we showed how Dart Frog can integrate smoothly with AWS resources to build a full-stack cloud solution that maintains clear separation of concerns between frontend and backend while speeding up development for Engineering teams.  ## Why AWS? [AWS Amplify](https://docs.amplify.aws/flutter/) offers a quick and easy way to get a Flutter app running with Amazon services. Since upgrading to [Gen 2](https://aws.amazon.com/about-aws/whats-new/2024/05/aws-amplify-gen-2-available/), Amplify has used a code-first approach to defining backends. Developers define resources in Typescript files, which the  [AWS Cloud Development Kit](https://aws.amazon.com/cdk/) (CDK) uses to generate and provision cloud infrastructure. [Sandbox environments](https://docs.amplify.aws/flutter/deploy-and-host/sandbox-environments/setup/) offer isolated services that developers can use in pre-production environments to speed up development, and hosting can be connected directly to a project repository for easy deployment.  While it is possible to connect Amplify directly to a Flutter app, you can also take advantage of [several pure Dart packages](https://pub.dev/packages?q=amplify+dart) the Amplify team has created to connect Amplify directly to a Dart Frog backend. Additionally, the [AWS V4 Signer](https://pub.dev/packages/aws_signature_v4) (also published by the Amplify team) allows connection to virtually any other AWS Resource not supported by an existing Dart package. ## Amplify & Dart Frog are better together Integrating Amplify with Dart Frog allows for cleaner separation of concerns between frontend and backend responsibilities. Dart Frog acts as an interface layer between the Flutter application and AWS resources, managing traditional backend concerns like data access/manipulation, domain logic, and caching, so that the frontend is only responsible for fetching and displaying data and reacting to user inputs. All interaction between the application and AWS comes on the server side, where traffic can be more easily controlled, which makes apps more secure. ![Architecture diagram showing Flutter frontend communicating with Dart Frog backend and AWS services](/assets/images/blog/supercharging-your-development-team-with-dart-frog-and-aws/body-0.png) Overall this [Backend for Frontend](https://learn.microsoft.com/en-us/azure/architecture/patterns/backends-for-frontends) architecture setup is a great way to move as much business logic as possible to your backend. This also allows for quick and easy bug fixes or feature additions to your product that don’t always have to go through the long and sometimes frustrating process of mobile app store review. This puts the power of deployment back into your team's hands and utilizes a language that they are already writing code in daily. ## Example: Fluttercon Schedule App To help demonstrate how teams can take advantage of Dart Frog and AWS Amplify, we created a simple demo of the 2024 Fluttercon USA schedule. The demo is [currently live](https://main.d26gw3imxd1pmt.amplifyapp.com/), and the source code is available [here](https://github.com/VGVentures/fluttercon_usa_2024_dart_frog_aws/tree/main). We used Amplify to provision AWS resources for our Dart Frog backend to consume. These resources are defined inside of an Amplify npm package, nested within our Dart Frog app. By default, a new amplify project creates resource files for data access and for authentication. The [data access resource](https://docs.amplify.aws/flutter/build-a-backend/data/set-up-data/) provides a schema where we can define our app entities and the relationships between them. The [auth resource](https://docs.amplify.aws/flutter/build-a-backend/auth/set-up-auth/), meanwhile, defines how users should authenticate to our app. Amplify then uses the typescript resource files to generate AWS resources in the cloud: data tables (these are [DynamoDb](https://aws.amazon.com/dynamodb/) tables by default, but Amplify is compatible with [other databases](https://docs.amplify.aws/flutter/build-a-backend/data/connect-to-existing-data-sources/connect-postgres-mysql-database/)) for data, and [Amazon Cognito](https://aws.amazon.com/cognito/) user pools for auth. Our Dart Frog app manages user authentication and data access for the frontend. We validate access tokens fetched from Amazon Cognito using [Dart Frog authentication](https://dart-frog.dev/tutorials/authentication/). We fetch data from our Dynamo tables and construct our presentation models to be returned from our endpoints. We also set up a framework for caching data to limit calls to cloud resources. While our caching solution stores data locally on the server, this approach could easily be configured to connect to a cloud caching service, whether [Amazon ElastiCache](https://aws.amazon.com/elasticache/) or something different.  Putting all of this authentication and data logic in Dart Frog makes our frontend lean and simple. Here is the pubspec for the frontend app, demonstrating how few dependencies are required when Dart Frog handles the heavy lifting: ![Lean frontend pubspec.yaml with minimal dependencies thanks to Dart Frog backend](/assets/images/blog/supercharging-your-development-team-with-dart-frog-and-aws/body-1.png) The developer experience is also great when creating this solution. In addition to the powerful hot reload that speeds up our Dart development on both frontend and backend, Amplify sandbox resources are automatically re-provisioned when changes are made to typescript configuration files. When the schema needs to be updated, no messy data migration is necessary–just update the data resource in typescript, and the changes will be deployed to the cloud.  When it comes time to deploy the app, Amplify offers options to cater to different teams’ preferences. [Amplify hosting](https://docs.aws.amazon.com/amplify/latest/userguide/welcome.html) provides a platform for hosting serverless web apps with continuous deployment based on Git repositories. Dart Frog, meanwhile, can be deployed in a Docker container to several different cloud services, including [AWS App Runner](https://dart-frog.dev/deploy/aws-app-runner/). Since Amplify can be accessed via the [AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/amplify/), we chose to deploy this app via a [Github Actions workflow](https://github.com/VGVentures/fluttercon_usa_2024_dart_frog_aws/blob/main/.github/workflows/deploy.yaml) that builds the Dart Frog solution in Docker and deploys it to App Runner, before triggering a build of the frontend on Amplify. This workflow enables branch-based deployment of the entire stack, with the API url automatically supplied to the frontend based on the name of the feature branch. This is just one example of how to integrate Dart Frog with AWS resources. Both Dart Frog and Amplify are highly customizable based on your product and team’s requirements. The approach adopted in making this example app uses our scalable best practices to accommodate more complex backends, including different databases, more sophisticated authentication, and additional backend services such as subscription hubs and scheduled tasks.  ## Let us help you get started with Dart Frog & AWS! If you found this article helpful, don’t hesitate to share. We would love to hear about Dart being used on any backend, so contact us if you want to share your story as well. If you and your team need help understanding these topics, feel free to [reach out](https://verygood.ventures/contact-us/)!  [ Watch the full session recording here](https://www.droidcon.com/2024/10/17/supercharging-development-with-full-stack-dart-and-aws/) ### Very Good Growth: Meet the New Leadership Driving VGV’s Go-To-Market Source: https://verygood.ventures/blog/very-good-growth-meet-the-new-leadership-unicorns-driving-vgvs-go-to-market/ As we enter the year's final quarter at Very Good Ventures (VGV), we're excited to share new initiatives planned for the remaining months and prepare for an exciting 2025. We've come a long way in our journey, but there's still much more ahead as we pursue our mission of building successful experiences for all. Today, we’re announcing a major milestone for our team — the addition of three new executives in Strategy, Business Development, and Marketing, positioning us for even greater growth and success.** ** ## Shaping The Future: Fresh Strategic Insights, Creative Marketing and World-Class Business Development ### Chelsea Pritchard, Director of Marketing **Chelsea** joins the VGV team with extensive marketing leadership experience and a key focus on understanding client needs and driving business outcomes. She’s successfully led creative direction, brand management, and marketing strategies across diverse industries, including digital agencies, technology start-ups, nonprofits, and financial services. In addition, her background in driving marketing and branding initiatives for companies such as MasonHub, GasBuddy, and Wayfair—reinforced by her expertise in creative, digital, and content strategies—will strengthen VGV’s service offerings, enhancing VGV’s brand visibility, go-to-market messaging and driving strategic marketing initiatives. This will bring a valuable dimension to our team, ensuring we continue to meet our client’s unique needs with even more significant impact. ### Billy Fischer, EVP Business Development We are excited to welcome **Billy** to the team to lead our Business Development. With extensive experience in growth and sales leadership across various companies, Billy has successfully driven digital product consulting, digital marketing, and commercial growth. His deep experience at Fueled, WillowTree, and Dynamit, where he has led sales and marketing efforts, makes him a perfect fit for VGV. Billy will lead business development efforts, forging new partnerships, and expand VGV’s market reach. ### Nate Wootten, Chief Strategy Officer **Nate** joins VGV to lead and elevate VGV’s strategic planning, positioning, pursuit process, and overall growth initiatives. Additionally, Nate will be driving the evolution of our strategy consulting service, helping clients better understand not just "what" they should build, but "why"—articulating value-focused solutions from the outset. Nate has over 10 years of experience at WillowTree , progressing through various roles, including Partner & VP of Commercial Strategy. He has a strong background in leading digital innovation, product strategy, and UX design, working with cross-functional teams to deliver client solutions. > *“Adding Billy, Nate, and Chelsea to the team represents a massive leap forward for VGV. These three individuals are at the top of their fields, proven leaders, and difference makers. Most importantly, they’re fantastic people who will help drive the exceptional culture that has been so core to VGV’s growth. With these new leaders, we’re reshaping VGV’s strategy, sales, and marketing capabilities so that we can continue to grow at the rapid pace we’ve grown accustomed to at VGV.”* - *David DeRemer, Founder and CEO of Very Good Ventures* At VGV, we have lofty goals for our company as well as for the impact we want to have on our clients and our ecosystem. These new leadership hires are indicators not only of our success so far, but also of the Flutter ecosystem. We have a lot of work ahead in pursuit of our mission, and Billy, Nate, And Chelsea are key to our future story and our ability to help the best and most innovative companies in the world succeed with their technology. Please join us in welcoming these professional unicorns today.  To learn more, you can read the official [PR released](https://www.prnewswire.com/news-releases/very-good-ventures-welcomes-key-leaders-to-shape-future-302273310.html?tc=eml_cleartime) or take a look at their LinkedIn profiles: [Chelsea Pritchard](https://www.linkedin.com/in/cprcreative/), [Nate Wootten](https://www.linkedin.com/in/nate-wootten/) & [Billy Fischer ](https://www.linkedin.com/in/billyfischer)We are looking forward to seeing what the team will achieve with these new teammates onboard! Welcome to the team Chelsea, Billy and Nate! ### Fluttercon USA 2024: Key Insights and Learnings Source: https://verygood.ventures/blog/fluttercon-usa-2024-key-insights-and-learnings/ Fluttercon USA 2024 marked the first and largest Fluttercon event in the United States, bringing together key figures from the community, industry leaders, and top developers. The event buzzed with energy as passionate Flutter enthusiasts gathered to network and showcase the latest innovations. The event welcomed strong teams and professionals like Google, Betterment, Eric Seidel, among others, who added immense value to the event with their expertise and insights into the future of Flutter. With influential speakers, engaging sessions, and a vibrant atmosphere, Fluttercon USA 2024 reaffirmed its position as a must-attend event for anyone in the Flutter community.  ## VGV’s commitment to the Flutter community The Very Good Ventures (VGV) team was thrilled to have a strong presence at Fluttercon USA, with a diverse team representing Engineering, HR & People, Business Development & Sales, and Product and Program teams. ![__wf_reserved_inherit](/assets/images/blog/fluttercon-usa-2024-key-insights-and-learnings/body-0.jpeg) *VGV crew in NYC* This time, VGV was focused on reaffirming its commitment to the Flutter community: As a primary call to action, David DeRemer, Founder & CEO at Very Good Ventures, proposed to begin a more productive dialogue to set objectives for the community to undertake in order to further Flutter's influence in his keynote [“Flutter's Future: A Call For Community-Driven Commercial Roadmap.”](https://verygood.ventures/blog/flutters-future-a-call-for-community-driven-commercial-roadmap/) David emphasized the need for a collaborative approach to shape Flutter’s economic growth and announced that VGV has open-sourced its Flutter Commercial Roadmap for 2024-2025 to inspire community discussion and collaboration. [You can watch the full keynote recording here.](https://www.droidcon.com/2024/10/17/build-to-succeed-with-flutter-a-commercial-roadmap-for-the-community/) Another exciting news was sharing [VGV’s new Very Good Engineering website](https://verygood.ventures/blog/introducing-very-good-engineering/), which aims to empower Engineering teams with knowledge and tools to succeed. VGV has been and will continue to support the community through open-source contributions, forums, and comprehensive resources.  Additionally, several VGV team members had the chance to take the stage and share their expertise on various Flutter topics, focusing on the latest developments in Flutter and Dart:  **David DeRemer and Sam Moore**, VP of Architecture at Betterment, shared the story of Betterment's successful transition to Flutter using the add-to-app approach in the “Successful Flutter Re-Platforming — The Path For Betterment” session. They covered the entire journey, from building a business case and validating Flutter to executing a detailed plan and navigating a cultural shift.  [Watch the full session recording here.](https://www.droidcon.com/2024/10/17/successful-flutter-re-platforming-the-path-for-betterment/) **Jorge Coca**, VGV's Head of Engineering, dove into optimizing Flutter and Dart performance. He emphasized the importance of mastering DevTools to transform underperforming apps into top-tier experiences, highlighting that effective performance tuning is crucial for maintaining reputation and releasing with confidence. [Watch the full session recording here.](https://www.droidcon.com/2024/10/17/demystifying-performance-tune-up-in-flutter-and-dart/) **Kyle Finlinson**, Senior Engineer at VGV, provided a deep dive into the implications of using Flutter with WebAssembly (Wasm). He explained how Wasm could revolutionize software creation and consumption and shared actionable insights for Flutter developers to get started. [Watch the full session recording here. ](https://www.droidcon.com/2024/10/17/implications-of-flutter-wasm/) **Marcus Twichel**, Engineering Lead at VGV, demonstrated how Dart Frog and HTMX can simplify web development, allowing Flutter developers to build interactive websites without complex JavaScript frameworks. He illustrated how these tools make creating modern web solutions both easy and efficient. [Watch the full session recording here.](https://www.droidcon.com/2024/10/17/htmx-dart-a-good-idea/) **Tom Arra and Stefan Hodges-Kluck**, Director of Product Development & Engineer at VGV respectively,  explored the integration of full-stack Dart with AWS services. They demonstrated how combining Dart and Dart Frog with AWS Amplify accelerates development, offering speed and flexibility for building robust full-stack applications. [Watch the full session recording here.](https://www.droidcon.com/2024/10/17/supercharging-development-with-full-stack-dart-and-aws/) Last but not least, **Samantha O'Brien and Marcus Twichel**, Engineers at VGV, showcased how incorporating SwiftUI into Flutter apps can enhance user experiences across the Apple ecosystem. They demonstrated ways to extend app functionality beyond the main interface, utilizing features like Siri and Apple Watch to create more dynamic interactions. [Watch the full session recording here.](https://www.droidcon.com/2024/10/17/swiftui-for-flutter-developers/) If you want to learn more about the sessions and keynotes at Fluttercon USA, stay tuned for the Fluttercon session recordings and related topic content coming soon! ## Some impressions from the team The VGV team returned from the event with a wealth of ideas and insights. Here are some of their key takeaways: > “It was amazing to see so much passion for Flutter right here in the USA. We met so many people using Flutter to build up their businesses, from food and beverage, and e-commerce. We also got to have great conversations with people building new startups centered around making Flutter development easier, like Shorebird, FlutterFlow, Serverpod, and Celest. It was great to exchange ideas with all these people and inject new ideas into the community.” - Marcus Twichel. > > "With this being the first big Flutter-focused event since 2020, it was the first time a lot of attendees got a chance to talk face to face with people that we have been working with over the past number of years. That combined with the energy and enthusiasm that we saw as people were attending the sessions really encourages me that our community is still growing and I’m happy that I was able to be a small part of that overall. Can’t wait for the next event to see some new faces and companies get involved.” - Tom Arra > “So many people are working on so many interesting things! It was great to meet some of them in person and be inspired to dig into building the future.” - Kyle Finlinson > “Attending my first Fluttercon was an absolute whirlwind, but what stood out the most wasn't the tech itself - it was the incredible sense of community. Everywhere I looked, people were connecting, sharing their passion for Flutter, and supporting each other. From the lively hallway conversations to the mirrored, kaleidoscopic talks, it was clear that Fluttercon was about more than just code; it was a gathering of like-minded individuals who believe in the power of collaboration. It's a community that celebrates innovation, encourages experimentation, and most importantly, attempts to foster a sense of belonging. Fluttercon reinforced my belief that building great software is about more than skill; it's about coming together, sharing a common passion, and lifting each other up.” - Jared Olmsted > > “It was impressive to see the breadth of attendees from large companies using Flutter (e.g., Headspace, SoFi, Comcast, Amica, Nestle, etc.) at Fluttercon NYC. Meeting them illustrated the reach of the community as well as the importance of these types of events to bringing more people and companies into the community to engage with each other.” - David DeRemer ## Stay tuned for more We plan to share more Fluttercon USA 2024 content, including sessions and keynote recordings, blogs and webinars about key topics discussed. To stay in the loop, [sign up for our monthly newsletter](https://verygood.ventures/#newsletter) and never miss out on updates like these and more about Flutter & Engineering! ### Pub in Focus: The Most Critical Dart & Flutter Packages of 2024 Source: https://verygood.ventures/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/ The Dart ecosystem has more than 55,000 published packages in [pub.dev](http://pub.dev). Given its growth over the last few years, understanding what packages are the most critical in the ecosystem helps in having productive conversations about the dependencies in your product. These dependencies tend to allow you to move fast by not building everything from scratch. However, it's crucial to ensure you're using packages that are actively maintained and not outdated. We’ve gathered criticality information from packages in [pub.dev](http://pub.dev) and the [Open Source Security Foundation (OpenSSF) database](https://commondatastorage.googleapis.com/ossf-criticality-score/index.html), to bring attention to Dart packages, developers, organizations, communities and projects in 2024. ## What is a criticality score? In 2020, Google under the [OpenSSF](https://openssf.org/) (OpenSSF) [released](https://opensource.googleblog.com/2020/12/finding-critical-open-source-projects.html) a metric named “criticality score”. The aim was to identify those influential and important projects, with a number between 0 (least important) and 1 (most important). The score is based on an [algorithm](https://github.com/ossf/criticality_score/blob/main/Quantifying_criticality_algorithm.pdf) designed by [Rob Pike](https://github.com/robpike), and it considers contributors count, commit frequency and more to derive the final score of a project. ![__wf_reserved_inherit](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-0.jpeg) *Distribution showing the criticality scores of GitHub repositories hosting pub packages, with a peak frequency of 5750 close to a 0.2 criticality.* ###### The criticality score tool measures repository criticality, not a package. We’ve only analyzed those [pub.dev](http://pub.dev/) packages that are correctly associated with a GitHub repository and Dart repositories collected by the [Open SSF database](https://commondatastorage.googleapis.com/ossf-criticality-score/index.html). ## Why should we care about critical packages? Knowing about critical packages in the ecosystem helps us to have productive conversations about our dependencies. The more critical a package is the more important its maintenance becomes in the ecosystem. We depend on the collective effort to ensure critical dependencies remain updated and healthy. For example, when looking at the [OSV Vulnerabilities Database](https://osv.dev/list?q=&ecosystem=Pub), there are only nine vulnerable packages hosted in Pub. This either means Dart developers always write [very good](https://verygood.ventures/) code, or there is a lack of vulnerabilities being reported.  Consider the [archive](https://pub.dev/packages/archive) package, it has a high criticality score of around 0.58. A [vulnerability](https://osv.dev/vulnerability/GHSA-9v85-q87q-g4vg) was [reported](https://github.com/brendan-duncan/archive/issues/265) by [Mohamed Benchikh](https://github.com/BlueSquare1), a community member, and fixed by its maintainer Brendan Duncan. In all, it is important we notify maintainers about issues and vulnerabilities in their packages to ensure we collectively enjoy a healthier and secure Dart ecosystem; specially for those with high criticality scores. In addition, some OSS projects are struggling for the time, resources, and the attention they need to be maintained. By identifying the critical packages in the ecosystem we connect critical open source projects we all rely on with organizations that can provide them with adequate support (as suggested by the [Google Open Source Blog regarding Finding Critical Open Source Projects](https://opensource.googleblog.com/2020/12/finding-critical-open-source-projects.html)). We have taken the time to do the analysis of the ecosystem and have broken it down in the following charts focused on independent publishers, organization publishers, and open source projects. ## Most critical independent Pub publishers There are many developers that independently maintain critical Pub packages. This said, all of the packages below receive the help of external contributors, in aggregate they sum up to over 1500 contributors. Thanks to this incredible collective effort these packages are being used by more than 1 million developers! ![Top ten independent pub.dev developers ranked by total criticality score](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-1.png) *Top ten list of pub.dev developers with the largest summation of all their Dart packages’ criticality scores. Shown as a segmented bar graph with: felangel (2.87), rrousselgit (2.25), shamblett (2.05), kevmoo (1.98), fzyzcjy (1.46), simolus3 (1.44), DavBfr (1.26), Frezyx (1.22), atn832 (1.14) and lastly ryanheise (1.05).* Only the repositories that are sufficiently critical (above 0.35) contribute to the total score; hence avoiding developers with numerous uncritical packages from being outliers. ![Top ten independently owned pub.dev packages by criticality score](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-2.png) *Top ten list of pub.dev packages owned by an independent developer. Shown as bar graph with: flutter_rust_bridge (0.65), riverpod (0.64), bloc (0.60), freezed (0.60), drift (0.59) flutter_inappwebview (0.59), archive (0.58), get (0.58), chopper (0.56) and lastly file_picker (0.56).* ## Most critical organization publishers We’re incredibly excited to see more organizations joining the Dart & Flutter ecosystem. While we are lucky enough to be near the top of the chart, other organizations including service providers and product companies are also building for the community! ![__wf_reserved_inherit](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-3.png) *Top ten list of pub.dev organizations with the largest summation of all their Dart packages’ criticality scores. Shown as a segmented bar graph with: atsign-foundation (2.91), VeryGoodOpenSource (2.88), invertase (2.18), onepub-dev (2.07), Baseflow (1.93), GetStream (1.46), SimformSolutionsPvtLtd (1.33), apivideo (1.16), getsentry (1.09) and lastly surfstudio (1.06).* ###### Google has been excluded from the above graphic. Google itself has a total score beyond 50 (aggregate of Dart, Flutter, Firebase, and other Google services). They are by far the most critical organization in Pub! ![__wf_reserved_inherit](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-4.png) *Top ten list of pub.dev packages owned by an organization. Shown as  bar graph with: sentry (0.63), stream_video_flutter (0.60), flutter-permission-handler (0.58), patrol (0.57), melos (0.57), supabase (0.57), amplify_flutter (0.56),  flutter-geolocator (0.56), flutter_stripe (0.56), and lastly at_server (0.56).* ## Most critical community led publishers Besides developers and organizations, there are communities of developers that collectively aim to improve the Dart & Flutter ecosystem. They also deserve a shoutout for their valuable collaborative contributions! ![__wf_reserved_inherit](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-5.png) *Top five list of pub.dev packages owned by a community of developers. Shown as bar graph with: plus_plugins (0.62), flame (0.62), dio (0.60), audioplayers (0.57) and lastly photo_manager (0.52).* ## Most critical open source projects The [Open SSF database](https://commondatastorage.googleapis.com/ossf-criticality-score/index.html) also collects information about open source projects that are not published in Pub. Since we’re at it, we’ve crunched the data about those applications with the highest criticality scores. In aggregate all these projects sum up to more than 178k GitHub stars, more than those from the Flutter repository! ![Top eleven Dart open source projects ranked by criticality score](/assets/images/blog/pub-in-focus-the-most-critical-dart-flutter-packages-of-2024/body-6.png) *Top eleven list of Dart open source projects with the highest criticality scores. Shown as a bar graph with: ente (0.64), appflowy (0.64), fluffychat (0.60), spotube (0.57), cakewallet (0.56), revanced-manager (0.56), smooth-app (0.55), localsend (0.54), hiddify-next (0.54), lichess (0.53) and lastly kitchenowl (0.53).* ## Shaping the future of Pub The number of packages published in Pub has been growing. There are around ten thousand more published packages since we looked at the [distribution of licenses used in Pub packages](https://verygood.ventures/blog/very-good-cli-license-checker/) around a year ago! This has been largely possible thanks to the thriving Dart & Flutter community. As more offerings are available we should also aim to make better decisions about the introduction and longevity of the dependencies of our products.  We expect developers to meticulously evaluate new dependencies and call out when it’s time to replace a package or offer assistance to its maintenance. Evaluating the criticality scores of such dependencies helps us do so, but we should not forget it is a metric that might not unveil the entire story of the dependency, its maintenance and existence. We encourage developers to collaborate with the dependencies they rely on; just opening an issue is a massive help! When doing so on packages with high criticality the impact on the ecosystem is larger; nonetheless, low criticality repositories might be highly critical in the future! ###### Very Good Ventures has more than fifteen published packages in Pub, learn about all our tools at [vgv.dev](https://verygood.ventures/resources/open-source-packages/)! You can also check out our [GitHub](https://github.com/VeryGoodOpenSource) to get involved and discover all our best practices and tools! ## Reaching out If you found this article helpful, don’t hesitate to share. If you would like to reach out or discuss this topic feel free to reach out personally to Alejandro via [LinkedIn](https://www.linkedin.com/in/alejandro-santiago-44259b159/). If you and your team need help in understanding the criticality score of your dependencies feel free to [reach out](https://www.linkedin.com/in/alejandro-santiago-44259b159/) so we can help! ## *Written by Alejandro Santiago, illustrated by Luciano Bologna, revised by Tom Arra on September 2024* ### Flutter's Future: A Call For Community-Driven Commercial Roadmap Source: https://verygood.ventures/blog/flutters-future-a-call-for-community-driven-commercial-roadmap/ On September 20, 2024, I was honored to deliver a keynote address to the attendees of Fluttercon USA in New York City. As someone who has been involved in Flutter since its early days, my life has been radically altered by the emergence of this great technology. The objective of the presentation was to inspire the community to align our priorities and efforts to lend our collective strength to driving Flutter’s growth. As a primary call to action to the community, I propose that we begin more productive dialogue to set objectives for the community via a community-driven commercial roadmap. ![David DeRemer delivering keynote at Fluttercon USA 2024 in New York City](/assets/images/blog/flutters-future-a-call-for-community-driven-commercial-roadmap/body-0.jpeg) ## Keynote Recap The Flutter community has achieved remarkable success, but there's still untapped potential waiting to be unlocked. To truly thrive, we must move beyond the notion that Flutter's fate rests solely with Google. It's time to embrace a community-driven approach, where we collectively shape the future of this powerful framework. One persistent concern that hinders Flutter's broader adoption is the fear that Google might abandon it. We strongly believe this fear is unfounded, especially given Google's significant investment and the widespread use of Flutter within their own products. Nonetheless, it's up to us, the community, to dispel this notion by lending our collective strength to complement Google’s efforts. We must showcase Flutter's strength, independence, and the vastness of its impact. To guide our collective efforts and ensure Flutter's continued growth, we need a clear roadmap for the ongoing development of Flutter’s economic growth. We need a "Commercial Roadmap," focusing on the business aspects of the Flutter ecosystem, that will complement the existing technical **Flutter project roadmap** and outline strategic initiatives to foster community engagement, drive market adoption, and increase commercial outcomes. At Very Good Ventures, we're aiming to start this conversation by providing an example to illustrate how companies can directly influence Flutter’s commercial success. We are open-sourcing our own [Flutter Commercial Roadmap for 2024-2025](https://github.com/VGVentures/flutter-commercial-roadmap). This roadmap serves as a starting point for community discussions, highlighting our priorities and inviting others to share their own. ![VGV Flutter Commercial Roadmap 2024-2025 with four key goals](/assets/images/blog/flutters-future-a-call-for-community-driven-commercial-roadmap/body-1.jpeg) ## Our roadmap is centered around four key goals: 1. **Community:** We're committed to fostering a stronger, more engaged community. This involves organizing forums, championing the development of a community-driven commercial roadmap, and actively seeking feedback on our own initiatives. 2. **Confidence:** We want to empower new Flutter teams with the knowledge and tools they need to succeed. We'll continue investing in open-source software, engage in community forums to offer guidance, and provide comprehensive resources like our "[Very Good Engineering](https://engineering.verygood.ventures/)" website. 3. **Commerce:** We recognize the importance of corporate involvement in the Flutter ecosystem. We'll encourage our clients to share their success stories, offer pro bono marketing support to large enterprises, and actively sponsor Flutter conferences. 4. **Capability:** We're dedicated to showcasing Flutter's full potential as a multi-platform framework. We'll prioritize client projects that demonstrate novel use cases, invest in content highlighting non-mobile applications, and develop open-source demos that push the boundaries of what's possible with Flutter. The future of Flutter is in our hands. It's time to step up, share our priorities, contribute to open-source projects, and collaborate on the development of a shared community roadmap. Together, we can make Flutter the "everywhere framework" and ensure its continued growth and success for years to come. ## How to Get Involved 1. Express your interest and intent in getting involved in the development of a community-driven commercial roadmap. [Contact us](https://verygood.ventures/contact-us/) or comment on [this first issue](https://github.com/VGVentures/flutter-commercial-roadmap/issues/1). 2. Create and share your own set of priorities or roadmap. Ours can be found here: [https://github.com/VGVentures/flutter-commercial-roadmap](https://github.com/VGVentures/flutter-commercial-roadmap) 3. [Contact us](https://verygood.ventures/contact-us/) if you’re interested in discussing or collaborating on any of the items in our roadmap. 4. [Watch the full keynote recording here!](https://www.droidcon.com/2024/10/17/build-to-succeed-with-flutter-a-commercial-roadmap-for-the-community/)  5. If you are a large enterprise working with Flutter interested in pro bono marketing support to tell your Flutter story, please [get in touch](https://verygood.ventures/contact-us/). ### The T3 Engineering Leadership Summit 2024 Builds a Strong Case for the #unconference Source: https://verygood.ventures/blog/the-t3-engineering-leadership-summit-2024-builds-a-strong-case-for-the-unconference/ The T3 Engineering Leadership Summit builds a powerful case for the #unconference format and the need for a strong return to in-person discussion and collaboration among peers spanning diverse organizations and roles. On September 17, 2024, VGV co-sponsored the T3 Engineering Leadership Summit in NYC along with Betterment, AppDome, Touchlab. The event was organized by the team behind Droidcon and Fluttercon, and was described as “where senior technical leaders managing software development teams can network, exchange ideas, share problems and learn from their peers.” Importantly, the event was invite-only with a guest list oriented to include senior technology and engineering leaders. ## An #unconference When the organizers first explained the proposed format to me, I was leery — the idea was to run multiple concurrent moderated roundtable discussions, where all of the attendees would be active participants. Most conferences feature 1-to-many presentations or moderated panels on stage that set the role of the audience as a pure consumer. In this #unconference format, there were no speakers and all attendees were expected to participate in the discussion. I hadn’t experienced a format like this, and based on my past conference experiences I was uncertain if the engagement would be there. Most importantly, I was concerned people may not decide to attend or would not derive enough value without specific speakers delivering polished presentations about specific topics. I was very pleasantly surprised! ### The Invite-Only & Senior Focus First, because the event was invite-only it meant that the attendee list was well curated. Additionally, invite-only events tend to be more network and referral driven. These aspects helped ensure that the attendees were well informed about the nature of the event and were more likely to be strong contributors to the conversation. Additionally, the focus on people in more senior leadership roles meant that the attendees all had a lot of experience and confidence in their points of view. They also were generally comfortable speaking in front of groups and sharing their ideas collaboratively. These factors combined such that, in the groups I was a part of, there was always a lively and active discussion. Smaller groups tended to be more intimate and larger groups tended to be a bit more loose, so there were some differences. But overall, the discussion was varied and lively. ### Open & Flexible Conversation A big factor I hadn’t considered in an #unconference format is that that lack of clear narrative and structure is one of the value-adds. In a typical conference format, the presentation narrative is locked and you’re along for the ride whether you value the content or not. In the open discussion format at T3, each table had a theme and moderators were there to navigate the time effectively. But, the conversation was allowed to flow and develop naturally based on the input and interest areas of the attendees. I found this fascinating as it allowed us to go deep in some areas while completely skipping over other areas. If I had been the presenter, I would have been guessing about what mattered to the audience; but in this format we could pivot to the most engaging areas. Perhaps most importantly, the content was varied and infused with the experiences and points of view of a lot of different people. This was fun and exciting because we could identify areas of consistent alignment as well as find divergences and constructively challenge ideas as they arose. ### Built-In Icebreaking One of the things I hate about most conferences is the inefficient use of time between talks for networking. Generally, I’m more interested in the people attending the conference than I am in any particular talk. However, we’re generally left to mill about and strike up conversations with random people that may or may not be mutually relevant connections. In the #unconference format you start discussions with other attendees naturally as part of the event. So by the end of the day, you’ve already been talking with a ton of people at the event. This meant that the talking never ceased even in the spaces between sessions. The flow of discussion was natural and the awkwardness of striking up a new conversation was eliminated. In fact, the conversation never stopped in between sessions as attendees just kept talking. ## Reflections on Cross-Platform Development in the Age of Engineering Efficiency Throughout the day there were 3 concurrent moderated roundtables with different focus areas. I was a moderator for one called “Cross-Platform Development in the Age of Engineering Efficiency”, and I was pleased to have a large and engaged group. We had people deeply experienced with KMP, React Native, Flutter, and native software development; so we were able to cover the topic from many angles. ### Cross-Platform is no longer risky or a compromise Perhaps my biggest takeaway from this session was the feeling that cross-platform solutions are no longer seen (at least by this group) as a compromised or second-class solution compared to Native. Sure, Native is generally seen as “closest to the metal” but it comes with major organization costs and management issues. On the flip side, cross-platform has some limitations and considerations (as all technologies do) but the efficiencies cannot be ignored. The key insight was that all of these tools (React Native, Flutter, KMP) have matured and improved significantly since the early days. Cross-platform in general suffers from a stigma that they are somehow inferior or majorly limited, but from the perspective of this group, those stigmas are based on old data that is no longer true. The question is how many engineers and companies are ignoring the benefits of cross-platform based on experiences they had 5 years ago or more, and meanwhile these technologies have improved significantly. ### Cross-Platform is a natural progression to no-platform One of the most interesting insights for me that emerged was the feeling that in general technology is progressing to where engineers can work on any platform. Cross-platform (e.g., React Native) enables an engineer to work on both iOS and Android platforms. Multi-platform (e.g., Flutter) enables an engineer to work on mobile, desktop, linux, and many other platforms. As these tools, AI, and other sources of productivity continue to develop, engineers will have more and more tools at their disposal to contribute to any platform. **In other words, in the long run engineers may reorient from platform-specific focuses to being purely focused on product wherever that product needs to be.** Additionally, we discuss how even the native platforms are converging around similar architectures and patterns. There is more similarity between Jetpack Compose, Swift UI, and Flutter than most people realize or talk about. Learning one of those tools is a step in the right direction to learning the others. That didn’t used to be the case in mobile development. What that means is that developers learning these tools are becoming more portable and adaptive. ## You should consider attending the next T3 Summit too!  In the end, I had a great day and the feedback I heard from others was resoundingly positive. Given that this was a brand new event with a different kind of format, I think it was a big success. We had 40-50 attendees, which exceeded our expectations, from a mix of major companies (e.g., Norwegian Cruise Lines, Peloton, Blade, Meta, eBay, Betterment, NY Times, and many more) as well as variety of consultancies (e.g., VGV, Accenture, Blue Orange, LeanCode, Eskridge, and more). Most importantly, the discussions were engaging and the open format created more opportunities to meet other attendees than any other conference I’ve ever attended. So I’d definitely recommended following @T3 Software Engineering Leadership Summit and considering attending their next event. ### Introducing Very Good Engineering Source: https://verygood.ventures/blog/introducing-very-good-engineering/ We’re excited to introduce [Very Good Engineering](https://engineering.verygood.ventures/), a new website for Flutter engineers that provides access to our technical articles, demos, and open source projects. After years of advocating for high quality software and [Very Good Architecture](https://verygood.ventures/blog/very-good-flutter-architecture/), we’ve been compiling all of our secrets and are eager to share them with the wider Flutter community. Very Good Engineering is a result of numerous contributors compiling many man-years worth of technical expertise and knowledge from across our company, including all of the learnings from our many client projects and open source tools, apps, and demos. Currently, there are over 20 technical articles, with many more on the way. ![__wf_reserved_inherit](/assets/images/blog/introducing-very-good-engineering/body-0.png) ## Embracing the Flutter community As part of an ongoing effort, we hope that Very Good Engineering will be a valuable resource for our own engineers, as well as anyone who wishes to build high quality Flutter applications and games. In the spirit of collaboration, the entire Very Good Engineering is an open source [Astro](https://astro.build/) + [Starlight](https://starlight.astro.build/) static documentation website available on GitHub. We welcome community discussions, issues, and contributions, as well as tips and suggestions for the latest technical innovations and breakthroughs in cross-platform development. ![__wf_reserved_inherit](/assets/images/blog/introducing-very-good-engineering/body-1.png) ## All of our open source projects…in one place We’ve integrated the GitHub API into our build process, allowing us to include GitHub project cards on our landing page that point to our open source tools and projects. Over the years, we’ve built a number of production-grade tools that we’ve shared with the community, including [Very Good CLI](https://cli.vgv.dev/) and [Dart Frog](https://dart-frog.dev/), as well as numerous other packages to streamline development. ![__wf_reserved_inherit](/assets/images/blog/introducing-very-good-engineering/body-2.png) ## Oh, and three more things… As part of Very Good Engineering, we’re introducing *three* (3!) entirely new open source apps that you can view right in the web browser. Like all of our projects, these conform to our architectural best practices and have 100% unit test code coverage. Each demo showcases a number of unique, custom-rendered widgets, as well as an underlying simulation of some sort to demonstrate how the layers of a sophisticated application come together. ### Airplane Entertainment System ![__wf_reserved_inherit](/assets/images/blog/introducing-very-good-engineering/body-3.png) Our new [Airplane Entertainment System](https://vgventures.github.io/airplane_entertainment_system/) demo showcases dynamic weather changes with custom-rendered clouds, simulated flight tracking, and animated transitions with a working music player, complete with a fake visualizer effect. We hope you sit back, relax, and enjoy the flight! When you’re finished, you can check out [how it works](https://github.com/VGVentures/airplane_entertainment_system) — or just read the technical breakdown guide we put together for it. ![__wf_reserved_inherit](/assets/images/blog/introducing-very-good-engineering/body-4.gif) ### Financial Dashboard The [Financial Dashboard](https://vgventures.github.io/financial_dashboard/) project demonstrates three in-app themes that can be dynamically changed, as well as animated charts and numeric indicator widgets. ![Financial Dashboard demo showing animated charts and theme switching](/assets/images/blog/introducing-very-good-engineering/body-5.png) If you’re feeling lucky, you can pull-to-randomize your retirement funds! Once you get some numbers you like, take a look at the [GitHub repository](https://github.com/VGVentures/financial_dashboard) or read the technical breakdown guide. ![Financial Dashboard pull-to-randomize animation updating retirement funds](/assets/images/blog/introducing-very-good-engineering/body-6.gif) ### Vehicle Cockpit Finally, you can feel the thrill of a physically-based vehicle acceleration model without actually having to steer! Our new [Vehicle Cockpit](https://vgventures.github.io/vehicle_cockpit/) demo simulates the motion of a car around a track, all while showing off a custom-rendered dashboard, including a realistic tachometer gauge and speedometer. ![Vehicle Cockpit demo with custom tachometer gauge and speedometer](/assets/images/blog/introducing-very-good-engineering/body-7.png) After enjoying the single input simulation, you can take a peek under the hood to see [how it works](https://github.com/VGVentures/vehicle_cockpit) (or, once again, read the technical breakdown guide).  ![Vehicle Cockpit simulation animating car acceleration around a track](/assets/images/blog/introducing-very-good-engineering/body-8.gif) ## Very Good Engineering…for everybody If you’re ready to amp up your Flutter game, we invite you to take a look at [Very Good Engineering](https://engineering.verygood.ventures/), its demos, and our open source projects. As we build out our technical knowledge base, we welcome community contributions and invite you to discuss Flutter app best practices on GitHub with us. If reading through all those technical articles seems a bit much, don’t worry — we can bring our knowledge and expertise to [your app project](https://verygood.ventures/contact-us/) to help you ship faster (and safer). ### We Are Sponsors of the T3 Summit in NYC Source: https://verygood.ventures/blog/we-are-sponsors-of-the-t3-software-engineering-leadership-summit-in-nyc/ We're thrilled to co-host and sponsor the T3 Software Engineering Leadership Summit in New York City on September 18, 2024. This invite-only event is a unique opportunity for senior technical leaders and directors in software engineering to connect, learn, and share insights with their peers. ## About the Event [T3 Summit NYC](https://www.t3-summit.com/t3-nyc) is designed specifically for senior technical leaders managing software development teams, including department heads, VPs, Directors, and CTOs. This summit is hosted by the Fluttercon USA team and will focus on the latest trends, challenges, and opportunities impacting engineering **teams**, **technology**, and **tools** (**T3**). It's an ideal setting for networking, exchanging ideas, sharing solutions, and learning innovative solutions from others navigating similar challenges. This year the event is being hosted by [Betterment](https://verygood.ventures/success-stories/betterment/) at their office in Hudson Yards. We had the pleasure of collaborating with them on an exciting project to enhance their mobile experience. The format of the T3 Summit includes a series of roundtable discussions, where attendees will break out into smaller, moderated groups (10-15 participants) to delve into specific themes. This informal setup encourages lively discussions and provides valuable insights that attendees can later use as actionable tools, solutions and insights to implement within their teams! **​** ## What to Expect from T3 Summit NYC If you are in the area of NYC this September, make sure to apply for your opportunity to participate in this intimate expert-led roundtables exchange to learn the latest trends and strategies for mobile tech to elevate your leadership. You will have exclusive access to network with top-tier peers and industry leaders in a collaborative environment. Roundtable topics this year will focus on:  - **Cross-Platform Development in the Age of Engineering Efficiency** - **AI Assistance & Mobile Developer Productivity** - **Mobile Experience & Performance: The CTO's Guide to Effective Mobile** - **Observability** - **Ensuring Mobile App Security in Rapid-Release World** - **Reducing Technical Debt: A Guide for CTOs in 2024** - **Developing an Engineering Culture and Effecting Change** ## The VGV Approach September 2024 is set to be an exciting month, particularly during [Fluttercon USA](https://verygood.ventures/blog/fluttercon-usa-2024-key-insights-and-learnings/) on the 19th and 20th. Our team will be connecting with community leaders, clients, partners, and Flutter enthusiasts throughout the week. The T3 Summit will be a perfect opportunity to engage with more industry leaders. The VGV team will attend various events throughout the week, but at the T3 Summit, guests will have the chance to connect with David DeRemer, Founder & CEO of Very Good Ventures, and Jorge Coca, Head of Engineering at Very Good Ventures who will both be roundtable moderators, along with several other industry leaders. They will be sharing valuable insights during the roundtable discussions and contributing to the conversation on the latest trends and best practices in the industry.  David will be leading the “Cross-Platform Development in the Age of Engineering Efficiency” roundtable discussion and he anticipates: “We’ll have a lively discussion about the opportunities and challenges faced in digital product software engineering, and the potential for more efficient solutions. Cross-platform tools have a long history, good and bad, and it will be insightful and interesting to evaluate the role and impact of these tools for the next decade.” Jorge will be also leading another discussion around “[How to Reduce Technical Debt - A Guide for CTOs in 2024](https://www-t3-summit-com.filesusr.com/html/6e1ab2_4482af0737a930ccab6bdd5d1f890efe.html#)”. He shared: “Addressing technical debt effectively is crucial to enabling innovation and fast-paced development. I expect a very insightful conversation among tech leaders on how to balance progress and maintenance and the role of different AI techniques and tools to reduce tech debt faster than ever!” ## How to get an invite If you're interested in attending, please use the code **TL-T3SUMMIT** to apply for an invitation [here](https://lnkd.in/dZiVtipZ). Seats are limited, so register soon for your chance to receive an invitation! To find out more about the T3 Engineering Leadership Summit and see the full agenda, visit [T3 Summit NYC](https://www.t3-summit.com/t3-nyc). We hope to see you in New York City and look forward to an engaging and insightful event filled with learning, collaboration, and innovation! ### Build to Succeed: A Podcast to Inspire Creating Digital Products That Win Source: https://verygood.ventures/blog/build-to-succeed-a-podcast-to-inspire-creating-digital-products-that-win/ We have released our new podcast series, *"Build to Succeed,"* where we dive deep into the world of tech product development with guests, who as experienced leaders, have shaped the digital and business landscape bringing ideas to life and nurtured them into long-term successes. This series is designed for the community, offering valuable insights into what it takes to turn ideas into successful products for longevity while optimizing for business goals. ## ‍Building “The right thing, the right way” The [*"Build to Succeed"* podcast](https://verygood.ventures/podcasts/) isn’t just about the tech—it’s about the strategy and mindset needed to thrive in today’s fast-paced business environment. Our conversations delve into the delicate balance between speed and resourcefulness, emphasizing the importance of crafting products built to last while meeting business objectives. Through these discussions, listeners will learn — from real people who have experienced building successful products firsthand — how to navigate the journey from launch to long-term growth, making informed decisions that lead to repeated success. The podcast series will feature guests from a variety of industries and career paths with experience and proof that can help you explore the world of building successful tech products.  ## The person who Built the Thing that Helped us Build our Thing Our [inaugural episode](https://verygood.ventures/podcasts/harnessing-innovation-to-shape-the-future-of-cross-platform-development/) features an inspiring conversation with [Eric Seidel](https://www.linkedin.com/in/ericseidel/), Founder of [Shorebird](https://www.linkedin.com/company/shorebirddev/), Co-Founder of [Flutter](https://www.linkedin.com/showcase/flutterdevofficial/) and former Director of Engineering at [Google](https://www.linkedin.com/company/google/). Eric has a wealth of experience, having transitioned from working on WebKit and Chrome at Google to pioneering Flutter, the framework that has revolutionized how we build digital experiences. In this episode, Eric shares his journey and the lessons he’s learned along the way. He talks about the power of focusing on solving significant problems and creating tools that people genuinely need. Eric also highlights the importance of eliminating redundancy in development processes to save time and resources. Listeners will also gain insights into some of the challenges and mistakes made with Flutter, the crucial concept of founder-market fit, and how embracing failure can drive innovation. ## Explore the World of Building Successful Tech Products Our podcast series is designed to be a go-to source for learning from the best in the industry. We have planned very good episodes coming next. Tune in, get inspired, and discover the strategies that will help you build digital products that truly win! ### Very Good Keynotes & Sessions at Fluttercon USA 2024 Source: https://verygood.ventures/blog/very-good-keynotes-sessions-at-fluttercon-usa-2024/ ## We are excited to announce that seven of our teammates will be taking the stage at Fluttercon USA on September 19th and 20th, delivering seven talks, including a keynote on building for success with Flutter. The main Flutter event of the year, [Fluttercon USA](https://flutterconusa.dev/), is coming to New York City for the first time. This is a special homecoming for Very Good Ventures (VGV), as our journey with Flutter began here in the Big Apple when we developed the first Flutter app outside of Google for the Hamilton Broadway Show. We are welcoming the Flutter community back to where it all started. This year, Fluttercon, co-located with [Droidcon NYC](https://nyc.droidcon.com/), will bring together developers, enthusiasts, and the entire Flutter community like never before. American companies and leading teams, including the Google team, will be in attendance, making it an extraordinary gathering.As [Gold Sponsors](https://flutterconusa.dev/sponsors/) at Fluttercon, we will enjoy having attendees over at our booth, where they can meet our team and learn about VGV’s work and last contributions to the Flutter ecosystem. ## A Sneak Pick into VGV’s Talks at Fluttercon USA The event will feature keynotes, sessions, lightning talks, panel discussions, and workshops from influential individuals in the Flutter community who will share their knowledge and findings about Flutter’s growth and upcoming features. This time, [15 of the 57 listed speakers so far are VGV teammates or alumni](https://flutterconusa.dev/speakers/), and we couldn't be prouder! Here you can find out about the main topics VGV’s team will bring to the event: [**David DeRemer**](https://flutterconusa.dev/speaker/david-deremer/), Founder & CEO at Very Good Ventures, will present a keynote about how to Build to Succeed with Flutter. In this opportunity, David will share a commercial roadmap for the community with Flutter’s economic growth, bringing VGV’s enterprise Flutter expertise and sharing an actionable vision to align the community for long-term success. David highlighted: **“The Flutter team at Google has always helped guide the community with its product roadmap. Now, we think it’s time for the community to start building its own commercial roadmap.”** Later added: **“At Fluttercon USA 2024 we hope to establish a clear vision for the ecosystem to grow and flourish”.** Later the same day, on Friday 20th, [**David and Sam Moore**](https://flutterconusa.dev/speaker/david-deremer-speaker-2/), VP of Architecture at Betterment, will share the success story of how the [**VGV team helped Betterment transition to Flutter**](https://verygood.ventures/webinars/maximizing-your-business-value-with-flutter/) using add-to-app for seamless ongoing development. In their session, you’ll discover how Betterment built a business case, validated Flutter, executed a comprehensive plan, and navigated a culture shift, all while uncovering the opportunities and potential risks along the way.  [**Jorge Coca**](https://flutterconusa.dev/speaker/jorge-coca/), Head of Engineering at Very Good Ventures, will present his session about “Demystifying performance tune up in Flutter and Dart”. Jorge will share how to master DevTools, often underused and intimidating, to transform underperforming apps into excelling experiences. Jorge explained: **“One of the biggest nightmares that organizations and dev teams share is that, after months of development, their execution might lead to a non-optimized performance experience. It directly affects their reputation. Knowing how to effectively use the tools that allow you to tune up these experiences is critical to release with confidence.”** [**Kyle Finlinson**](https://flutterconusa.dev/speaker/kyle-finlinson/), Senior Engineer at Very Good Ventures, will share a deep dive into “Implications of Flutter WASM”, to discover best practices, benefits, and key implementation considerations for using WASM. Kyle added: **"If you're like me, you've heard rumblings about WASM for years but haven't fully understood why it's such a big deal or what you should do about it. I'm excited to share how WASM will change the way we create and consume software along with actionable things Flutter developers can do now."** [**Marcus Twichel**](https://flutterconusa.dev/speaker/marcus-twichel/), Engineering Lead at Very Good Ventures, will go through how Dart Frog and its power can help Flutter developers seamlessly leverage their existing skills to build full-fledged web solutions using HTMX. Marcus will demonstrate how HTMX and Dart Frog can simplify web development by creating engaging websites. He shared: **”HTMX has challenged the dominant belief that modern web development requires complex Javascript frameworks. Combined with the simplicity of Dart Frog, we’ll see how incredibly easy it is to build a modern interactive website and remember why we love the web.”** [**Tom Arra**](https://flutterconusa.dev/speaker/tom-arra/) & [**Stefan Hodges-Kluck**](https://flutterconusa.dev/speaker/stefan-hodges-kluck/), Director of Product Development & Engineer at Very Good Ventures respectively, will present  “Supercharging Development with Full Stack Dart and AWS.” This session will demonstrate how full stack Dart projects work with AWS services, whether one wants to create a robust full stack application from scratch or connect an existing cloud infrastructure. Tom and Stefan will explore integrating Dart and Dart Frog with AWS using Amplify, demonstrating the speed and flexibility of this stack. Tom explained: **“Having backend Dart as part of your tech stack can really accelerate your product development process while also building it for future growth. Join us to hear how we have seen this in action and learn how you can leverage AWS services to do the same for your team”.** [**Samantha O'Brien**](https://flutterconusa.dev/speaker/samantha-obrien/) & [**Marcus Twichel**](https://flutterconusa.dev/speaker/marcus-twichel/), Engineer & Engineering Lead at Very Good Ventures, will demonstrate how to incorporate SwiftUI and add unique iOS features to the apps to create more dynamic and useful user experiences across the Apple ecosystem. In this session, attendees will learn how to incorporate SwiftUI and add unique iOS features to the apps to create more dynamic and useful user experiences across the Apple ecosystem. Marcus highlighted: **“Modern iPhone users expect their apps to extend beyond the confines of the app, whether on the home screen, Siri, or on their Apple Watch. Does your Flutter app meet them where they are? Come see how adding a pinch of native development can make your app the most interactive it can be.”** ## Get Updates on All Things Fluttercon USA #### #### Follow our social media channels for the latest updates on our activities, talks, and news related to Fluttercon USA. We will be sharing content around the keynote and sessions for those who can’t attend the event. You can find the event's full agenda [**here**](https://flutterconusa.dev/agenda/). ### Very Good Ventures Shining Bright at Fluttercon USA 2024 Source: https://verygood.ventures/blog/very-good-ventures-shining-bright-at-fluttercon-usa-2024/ For the first time in the US, the top Flutter community event, Fluttercon, will be held in New York City on September 19th and 20th. This milestone event carries significant meaning for many reasons, where we’ll be thrilled to share what’s coming for the Flutter community and Very Good Ventures (VGV). Flutter and VGV have a long-standing history and a bright future ahead. This time, we will have the opportunity to celebrate the amazing achievements we have all reached using this life-changing framework in the heart of the community. Our commitment to innovation and excellence continues to drive us forward, matching our presence and impact within the Flutter ecosystem. The VGV team will be in attendance at [Fluttercon USA](https://flutterconusa.dev/) as Gold sponsors. Meet our team at our booth, see our talks and presentations, and join us to celebrate Flutter’s incredible growth. This is not just another Flutter event, it will bring together the Flutter community like never before, and here’s why: ## Where It All Started This year, we are bringing Flutter to NYC, celebrating a technology and community that have profoundly impacted the global tech industry. For VGV, this event holds special significance. We began the Flutter revolution, showing what’s possible to build with this framework, right in New York City. In 2017, we started designing, building, and delivering [the first commercial Flutter app for the Broadway musical Hamilton](https://verygood.ventures/success-stories/hamilton/) before starting one of (if not *the*) first consultancies to fully embrace Flutter. We have numerous activities and surprises planned during the event — it’s going to be a big celebration, and we hope you can join us! ## The People This is THE big Flutter event and is sure to be a celebration of the wild growth and progress that Flutter and its community have made over the last seven years. Flutter developers from around the world will converge at Fluttercon in New York where attendees will get the opportunity to listen to top tech speaker sessions and workshops while connecting with more enthusiastic Flutter developers. Fluttercon USA promises to be an unparalleled gathering due to the high concentration of American companies and engineers, something that was missing in most previous Flutter events held outside the US. Additionally, major companies in the community, including the Google team and many others, will be there. ## Very Good Keynotes & Sessions We’re excited to share our knowledge and insights through different sessions and keynotes selected to take the stage at Fluttercon USA. I will be honored to introduce the audience to my Keynote: [**Build to Succeed with Flutter — A commercial roadmap for the community**](https://flutterconusa.dev/speaker/david-deremer/). I will be presenting a commercial roadmap for Flutter’s economic growth, bringing VGV’s enterprise Flutter expertise and sharing an actionable vision to align the community for long-term success. With this keynote, attendees will learn how, together, we can generate revenue, drive business strategy, and create new opportunities within the Flutter ecosystem. I hope this talk will inspire and guide the many teams committed to Flutter’s future success. Sam Moore, VP of Architecture at Betterment, and I will also present [**Successful Flutter Re-Platforming—The Path For Betterment**](https://flutterconusa.dev/speaker/david-deremer-speaker-2/), where we’ll explore the transformative journey of re-platforming their native mobile app to Flutter. A success story filled with incredible opportunities but also significant challenges, walking the audience through how to use add-to-app for seamless ongoing development and providing valuable insights on how Betterment built a compelling business case, validated Flutter, executed a comprehensive plan, and managed a cultural shift. Other sessions provided by VGV’s team will also take place at Fluttercon USA. From mastering DevTools to learning how to transform underperforming apps, understanding the best practices and benefits of using Flutter WASM, and discovering how Dart Frog and HTMX can simplify web development. Additionally, VGV’s team will demonstrate how to create robust full-stack applications with Dart and AWS, and show how to incorporate SwiftUI to add unique iOS features to Flutter apps. These sessions will provide valuable insights and practical knowledge to enhance attendees’ development skills. Explore each session in detail and gain insights from our speakers on what to expect from this major event, including key takeaways and learning opportunities. ## Gold Sponsors We’ve been a part of Flutter’s history from the beginning, leading the community and supporting it with open-source tools whenever possible. As [Gold sponsors](https://flutterconusa.dev/sponsors/), we’re excited to share more about our work and latest news at our booth on the 1st floor. Make sure to stop by to meet our expert team for networking, fun activities, assessments, VGV swag, and special surprises for the community. You can learn about our new releases and how they can enhance your Flutter projects. ## ‍Fluttercon + Droidcon Event This year, one ticket grants access to two conferences: When you buy a ticket for Fluttercon USA, you are automatically registered to attend the co-located [Droidcon NYC](https://nyc.droidcon.com/). You’ll be able to explore the latest in Android and multiplatform services, experiencing the best of both worlds. ## ‍More to Come We’re thrilled to connect with colleagues, partners, clients, and the developer community during these exciting days. [Learn more](https://verygood.ventures/blog/fluttercon-usa-2024-key-insights-and-learnings/) about our event agenda and follow us on social media for updates. If you can’t make it to the event and talks, don’t worry! We got you covered: Stay tuned for expanded content about all Fluttercon details. We hope to see you there! ### How we developed a scalable, incredibly fast crossword generator Source: https://verygood.ventures/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/ For this year’s Google IO, we built a [Crossword game](https://crossword.withgoogle.com) and we had to experiment generating crosswords, with different layouts and sizes, to be displayed in the game. Our goal is to create crossword puzzles from a list of words. We wanted to develop both asymmetrical and symmetrical boards to come up with an appealing layout to play with. What sets our approach apart is its efficiency. Whether you're creating a small puzzle or one with over 80k words, our crossword generation allows for quick and efficient generation for symmetrical and asymmetrical boards. ## How do we represent the crossword? An important starting point is to know how we want to represent the puzzle in our generator. In our approach we represent the crossword as a matrix with coordinates (x, y) where each character fills a position.  Consider the following crossword: ![Crossword grid showing words URL, BOUND, and KILOS with coordinate positions](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-0.png) If we define the center (0, 0) where the R of the word ‘URL’ is, we would represent it as:: ```dart (0, -1) -> character 'U' and words ['bound', 'url'] (0, 0) -> character 'R' and words ['url'] (0, 1) -> character 'L' and words [kilos, 'url'] (-2, -1) -> character 'B' and words ['Bound'] (-1, -1) -> character 'O' and words ['Bound'] (1, -1) -> character 'N' and words ['Bound'] (2, -1) -> character 'D' and words ['Bound'] (-2, 1) -> character 'K' and words ['kilos'] (-1, 1) -> character 'I' and words ['kilos'] (1, 1) -> character 'O' and words ['kilos'] (2, 1) -> character 'S' and words ['kilos'] ``` Overall, this approach will help us: to obtain the character at any desired location, the words that are passing through a position, ensuring words are connected, checking for overlapping words and deriving word constraints. ## Understanding word connections, overlaps and constraints The Crossword class stores the words that are placed so far, and the bounds, if any, that define the maximum board size as a rectangle. In addition, the class has methods that provide information about what adding a new word into the existing board entails, these are: 1. Check that a word is **connected** to the board. Adding the word "SUN" at (2, -2) would be connected: ```dart -2 -1 0 1 2 -2 A L B U S -1 - - E - U 0 - - H - N 1 - - A - - 2 - - N - - ``` However, adding the word "SUN" at (-2, 0) would not be connected: ```dart -2 -1 0 1 2 -2 A L B U S -1 - - E - - 0 S - H - - 1 U - A - - 2 N - N - - ``` 2. No **overlapping** of words. Overlapping a word means that adding such an entry would change an existing word or overwrite it completely. Adding the word "USA" at (-5, -2) would overlap with "ALBUS": ```dart -5 -4 -3 -2 -1 0 1 2 -2 U S A A L B U S -1 - - - - - E - - 0 - - - - - H - - 1 - - - - - A - - 2 - - - - - N - - ``` However, adding the word "SUN" at (2, -2) would not overlap: ```dart -2 -1 0 1 2 -2 A L B U S -1 - - E - U 0 - - H - N 1 - - A - - 2 - - N - - ``` 3. Get the **constraints** of a position on the board. In this next example we are going to evaluate the position (2, -2). We have three constraints, one at position 0 for the character 'S', another at position 4 for the character 'W' and invalid length of a word with 4 characters. Constraints might also enforce invalid lengths, since otherwise a word may overlap, invalidating the crossword. Bigger and smaller words can be added, but not the exact size marked as invalid. ```dart -2 -1 0 1 2 -2 A L B U S -1 - - E - - 0 - - H - - 1 - - A - - 2 - - N O W ``` ## Sorting the words When generating a crossword, one of the most crucial steps is finding and placing words that meet the requirements of a crossword quickly.  A slow search can significantly increase the time it takes to complete the generation. To optimize the search process, the pool of words had to be organized into a data structure that makes the lookup of fitting words faster.  Given the list of words 'Aunt', 'Addy', 'Adds' and 'Away' will structure and sort them as: ```dart final map2 = >>> { 4: { 0: { 'a': {'Adds', 'Addy', 'Aunt', 'Away'}, }, 1: { 'u': {'Aunt'}, 'd': {'Adds', 'Addy'}, 'w': {'Away'}, }, 2: { 'n': {'Aunt'}, 'd': {'Adds', 'Addy'}, 'a': {'Away'}, }, 3: { 't': {'Aunt'}, 'y': {'Addy', 'Away'}, 's': {'Adds'}, }, }, }; ``` As you may notice, some words appear multiple times. Although it increases storage, this sacrifice boosts the search performance. Now that the words are sorted, we can search for a word efficiently with the given constraints: ```dart // If the only requirement is to have the character starting with "a": words[4][0]['a'].first; // Aunt // If we need a word starts with "a" and has another "a" in its second position: words[4][0]['a'].firstWhere((word) => word[2] == 'a'); // Away ``` To showcase the increase in search performance by using this sorting system, we can see the difference in time when [searching for 5 words](https://gist.github.com/AyadLaouissi/0e46839b3d0e68400721e7151f73e724) with the different methods: ![Benchmark comparing sorted vs unsorted word search performance](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-1.png) We can notice that the speed is constant in the sorted words. The bigger the list the more noticeable the performance will be, due to their [asymptotic time complexities](https://en.wikipedia.org/wiki/Asymptotic_computational_complexity). ## Generating the crossword board Now that we have a clear understanding of how we structured the initial pool of words, how we are representing the position of the words in the board and the data model that will hold all this information, it is time to discuss how each word is placed in the crossword. Our approach starts with a **seeding** phase. We select one of the largests words and place it on the board. Then we **populate** the board by evaluating if any of its characters allows a new word to be placed, and we continue **resolving**, **adding** and **expanding** the words iteratively until there are no candidates left. ![Crossword generation phases: seeding, populating, resolving, and expanding](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-2.png) ### How do we pick a spot to expand the board? **Expanding** refers to the process of introducing a new word into the crossword. There are different algorithms we can use to determine what location should be expanded next, for example: [Depth-First Search (DFS)](https://en.wikipedia.org/wiki/Depth-first_search) or [Breadth-First Search (BFS)](https://en.wikipedia.org/wiki/Breadth-first_search). We followed the BFS approach to expand the crossword, expanding a word first entirely before continuing to the next word. ![Breadth-first search expanding the crossword word by word](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-3.gif) In order to optimize this process we store all the candidates that we check to place a new word. By doing this, we avoid reevaluating the same position multiple times. ### Adding a word The crossword is **populated** iteratively from the starting point. The process continues until there are no candidates or all the words are placed. When searching for a word to be placed in the crossword, the strategy is to add the word with the largest length that meets the given constraints. This simple heuristic aims to increase the probabilities of finding words that can connect, since the longer the word, the more characters can be considered during expansion. During the adding process, we perform the following steps: 1. The word is added to the crossword. 2. The word is removed from the pool of available words to prevent it from being used again. 3. Add new candidates from the new word. For diversified crossovers, when adding new candidate locations, we don’t just add the locations that a word spans through, but we also add those neighboring locations: ![New candidate locations added from neighboring positions of placed words](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-4.gif) ## Asymmetrical Generator In an asymmetrical crossword board, we are placing the words iteratively from the top left of the board to the bottom right. We place the initial word in the top left corner as a strategic choice because we expand from left to right and from top to bottom. The steps to place a word from a candidate position are as follows: 1. Check the constraints of the candidate. 2. Search for the first word that matches the constraints. 3. Place the word. In the following image we can see that after adding the seed, we add its possible candidates, and after that we continue with the second placed word ‘identical’ iteratively until we don’t have candidates or words left. ![Asymmetrical crossword generation growing from top-left iteratively](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-5.gif) ## Symmetrical Generator In the symmetrical crossword puzzle generator, we populate the puzzle with a horizontal line of symmetry. This means that the layout of words on the top half of the board mirrors the layout on the bottom half. The initial word needs to be placed strategically to make it grow across the area bounded by the board whilst keeping the symmetry. The initial seed word must have an odd length, so that it crosses the line of symmetry and respects the symmetry, by aligning the middle character with the line of symmetry. In addition, the seed word is placed at the left of the bound in the x coordinate; ensuring that the crossword expands to the right.  When growing a symmetrical crossword, there are two scenarios to consider when adding a new word: it can either cross the line of symmetry, or we have to find another word with the same length to fit in its reflected position. #### Crossing the Line of Symmetry When adding a single word that crosses the line of symmetry, we have to follow the same approach as the initial seeded word. It must have an odd length to allow for the middle character of the word to align with the line of symmetry. In practice, such scenarios are rare, depending on the word pool, finding a word with a single valid length and multiple letter constraints is unlikely to succeed.  #### Mirrored Placement When we find one word on the bottom half of the board, we have to find a word of the same length on the top half of the board. If we can’t find a word with that length we will retry with a smaller length until we find a suitable match for both halves or until there are no suitable words. This ensures that every word added has a mirrored equivalent, maintaining the symmetry. ![Symmetrical crossword generation with mirrored word placement](/assets/images/blog/how-we-developed-a-scalable-incredibly-fast-crossword-generator/body-6.gif) ## Conclusion Now that we have a crossword generator we can start efficiently creating boards of any size that are symmetrical or asymmetrical by specifying a list of words. You can also create your own crossword generator with different types of symmetry extending the CrosswordGenerator class. Check out the complete [open-source project](https://github.com/VGVentures/io_crossword) and the [crossword generator package](https://github.com/VGVentures/io_crossword/tree/main/packages/board_generator)! ### How we built it: Creating the I/O Crossword puzzle, powered by AI Source: https://verygood.ventures/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/ [*This article was originally published on Flutter Medium.*](https://medium.com/flutter/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai-2210e39b04b9) To celebrate this year’s IO launches, Very Good Ventures partnered with the Flutter and Firebase teams at Google to create a unique, digital experience to showcase the power of the Gemini API. The fun (and challenge) of crossword puzzles is solving the clues to complete the board. A natural place to start for words and clues was everything we’ve talked about at IO this year. To generate the game content, we gave videos of the keynotes to [Gemini Advanced](https://gemini.google.com/) and asked it to create a list of topical technology-related words and clues to make playing the game a fun way to learn about everything announced this year at IO. Keep reading to learn how we built the UI with Flutter and check out the open-source code for the game, which you can access on [GitHub](https://github.com/VGVentures/io_crossword). ## How to play When you log in to the puzzle, you are prompted to choose your team. As you correctly answer a clue, the word fills in and the cells change to your team color. Your team earns points for every word you solve and you earn a bonus for a streak of solving words without asking for hints. Need a hint? Click the **Hint** button, then ask up to ten yes-or-no questions about the concealed word. The [Gemini API](https://ai.google.dev/) privately responds to  your questions so you can fill in more words and help your team claim Victory!  ![I/O Crossword game board with team colors and hint button](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-0.png) The design of the *hint* feature is a great example of how different technologies can work together to solve a problem. Hints are powered by [Firebase Genkit](https://firebase.google.com/docs/genkit), a new framework for AI development announced at IO, and are deployed as a Firebase Function. The network request to the API includes the answer to the clue, so to keep the response hidden from the client in the message data, we built a simple Dart backend using the [Dart Frog package](https://pub.dev/packages/dart_frog) to make the experience more robust.  The Frog back end calls Genkit to get the hint and retrieves the answer from the database. That way players can’t simply inspect the network call to figure out the answers to the game. You can read more about how the Genkit flow was built in the [Firebase deep dive blog](https://firebase.blog/posts/2024/06/ai-powered-crossword-genkit). ![Firebase Genkit hint flow architecture with Dart Frog backend](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-1.png) ## Rendering a performant crossword board with Flutter The board is one of the core pieces of the game. We optimized player movement on the puzzle to ensure good performance and make the best user experience for players.  We considered two options to build the board: straight Flutter or leverage the [Flame](https://flame-engine.org/) game engine. The most compelling feature of Flame for this game is its camera API, which allows the mascots to easily move and supports zoom controls. However, since that was really the only feature of Flame we would be using, we ultimately decided that using a full game engine like Flame would be overkill for this scenario.  Looking for an alternative solution, we explored the [InteractiveViewer](https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html) widget, which allows rendering the words in a canvas of a custom size, and moving the mascots around freely with matrix transformations. InteractiveViewer was a simpler solution for our needs with less dependencies and load, which better allowed us to showcase the flexibility and performance of Flutter. ## The power and flexibility of the InteractiveViewer The InteractiveViewer has built-in zoom gestures, but we wanted to add more intuitive buttons for the desktop experience. Leveraging the matrix transformations, we implemented the zoom controls by first calculating the change in scale and updating the new viewport with the center as a constant reference point: ![InteractiveViewer zoom control code calculating scale and viewport center](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-2.png) Once we have the new tentative viewport, we have to ensure that it fits within the boundaries of the board. We can have two scenarios that we must allow for: when the viewport is bigger than the board, or when it’s positioned out of bounds. We update both the change in scale and the translation of the viewport to ensure it fits within the boundaries by tweaking the zoom level or the location of the viewport, as shown in the following code: ![Viewport boundary clamping code for board edge constraints](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-3.png) Finally, calculate the transformation and apply it to the InteractiveViewer controller: ![Matrix transformation applied to the InteractiveViewer controller](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-4.png) With this code, we’ve extended the zoom controls of the InteractiveViewer and transformed the viewport according to our needs. ## Boosting performance with WebAssembly in Flutter WebAssembly support for Flutter web apps was one of the major announcements for the Flutter community this year at IO. With players across the world playing the game at the same time, performance was a critical factor. We leveraged [WebAssembly (Wasm)](https://docs.flutter.dev/platform-integration/web/wasm), built into Flutter, to reduce performance bottlenecks and maintain a smooth frame rate, particularly when it comes to the character and board animations in the game.  ![__wf_reserved_inherit](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-5.gif) ## Dart Backend and Cloud Run: Seamless code sharing between backend and frontend To ensure a smooth gaming experience for all users, our Dart backend is built with the [Dart Frog package](https://pub.dev/packages/dart_frog) and is hosted on [Google Cloud Run,](https://dart-frog.dev/deploy/google-cloud-run/) to leverage the auto scaling capabilities. This helps to maintain optimal performance regardless of the active number of players as the game makes multiple calls, such as every time a user is created, or when a player submits a word or asks for a hint. Using a backend to confirm the answers to each clue also allowed us to safeguard the crossword puzzle and prevent cheating. Specifically, the app reads information with the Flutter Firestore SDK but the database *only* allows changes from the Dart back end. This also allowed for faster development since we could use the same language (Dart) between the front end and the back end.  We can see this pattern in action in the Player data model, for example, which creates a player using the Dart Frog API. ![Dart Frog API endpoint creating a Player with shared data model](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-6.png) The app directly  accesses the players leaderboard, reusing the same model and avoiding duplication and desynchronization: ![Flutter Firestore SDK accessing the players leaderboard with shared model](/assets/images/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/body-7.png) ## Start playing: Solve the I/O Crossword puzzle! Start playing the [crossword](https://crossword.withgoogle.com/) on your own in all its across-and-down glory. For those who want to dig into the details, check out its [open-source code](https://github.com/VGVentures/io_crossword) and the [Developer Learning Pathway](https://developers.google.com/learn/pathways/solution-crossword) to showcase how we built it. Catch the [recap of Google I/O](https://www.youtube.com/watch?v=xKmEOXZsU_0) to learn more about everything announced this year! ### Building Better Software: The Importance of Rigorous Code Reviews Source: https://verygood.ventures/blog/building-better-software-the-importance-of-rigorous-code-reviews/ Every engineering team starts a new project with excellent coding standards, great architecture, and the cleanest code they have ever written. New people are added to the team, different coding styles conflict with each other, deadlines approach quickly, and you just need to get the feature in, telling yourself you’ll go back and clean it up later. Later rarely comes, and there is always another deadline or another new feature to build. The codebase is now so large that it’s difficult to grasp how far you’ve deviated from best practices, and you’re not entirely sure what to do in order to get your codebase back to a healthy state. At Very Good Ventures, our team has done code assessments for small startups, to large enterprise apps, and we want to share some quick tips you can use to evaluate your codebase. To get a sense of how well you and your team are doing in following best practices and standards you’ve set up, check out the 5 tips below. ## Documentation To get started, let’s focus on where new team members look when they start on a project: your README file. Anything that pertains to the code in the associated repository should be here for engineers to get the product up and running quickly and easily. The README should contain things like:  - The steps to get the app running locally.  - How to run any CI checks locally; like test coverage reports, spell check, and linters.  - Code generation commands like those needed to build localization files or serialize JSON.  Having the relevant documentation so close to the codebase improves the speed in which new engineers being introduced to the project can onboard, reduces time of digging through endless websites and portals for links or commands needed to build a feature, and ultimately allows engineers to selfserve information. We should add here that not *all* of the documentation needs to live in the README but a good starting point is to make sure everything needed to build, develop, and deploy the code is in there.  ## Project Structure Having a well-organized structure makes navigating the project easier and faster. When structuring your project, it’s best to make sure everything is grouped by domain.  As your project grows, accumulating hundreds of files, broad categorization of these files will result in unwieldy structures inside your code repository.   Here at VGV, we recommend following a *feature-based structure*. As the name implies, we organize our source code by the domain feature that a file belongs to. You can see an example below: ![Feature-based project structure with folders for each domain feature](/assets/images/blog/building-better-software-the-importance-of-rigorous-code-reviews/body-0.png) Important things to notice is that each feature has its own folder where all related source code lives. Items like the state management structure, views, view components, and their accompanying barrel file. *Your test folder should mirror your lib/ folder. This makes it easy to see which parts of the code have been tested and which have not. The only difference between test/ and lib/ is that each file will have \_test.dart appended to it.*  This structure makes it very easy to navigate from one feature to another, as everything a feature needs is encapsulated within its relevant folder. Organizing your code by component (I.E having a *screens* folder that holds all of your apps screens) eventually becomes unwieldy and difficult to navigate. ## Packages vs One Folder Creating packages for different modules like *ApiClients, Repositories, etc,* has several advantages over including everything in your source folder. These advantages include maintainability, testability, and CI workflows. You can read more about VGV’s architecture [on Marcos’ blog here](https://verygood.ventures/blog/very-good-flutter-architecture/). **Maintainability:** Keeping code up to date is a challenge we continually face in software development, especially when working with parts of software that interact with the outside world like *ApiClients* and *repositories.* Having those modules self-contained makes updating and changing them easier than when they are part of the full application. When something like an *ApiClient* is part of the source code updating that client can have cascading effects across the app. By putting these modules in packages, we don’t expose domain level implementation details. This makes refactoring easier in the future as the implementation of the client doesn’t change, just the details inside the client do, details that are not exposed to any outside uses. **Testability:** 100% code coverage through widget & unit tests is a strict standard we follow at VGV. We dive deeper into why we enforce 100% test coverage at VGV in [Oscar's blog](https://verygood.ventures/blog/road-to-100-test-coverage/), but keep reading for a brief overview of the benefits.  Having modules in packages helps in maintaining that standard. When testing parts of the code that rely on a module, you only need to mock the module without needing to be overwhelmed with how you get the information or where the information comes from. Writing tests will help you identify where your coupling is high, and alert you that you might want to consider moving parts of the implementation to a package.  **CI:** Having CI workflows implemented is a great tool to enforce code style, automate tests, and other automations. However, it can lead to large execution times as the application expands. Having self-contained packages also helps in this matter, since a change in those can trigger a specific workflow that runs only the required packages, and packages that remain unchanged. You can quickly get these benefits and more by adding [Very Good Workflows](https://workflows.vgv.dev/) to your CI/CD. ## Testing When assessing code we always check the code coverage using automated tooling. Our Flutter projects use [very_good test --coverage](https://pub.dev/packages/very_good_cli), our Android projects use JUnit, and our web projects use Jest. At VGV, we run this on the main application and all local packages within the project. In Flutter, you'll get a coverage report printout like the one below that I ran on the [Google IO_Flip](https://github.com/VGVentures/io_flip)  repository: ![Code coverage report from very_good test showing per-file coverage percentages](/assets/images/blog/building-better-software-the-importance-of-rigorous-code-reviews/body-1.png) This will show you what percentage of each folder/file has a test that covers those lines of code. Covering a line of code is not all that useful on its own. Test coverage helps you verify your widgets are displaying correctly, your processes are outputting the correct data, find and address code smells such as: large complex widgets, and tight coupling of domains as described above in the *Packages vs One Folder* section. ## Code Analysis & Linting Lastly, we check for strong linting and code analysis. Linting rules are the first line of defense for a healthy codebase. A healthy codebase implies that all developers on the team rigorously follow established rules, therefore we always advise to not use any *ignore\_for\_file* or *ignore\_for\_line*. These are typically code smells of quick, hasty development and degrade your codebase over time. At Very Good Ventures we use our open source analysis tool, [very_good_analysis.](https://pub.dev/packages/very_good_analysis) Very good analysis is a lint library heavily inspired by [flutter_lints](https://pub.dev/packages/flutter_lints) and [pedantic](https://github.com/googlearchive/pedantic) (although it is deprecated now), but has a stricter set of lint rules. We believe these linting rules create a healthier, more scalable codebase. We strongly suggest running [very_good_analysis](https://pub.dev/packages/very_good_analysis) in your pipeline to verify that all code being committed to your code base follows the same rules and hygiene from start to finish.  ## Where to get started Reviewing these five key sections of your product will give you a great starting point for assessing the health of your code base, as well as give you an idea of how aligned your team is, while taking less than an hour to run!  This is only the beginning though: At Very Good Ventures when we perform a [code assessment](https://verygood.ventures/solution/flutter-code-assessment/) we do a thorough investigation into the application, where we delve into every detail of the app, from the documentation surrounding it, to the CI/CD pipeline that was used to build and ship it. Afterward, we provide our findings and recommendations for next steps to get it to a better place.  If you’d like to talk more about how to increase velocity while continuing to build safe, scaleable code, [contact us!](https://verygood.ventures/contact-us/) We are passionate about ensuring code is scalable, while maintaining high quality, and want to help you do it too! ### Seamless Full Stack Development using Flutter & Dart Frog Source: https://verygood.ventures/blog/seamless-full-stack-development-using-flutter-dart-frog/ The last week of May brought the introduction of a new virtual conference for the Flutter & Dart community, [Full Stack Flutter](https://fullstackflutter.dev). The conference aims to extend beyond front-end development, covering backend solutions in Dart and a range of tools designed to enhance your development experience to a worldwide audience – having sessions on multiple days and times throughout to reach the global Flutter and Dart audience.  ## Beyond Just Flutter As a speaker, this was a great time to break away from talking only about Flutter in the context of what we do at Very Good Ventures. With a session focused on Flutter and Dart overall, I was able to cover a broader topic area–  “Dart on the backend: Seamless Full Stack Development, using Flutter & Dart Frog to Supercharge your Development Team.” At Very Good Ventures, we are starting to see a new change when it comes to bringing frontend and backend teams together. In fact, we have previously presented on this topic at Google Cloud Next with a Lightning Talk titled “[From Idea to Full Stack Solutions](https://verygood.ventures/blog/from-idea-to-full-stack-solutions-using-dart-and-google-cloud/)”, where we focused on the technical aspects of why this full stack approach with Dart is now feasible.  During this complementary talk about Seamless Full Stack Development using Flutter and Dart Frog, we focus on the outcomes that we are seeing within project teams when making the shift of unifying under one common language. This is something that we, as an industry, have been trying to do for many years . However, in our experience, this appears to be the first time where there are no obvious trade-offs when making the change given the maturity of Dart as a language. You can watch the entire talk here: {% video url="https://www.youtube.com/watch?v=2DJhzbhW0lQ" title="Video" %} ## How To Get Started We hope that this inspires you to take a look at your tech stack and how it could be improved by unifying on a single language and codebase. It’s a big shift for an engineering team but only takes one person passionate about the topic to make a difference and generate the spark needed to get your company thinking about it. If you are that person and want to talk more about this topic, or more specifically on how we have seen this work with Dart and Dart Frog, feel free to [reach out](https://verygood.ventures/contact-us/) so we can help! ### Deploying Fullstack Dart to AWS, Part III: Deploying to AWS Source: https://verygood.ventures/blog/deploying-fullstack-dart-to-aws-part-iii-deploying-to-aws/ In the last two posts of this series, we discussed [building a Dart Frog API to fetch blog content from a headless CMS](https://verygood.ventures/blog/deploying-fullstack-dart-to-aws/) and [building a Flutter web app to display the content in a browser](https://verygood.ventures/blog/deploying-fullstack-dart-to-aws-part-ii-building-the-web-app/). Now, it’s time to build a pipeline that deploys this content to AWS. To recap, here is the structure of the app we are building: ![Architecture diagram showing AWS App Runner, Amplify, and GitHub Actions pipeline](/assets/images/blog/deploying-fullstack-dart-to-aws-part-iii-deploying-to-aws/body-0.jpeg) For more details, you can also check out the articles on my personal blog about making [the API](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-i), [the app](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-ii), and [the pipeline](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-iii). ## Making Deployments Easy Dart Frog and Flutter offer great developer experiences, but you still have to get your app deployed so that other people can enjoy it. AWS has [several options](https://aws.amazon.com/websites/) when it comes to hosting web content on the cloud, such as AWS App Runner, Amplify, Lightsail, EC2 instances, and S3 Storage buckets. For this project, we’ll host the API on [AWS App Runner](https://aws.amazon.com/apprunner/), and the web content on [Amplify](https://aws.amazon.com/amplify/hosting/). Be warned that since App Runner is always running, you will have to pay a small fee (around $10 USD) based on app usage. If you wanted, you could deploy the entire app on App Runner, since it runs a container on the cloud. The benefit of separating hosting is that you don't have to re-deploy the API if you make minor changes that only affect the app, and Amplify hosting provides a *very* quick and easy way to host and update static web content (with a free first year of hosting as well!).  For both API and app deployments, we can use Github Actions to automate deployment. For the API, make a file in .github/workflows titled  deploy\_api.yaml.  Installing the Dart SDK and building the app, this pipeline makes use of AWS actions to push to a repository on Elastic Container Registry (ECR), which is then connected to a new App Runner service. ```yaml - name: Configure AWS credentials id: aws-credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 with: mask-password: true - name: Build, tag, and push image to Amazon ECR id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: your-ecr-repository-name IMAGE_TAG: ${{ github.sha }} run: | cd api docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - name: Deploy to App Runner id: deploy-apprunner uses: awslabs/amazon-app-runner-deploy@main with: service: your-app-runner-service-name image: ${{ steps.build-image.outputs.image }} access-role-arn: ${{ secrets.ROLE_ARN }} runtime: NODEJS_12 region: ${{ secrets.AWS_REGION }} cpu: 1 memory: 2 port: 8080 wait-for-service-stability: true ``` Be sure to set up the necessary [IAM permissions](https://docs.aws.amazon.com/apprunner/latest/dg/security_iam_service-with-iam.html#security_iam_service-with-iam-roles) in AWS to allow App Runner to [access ECR and to allow Github to access ECR and App Runner](https://aws.amazon.com/blogs/containers/deploy-applications-in-aws-app-runner-with-github-actions/).  To securely store API credentials to the CMS, you can use [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/), because App Runner is able to use Secrets Manager to retrieve environment variables. You will need another IAM role to allow App Runner to access Secrets Manager. For the app deployment, we will host this web app using AWS Amplify, which allows us to hook our repository directly up to AWS for automatic updates when the source code changes. I recommend creating a dedicated branch titled ci for this purpose. Add build and test pipelines to protect main, and then when the main branch is updated, automate a commit to the ci branch that will push your code change. To do this, create a file titled deploy\_app.yaml inside .github/workflows. After checking out the repo and setting up Flutter, this action resets ci to the latest from main, builds the web app, copies the artifact to a dedicated folder, and commits it to the branch.  ```yaml - name: Set branch to latest from main run: | git fetch origin main git reset --hard origin/main - name: Build Web Application run: flutter build web -t lib/main_production.dart --dart-define BASE_URL=https://your-api-app-runner-url.com - name: Copy Build Artifact run: | [[ -d artifact ]] && rm -r artifact mkdir artifact cp -R build/web artifact - name: Commit the Artifact uses: stefanzweifel/git-auto-commit-action@v5 with: branch: ci commit_message: 'ci: automated artifact update at ${{ github.sha }}' push_options: '--force' ``` On the AWS console, if you go to Amplify, you will have the option to host a web app, and you can select Github to connect your repository to AWS. If you have given AWS permissions to access your repository, you should be able to select it in the setup. Specify that you want to connect to the ci branch, and enter the build artifact’s directory (artifact/web) under Connecting a Monorepo?.  You should now have a blog app that is wired up to AWS on both backend and frontend. Frontend is downstream from backend, so changes to the server will prompt a new deployment on both frontend and backend, but changes to the client will only prompt a new deployment on the frontend. All of this will be triggered when new code is merged into main.  If you have read and followed our three blogs around this topic: Well done! You have now a full-stack blog app that fetches from Butter CMS, serves content in Dart Frog, presents content in a Flutter app, and is hooked up to be deployed on AWS.  I'd love to hear your impressions on this tutorial. Feel free to reach out on [LinkedIn](https://www.linkedin.com/in/stefanhk31) if you have questions/comments, or if you have done similar work deploying web apps on AWS. I learned tons working on this project, and I would love to hear more about others' journeys! ### Google I/O 2024: Flutter Moving Forward Source: https://verygood.ventures/blog/google-i-o-2024-flutter-moving-forward/ The dust has settled on Google I/O 2024, and the future of Flutter is looking bright! As expected, AI remained a central theme, with exciting announcements across various Google products. But for Flutter developers, the real stars of the show were the advancements in the Flutter framework itself. Read on for major Flutter updates from Google I/O! ## Flutter development ecosystem gets an upgrade Google’s app development ecosystem saw some big advancements at I/O this year. Notably, [Flutter 3.22 and Dart 3.4](https://medium.com/flutter/io24-5e211f708a37) launched just in time for I/O, and were highlighted during the Developer Keynote.   ![Flutter 3.22 and Dart 3.4 announcement at the Google I/O 2024 Developer Keynote](/assets/images/blog/google-i-o-2024-flutter-moving-forward/body-0.jpeg) Flutter 3.22 and Dart 3.4, bring us closer to building beautiful and performant apps across platforms. The main highlight of which is stable support for compiling Flutter web apps to WebAssembly. This translates to a significant boost in performance, with benchmarks showing example apps compiled to WebAssembly achieving 2x to 3x faster speeds compared to the previous JavaScript implementation. Some of the other updates: - **Faster and smoother:** The improved Impeller rendering engine delivers smoother visuals and reduces CPU usage on iOS devices. - **Enhanced navigation:** Leverage Android's predictive back gesture for a more natural user experience. - **Monetization made easy:** Integrate the Google Mobile Ads SDK for new ways to earn revenue from your app. - **Powerful debugging:** A new deep link validator in DevTools helps you identify and fix linking issues. - **Simplified Dart upgrades:** Streamlined API migration with dart fix directly in your IDE makes upgrading Dart code a breeze. - **Advanced profiling:** New DevTools capabilities provide deeper insights into your app's performance. - **AI integration:** Preview the Vertex AI for Firebase Dart SDK and add powerful AI features to your app, all with robust security. ## A Focus on Developer Experience These updates go beyond raw performance improvements. They demonstrate Google's commitment to streamlining the development process and empowering Flutter developers. Features like simplified Dart upgrades and advanced profiling tools all contribute to a more efficient and productive development workflow. As Flutter continues to evolve, the possibilities for experimentation, innovation, and achieving new heights are more exciting than ever. [Watch the full flutter update here!](https://io.google/2024/explore/da049081-00e1-4476-80da-1cd039302ac6/?utm_source=twitter&utm_medium=organicsocial&utm_campaign=&utm_content=) ## The Future of Flutter With the advancements unveiled at I/O, Flutter is poised to become an even more compelling choice for cross-platform app development.  The combination of performance gains, a developer-centric approach, and the power of AI integration opens up a world of possibilities for experimentation and innovation.  Whether you're building the next big social app or a niche productivity tool, Flutter equips you with the tools to create exceptional experiences that reach users on all major platforms. ## VGV at I/O At VGV, we were thrilled to have several team members attend Google I/O 2024. They actively participated in sessions, networked with other developers, and gained valuable insights into the future of technology. Fresh from the energy of Google IO, Jaime Santiago, one of our Engineering Leads, is back to share his highlights and key takeaways!  > “Going to Google IO was a great experience, I love meeting people from the community in person and putting a face to those GitHub and Twitter usernames. > In the keynote I found some cool stuff announced, like the scientific advancements that [AlphaFold 3](https://blog.google/technology/ai/google-deepmind-isomorphic-alphafold-3-ai-model/) can bring to the world by understanding how proteins interact with other molecules and helping curing diseases. I think that fine-tuned AI for scientific discoveries is one of the best use cases for this technology, and seeing Google investing in it is great! But not everything is about science, the demo around the [Music AI Sandbox](https://blog.google/technology/ai/google-generative-ai-veo-imagen-3/#Music-AI-Sandbox) was so cool. The fact that you can have an assistant making the sounds that you can describe with words is mind-blowing, this helps artists be more creative and explore different sounds easier to create music. > For developers, the [long context window](https://blog.google/technology/ai/google-gemini-next-generation-model-february-2024/#context-window) with Gemini 1.5 Pro is certainly promising. Having the ability to give the model this vast amount of data and making specific questions about it, can help us developers be more productive, write better documentation and solve more difficult problems. > But the session that was the most interesting to me, with concrete examples that you can start using today, was the talk about [Building generative AI agents with Vertex AI Agent Builder and Flutter](https://www.youtube.com/watch?v=V8P_S9OLI_I). I find the idea about building a custom AI agent that accesses different, customized APIs is very powerful. On one side, you have the control over the information sources that the model will have, but on the other side, you leverage the power and autonomy of AI to orchestrate the information and provide it in an understandable way.” We're particularly excited about how these advancements can be applied to our client projects, allowing us to craft even more innovative and efficient solutions. Stay tuned for future blog posts where we'll dive deeper into specific Flutter updates and how they can benefit your app development journey. ![VGV team members attending Google I/O 2024](/assets/images/blog/google-i-o-2024-flutter-moving-forward/body-1.jpeg) ### Deploying Fullstack Dart to AWS, Part II: Building the Web App Source: https://verygood.ventures/blog/deploying-fullstack-dart-to-aws-part-ii-building-the-web-app/ In the previous post of this series, we discussed building a Dart Frog API to fetch blog content from a headless CMS. If you’ve been following along, you should have a working API that is delivering JSON blog posts. Now, it’s time to make a frontend that can display those posts. To recap, here is the structure of the app we are building: ![Architecture diagram showing Butter CMS, Dart Frog API, and Flutter web app](/assets/images/blog/deploying-fullstack-dart-to-aws-part-ii-building-the-web-app/body-0.jpeg) For more details, you can also check out the articles on my personal blog about making [the API](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-i), [the app](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-ii), and [the pipeline](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-iii). ## A Scalable, Responsive Frontend On the frontend, we should continue to adhere to the principles of layered architecture by creating designated packages for the fetching and manipulating of API data. In addition, we can make a designated package for theming data and widgets.  For fetching data, create a blog\_api\_client. This package will be responsible for interacting with our Dart Frog api. Right now, we’re only adding two endpoints, but with a few steps we can make our API scalable in case we need to make more. Create a class that extends the BaseClient of the http package, and use that extended client to interact with your endpoints. Then, make a generic \_sendRequest method that can be used for any type of future http request. ```dart Future _sendRequest({ required Uri uri, required FromJson fromJson, String method = 'GET', }) async { final request = Request(method.toUpperCase(), uri); final responseStream = await _client.send(request); final response = await Response.fromStream(responseStream); final responseBody = response.json; if (response.statusCode >= 400) { throw BlogApiClientFailure( statusCode: response.statusCode, error: responseBody, ); } } /// {@template blog_api_client} /// An implementation of an HTTP client to interact with the blog API. /// {@endtemplate} class BlogApiClient extends BaseClient { /// {@macro blog_api_client} BlogApiClient({required Client innerClient}) : _innerClient = innerClient; final Client _innerClient; @override Future send(BaseRequest request) { request.headers.putIfAbsent( HttpHeaders.contentTypeHeader, () => ContentType.json.value, ); request.headers.putIfAbsent( HttpHeaders.acceptHeader, () => ContentType.json.value, ); return _innerClient.send(request); } @override void close() { _innerClient.close(); super.close(); } } ``` Manipulating the data from our API is relatively simple, thanks to the legwork we did in our blog models package. To manipulate the data, create a blog\_repository package with a class to get the data from the api client and turn it into the correct model for the presentation layer. ```dart class BlogRepository { BlogRepository({required BlogApi blogApi}) : _blogApi = blogApi; final BlogApi _blogApi; /// Gets a list of [BlogPreview] objects. Future> getBlogPreviews() async { final response = await _blogApi.getBlogs(); return response.data.map(BlogPreview.fromBlog).toList(); } /// Gets a single [BlogDetail] object given a unique [slug]. Future getBlogDetail({required String slug}) async { final response = await _blogApi.getBlog(slug); return BlogDetail.fromBlog(response.data); } } ``` For theming and UI, we need to create a blog\_ui package. There are several widgets that you can make here to extract reusable code, but the main two for this project are BlogCard (for displaying a preview on the overview page) and BlogDetailContent (for displaying the full content of a blog post).  The BlogCard is a material Card widget with some extra image and spacing features. The BlogDetailContent is a little more complicated. Since Butter CMS returns blog content in HTML, we need a way to convert HTML into widgets that will fit our app’s styles. The package [flutter_html](https://pub.dev/packages/flutter_html) works well for this, but beware: as of writing this post, there is a [compilation error in the stable version of flutter html](https://github.com/Sub6Resources/flutter_html/issues/1398). Using 3.0.0-alpha.5 will work. Alternatively, if you want to use the most recent prerelease version of 3.0.0-beta.2, you will need to [fork the package repository](https://github.com/Sub6Resources/flutter_html) and [implement a workaround](https://github.com/Sub6Resources/flutter_html/issues/1314#issuecomment-1635802042) to bypass a rendering issue in [older versions of iOS Safari](https://github.com/Sub6Resources/flutter_html/issues/1314).   Once you have created the api client, repository, and UI packages, you have all the layers necessary to build out two pages: an overview page to present a list of blog previews, and a detail page to present the full content of a blog post. When clicking on a card on the overview page, you navigate to a detail page. Each of these pages has a bloc with a data requested event. The handler for this event sends a request to fetch data on page load. For example, here is the handler for the overview page: ```dart FutureOr _onBlogOverviewPostsRequested( BlogOverviewPostsRequested event, Emitter emit, ) async { emit(BlogOverviewLoading()); try { final previews = await _blogRepository.getBlogPreviews(); emit(BlogOverviewLoaded(previews: previews)); } on Exception catch (e) { if (e is BlogApiClientFailure) { emit(BlogOverviewFailure(error: e.body)); return; } emit(BlogOverviewFailure(error: e.toString())); } } ``` The detail page data fetch handler is almost the same, but with different state names (e.g., BlogDetailLoading instead of BlogOverviewLoading) and a call to getBlogDetail instead of getBlogPreviews. With our packages and our blocs set up, we can now code out our page views. Each page sends a request to the bloc on page load, then takes advantage of [Dart 3 switch patterns](https://dart.dev/language/patterns) on sealed classes to return content based on blog state. We will use [go_router](https://pub.dev/packages/go_router) to navigate between overview and detail. The [blog overview page](https://github.com/stefanhk31/personal_blog_flutter/blob/main/lib/blog_overview/view/blog_overview_page.dart) returns a list of BlogCard widgets: ```dart ListView.builder( itemCount: previews.length, itemBuilder: (context, index) { final preview = previews[index]; return BlogCard( title: preview.title, subtitle: preview.description, published: preview.published, imageUrl: preview.image, onTap: () { context.go( '/${preview.slug}', ); }, ); }, ); ``` The [detail page](https://github.com/stefanhk31/personal_blog_flutter/blob/main/lib/blog_detail/view/blog_detail_page.dart), meanwhile,  returns our custom BlogContentWidget: ```dart BlogDetailContent( authorName: '${detail.author.firstName} ${detail.author.lastName}', body: detail.body, published: detail.published, slug: detail.slug, title: detail.title, authorImage: detail.author.profileImage, featuredImage: detail.featuredImage, ); ``` Now start your API and set your BASE\_URL to [http://localhost:8080](http://localhost:8080). Run your Flutter app locally, and you should see the blog overview page when starting up (your content will be different): ![Blog overview page showing a list of BlogCard widgets with previews](/assets/images/blog/deploying-fullstack-dart-to-aws-part-ii-building-the-web-app/body-1.png) When you click on one of the cards, you should see a blog detail: ![Blog detail page showing full post content with BlogDetailContent widget](/assets/images/blog/deploying-fullstack-dart-to-aws-part-ii-building-the-web-app/body-2.png) If you've made it this far, congratulations! You now have a full-stack Dart app that displays content fetched in the backend from a headless CMS. Feel free to check out [my sample code](https://github.com/stefanhk31/personal_blog_flutter/tree/main/api) for more details, or reach out on [LinkedIn](https://www.linkedin.com/in/stefanhk31) with questions/comments!  [Continue reading Deploying Fullstack Dart to AWS Part III: Deploying to AWS.](https://verygood.ventures/blog/deploying-fullstack-dart-to-aws-part-iii-deploying-to-aws/) ### The Benefits of Streamlining Mobile & Web Development Source: https://verygood.ventures/blog/the-benefits-of-streamlining-mobile-web-development/ [***This article was posted on Laura Hughes’ blog first.***](https://www.linkedin.com/pulse/benefits-streamlining-mobile-web-development-laura-hughes-y1n2c/?trackingId=Gvz1HGk6TjWegMaOIaqRAg%3D%3D) [Very Good Ventures](https://verygood.ventures/) is known for its expertise in Flutter. However, what's not widely known is that Very Good Ventures also brings that same expertise to web design and development. The decision to bring these services in-house was strategic, aiming primarily to seamlessly integrate the aesthetic and functionality of mobile apps with their corresponding websites. This move enhances brand cohesion and delivers a unified experience for our clients and their customers. Centralizing both design and development within a single, cohesive team facilitates the creation of this synergy. This approach allows Very Good Ventures to offer clients a comprehensive solution for all their digital needs. Partnering with a team like Very Good Ventures, equipped with both design and engineering proficiency across mobile and web assets, unlocks a number of conveniences and benefits for clients. Here's a few reasons why this collaborative approach is instrumental: ## 1. Holistic Design and Development Opting for a design team capable of handling both mobile and web assets ensures a unified approach to the entire design and development process. This holistic perspective allows for consistency in visual elements, user experience, and functionality across both platforms. With a comprehensive understanding of your brand's objectives, the team can craft solutions that seamlessly translate from one medium to another, enhancing brand identity and user engagement. ## 2. Efficient Communication and Collaboration Working with a single team simplifies communication and fosters closer collaboration between designers and developers. With expertise spanning both mobile and web domains, the team can effectively translate design concepts into functional interfaces, reducing the likelihood of misinterpretation or discrepancies. This streamlined workflow accelerates the iteration process, leading to quicker turnaround times and more efficient project management. ## 3. Synchronized Design and Development Cycles By aligning design and development within the same, united team, businesses eliminate the potential for disjointed workflows and conflicting priorities. Design decisions can be seamlessly integrated into the development process, ensuring that both mobile and web assets evolve cohesively. This synchronization minimizes rework and maximizes efficiency, ultimately resulting in a more refined end product that meets both aesthetic and technical requirements. ## 4. Cost-Effectiveness and Time Savings Consolidating design and development responsibilities under one roof can yield significant cost savings and time efficiencies. With a unified team overseeing both aspects, redundant efforts and communication overhead are minimized. Moreover, the ability to leverage shared resources and expertise reduces project overhead, ultimately delivering greater value to the client. Additionally, streamlined workflows translate into faster time-to-market, allowing clients to capitalize on opportunities more swiftly. ## 5. Enhanced SEO and Web Performance Maintaining consistency in design and UX across mobile apps and websites is not only beneficial for branding but also for search engine optimization. Search engines favor websites with responsive design and consistent content across different devices, leading to improved search rankings and visibility. By ensuring uniformity in design and functionality, businesses can enhance their online presence and attract more organic traffic, ultimately driving business growth and success. ## 6. Comprehensive Support and Expertise Partnering with a design team that encompasses both mobile and web capabilities provides access to a broader spectrum of expertise and support. From conceptualization to deployment, businesses benefit from a comprehensive suite of services tailored to their specific needs. Whether it's optimizing user interfaces for different screen sizes or implementing responsive design principles, the team's collective knowledge ensures that all aspects of the project are addressed with precision and proficiency. ## Conclusion In an increasingly interconnected digital landscape, the importance of seamless integration between mobile apps and websites cannot be overstated. By enlisting the services of a single design team equipped to handle both mobile and web assets, coupled with engineers proficient in both domains, businesses can gain a competitive edge in terms of efficiency, consistency, and innovation. This collaborative approach not only streamlines workflows and communication but also delivers superior results that resonate with users across diverse platforms. As businesses strive to enhance their online presence and user experiences, the convenience of working with a unified design and development team becomes the gold standard for success. ### Deploying Fullstack Dart to AWS: Part I Source: https://verygood.ventures/blog/deploying-fullstack-dart-to-aws/ With [Dart Frog](https://dart-frog.dev), Flutter developers have a great opportunity to write full-stack Dart apps. This helps increase development speed by limiting the context switching between backend and frontend code, and also provides a consolidated set of models that can be shared between all your products. What’s more, Flutter’s flexibility makes it a [great solution for web apps](https://flutter.dev/multi-platform/web).  With this in mind, I set out to make a blog app that took advantage of the Dart/Flutter ecosystem on both backend and frontend. The app would have a server that fetches data from a headless CMS and then delivers that data to a clean, mobile-responsive Flutter front end. By handling CMS data in the backend, the app would follow security best practices, since API credentials would never need to cross into the client.  In addition, I thought this would be an excellent opportunity to showcase how smoothly you can integrate Dart software with AWS services. AWS is the most widely used cloud service provider on the planet, and customers who use AWS can also take advantage of all the great benefits Dart and Flutter have to offer. I built the app with the following structure: ![Architecture diagram of fullstack Dart blog app with Butter CMS and AWS](/assets/images/blog/deploying-fullstack-dart-to-aws/body-0.jpeg) On both backend and frontend, I did my best to adhere to a [layered architecture](https://verygood.ventures/blog/very-good-flutter-architecture/) pattern to create maintainable and scalable code.  What follows is a three-part series on how to build this blog app. In this article, I will discuss getting the project started and building the API. Part two will go over the frontend app, and part three will lay out the deployment to AWS. For more details, you can also check out the articles on my personal blog about making [the API](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-i), [the app](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-ii), and [the pipeline](https://www.stefanhodgeskluck.com/#/making-a-full-stack-blog-app-part-iii). ## Getting Started To follow along, you will need to have the following tools set up: - [Flutter](https://docs.flutter.dev/get-started/install) (installs both Flutter and the Dart SDK) - [Dart Frog](https://dart-frog.dev/getting-started/) - [Very Good CLI](https://cli.vgv.dev/) - A free subscription to [Butter CMS](https://buttercms.com) - An [AWS Account](https://aws.amazon.com/free/?gclid=EAIaIQobChMI8-C5gfvxhQMVj55aBR1cKgGtEAAYASAAEgKb7PD_BwE&trk=78b916d7-7c94-4cab-98d9-0ce5e648dd5f&sc_channel=ps&ef_id=EAIaIQobChMI8-C5gfvxhQMVj55aBR1cKgGtEAAYASAAEgKb7PD_BwE:G:s&s_kwcid=AL!4422!3!432339156165!e!!g!!create%20aws%20account!9572385111!102212379047&all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all) (your app will have a monthly fee if you keep it running) - A [Github account](https://github.com/) The [Very Good CLI](https://cli.vgv.dev/) provides an excellent starting point to any Dart or Flutter project. It's quick, easy, and lays a lot of groundwork for making a well-architected, well-tested project. To start, navigate to the directory where you want to store your project, and run the following terminal command: ```dart very_good create flutter_app YOUR_APP_NAME_HERE ``` Then, navigate to the root of the app and create a Dart Frog api: ```dart dart_frog create api ``` ## A Secure Backend to Fetch Data It is true that if you wanted, you could write a blog app in Flutter without a backend by simply calling your CMS directly from your frontend code. The problem with this approach is that your CMS will, in all likelihood, require credentials such as an auth token or API key to fetch data. There are ways to obfuscate credentials that are added as environment variables on the front-end, but in reality, the only secure place to store secrets is [on the server](https://codewithandrea.com/articles/build-deploy-dart-shelf-app-globe/). I was able to add a data client to my Dart Frog app that fetched blog content from my CMS without my frontend ever needing to know any credentials. I simply injected my API key as an [environment variable](https://dart-frog.dev/basics/environment/) when configuring the data client that interfaces with my CMS.  If you build your app using layered architecture, it is easy to wire things up so that your API is interfacing with your CMS. In this article, I use [Butter CMS](https://buttercms.com), a headless CMS with free read API access for non-commercial developer use. If you wanted to use a different CMS, however,  you could modify the models and data client and most of the architecture would still be the same.  The first step to setting up a full-stack project like this is to create a package for your models. Organizing the models into a single package simplifies access to all necessary object models throughout the project. Any other part of the app that requires model access only needs to import the blog models package. Plus, since we are using [json_serializable](https://pub.dev/packages/json_serializable) on the models that need serialization, it is nice to keep this dependency limited to a single place. I based my models on the data returned by the Butter CMS API, with modified classes for presentation: ```dart data_models // core models necessary to construct other models author blog_meta blog_summary blog blogs_meta category tag endpoint_models // representation of how blog data is returned by the API blog_response blogs_response presentation_models // models used to present blog data on the frontend blog_detail blog_preview ``` Once the model package is complete, we need to create a data client to connect with the CMS. If you follow this article and use Butter, this will be ButterCMSClient. This data client takes the Dart http client from the [Dart http package](https://pub.dev/packages/http) and uses it to interact with the CMS API. Since this app is just reading content, you only need two methods: one to fetch all blog posts, and one to fetch an individual post given a unique identifier: ```dart /// Fetches a list of blog posts from the ButterCMS API. Future fetchBlogPosts({ bool excludeBody = false, }) async { final queryParameters = { 'auth_token': _apiKey, }; if (excludeBody) { queryParameters['exclude_body'] = 'true'; } final uri = Uri.https(_baseUrl, '/v2/posts', queryParameters); return _httpClient.get(uri); } /// Fetches a single blog post from the ButterCMS API, /// given a unique [slug]. Future fetchBlogPost({required String slug}) async { final queryParameters = { 'auth_token': _apiKey, }; final uri = Uri.https(_baseUrl, '/v2/posts/$slug', queryParameters); return _httpClient.get(uri); } ``` Using Dart Frog [middleware](https://dart-frog.dev/basics/middleware/), we can inject our data client into context before API requests are executed. Then, we can [construct routes](https://dart-frog.dev/basics/routes/) based on the dart files in the routes directory: ```dart routes blogs [slug].dart // https://{api-url}/blogs/[slug] index.dart // https://{api-url}/blogs ``` The routes allow for the creation of clean, simple endpoints for fetching the necessary data. \[index\].dart provides the endpoint for fetching all posts: ```dart Future onRequest(RequestContext context) async { return switch (context.request.method) { HttpMethod.get => await _get(context), _ => Response(statusCode: 405, body: 'Method Not Allowed'), }; } Future _get(RequestContext context) async { final blogsResponse = await context.read().fetchBlogPosts({ excludeBody: true, //set this to reduce the size of the API response }); return Response( statusCode: blogsResponse.statusCode, body: blogsResponse.body, ); } ``` In \[slug\].dart we have a similar endpoint for fetching an individual post: ```dart Future onRequest(RequestContext context, String slug) async { return switch (context.request.method) { HttpMethod.get => await _get(context, slug), _ => Response(statusCode: 405, body: 'Method Not Allowed'), }; } Future _get(RequestContext context, String slug) async { final blogResponse = await context.read().fetchBlogPost(slug: slug); return Response( statusCode: blogResponse.statusCode, body: blogResponse.body, ); } ``` Run your app using dart\_frog dev and open up your favorite API tool (e.g., Postman) and make a GET request to http://localhost:8080/blogs. If everything is set up right, you should get back a JSON object containing metadata and an array of posts that only contains one post (Butter's default blog post). Check that default blog for its slug (should be example-post), and then make a request to http://localhost:8080/blogs/{slug}.  You should get back a JSON object with metadata and a single blog post object. If you've made it this far, congratulations! You've set up a Dart API that connects to Butter CMS and provides blog content. Feel free to check out [my sample code](https://github.com/stefanhk31/personal_blog_flutter/tree/main/api) for more details, or reach out on [LinkedIn](https://www.linkedin.com/in/stefanhk31) with questions/comments.  You can continue reading [Deploying Fullstack Dart to AWS Part II: Building the Web App.](https://verygood.ventures/blog/deploying-fullstack-dart-to-aws-part-ii-building-the-web-app/) ### How It’s Made: Digital Escape Room AI-driven game powered by Gemini and Flutter Source: https://verygood.ventures/blog/how-its-made-digital-escape-room-ai-driven-game-powered-by-gemini-and-flutter/ AI-powered experiences are opening a whole new world of possibilities where customization, personalization, speed, and effectiveness can create more engaging products. By developing this game in less than three weeks we wanted to prove how easy it is to bring new and unprecedented experiences to production in record time, thanks to tools like Gemini and Flutter. Now it’s our time to share the open-source code! Keep reading to learn how we built it.  A few weeks ago, we had the pleasure to take the stage at [Google Cloud Next](https://verygood.ventures/blog/very-good-ventures-official-google-cloud-partner-at-google-cloud-next-2024/) for the first time as an official Google Cloud partner and sponsor, and shared our experience on [building games powered by Flutter and Gemini](https://verygood.ventures/blog/building-ai-powered-games-with-gemini-and-flutter/).  {% video url="https://www.youtube.com/watch?v=_NRDyKiVIa4" title="Video" %} Today, [we’re open-sourcing the code](https://github.com/VGVentures/digital_escape_room) so everyone can see how we built the game, and use it as a seed of creativity, so developers all around the world can find inspiration and technical guidance from our work.  You can find the code here: [https://github.com/VGVentures/digital_escape_room](https://github.com/VGVentures/digital_escape_room). You will need your own Google AI studio API key, we explain how you can obtain it in the [Integrating Gemini AI with Flutter](https://verygood.ventures/blog/integrating-gemini-ai-with-flutter/) blog post. ## Iterating our prompt strategy until we get satisfactory results Your first prompt won’t be your final prompt*.* Your prompt becomes the most important when working with LLM tools like Gemini. As such, it is essential to fine-tune the prompt until you get exactly what you want consistently.  Being detail-oriented, providing examples and specifying exactly what you want and how you want it helps Gemini to adapt its response to your needs. As such, our first prompt was: ![Initial Gemini prompt requesting three escape room challenges](/assets/images/blog/how-its-made-digital-escape-room-ai-driven-game-powered-by-gemini-and-flutter/body-0.png) While this prompt actually delivered three different challenges, we were not able to parse the information received, we did not have information about the solution of the game, and the solution was not customized with the user’s information. After a few iterations, we finally found a prompt that we could work with, and that was providing results that were unique to each player: ![Refined Gemini prompt with structured JSON output and player personalization](/assets/images/blog/how-its-made-digital-escape-room-ai-driven-game-powered-by-gemini-and-flutter/body-1.png) ## Building in record-time with Flutter and Flame When we learned that our talk at Google Cloud Next was accepted, we were over the moon, but we had very little time to build this experience. Our ambitions were high and we knew we wanted to surprise and share an exciting activity with every player who came to our booth to play. Flutter gave us exactly what we needed to deliver: A multi-platform UI toolkit that allowed us to iterate fast without losing any code quality. We didn’t want this experience to be a proof-of-concept, we wanted hundreds of attendees to play the game during the conference without even realizing they were using Flutter. Additionally, the power of Flame helped us to integrate all the game mechanics, player controls and sprite sheets. Personally, this was my first experience building a game, and it was so easy and fast that I felt like I had been writing games all my life. ## Pixel art for a great look & feel ![Pixel art escape room game scene with retro SNES-style office interior](/assets/images/blog/how-its-made-digital-escape-room-ai-driven-game-powered-by-gemini-and-flutter/body-2.png) ![Pixel art escape room challenge screen with NES UI styled dialog](/assets/images/blog/how-its-made-digital-escape-room-ai-driven-game-powered-by-gemini-and-flutter/body-3.png) Visuals are everything when it comes to playing a game: When building this digital escape room, we wanted to have a retro vibe, almost like an SNES game. Thanks to Flutter’s customizable theming, we were able to use [Erick Zanardo’s Nes UI library](https://github.com/erickzanardo/nes_ui) to power our aesthetic needs.  In addition to those Flutter widgets, we also needed game assets that matched that vibe, and we found gold in [LimeZu’s itch page](https://limezu.itch.io/). We purchased the [Modern Office](https://limezu.itch.io/modernoffice) and [Modern Interiors](https://limezu.itch.io/moderninteriors) asset packages, and we couldn’t be happier with the results. ## This is just the beginning If this AI game took three weeks -working overnights and weekends- to get it ready, imagine all the things we can build in no time with Gemini and Flutter! This is just the tip of the iceberg for us, so what should we work on next? Let us know how [we can help you develop your project](https://verygood.ventures/contact-us/) with AI tools or [Connect with Jorge on Linkedin](https://www.linkedin.com/in/jorgecocaramos/) to learn more about this experience. ### WebView vs Native in Mobile Apps: When to Use Them in Mobile Development Source: https://verygood.ventures/blog/wrapped-up-in-apps-the-pros-and-cons-of-web-views-in-mobile-development/ The world of mobile apps is constantly evolving, and businesses are always looking for ways to improve the user experience. One approach that has gained popularity is the use of web views wrapped within native apps. But is this a shortcut to success, or a path fraught with limitations? This article will delve into the business value of  transitioning from wrapped experiences to native app development using frameworks like Flutter. Our discussion begins with a common scenario: a company launches a mobile app that feels more like a mobile website. This can happen for a number of reasons, but the core issue lies in the decision to wrap a web experience within a native app container. While this approach may seem appealing at first glance, it can lead to a number of drawbacks that hinder both the user experience and the ability to gather valuable data. ## The Allure of the Wrapper There are several reasons why companies might choose to wrap a web view in a mobile app. Development can be faster and cheaper, especially if a well-developed website already exists. This can be a tempting option for businesses looking to get a mobile presence up and running quickly. Additionally, web views can leverage existing web development skills and codebases, potentially reducing the learning curve for development teams. ### Cracks in the Facade However, the convenience of web view wrappers often comes at a cost. Here are some of the key disadvantages to consider: - **Limited Functionality:** Wrapped web apps often lack access to native device features like GPS, cameras, and offline functionality. This can significantly limit the capabilities of the app and hinder the user experience. - **Performance Issues:** Web views can be slower and more sluggish than native apps, especially on older devices or with complex content. This can lead to frustration for users and a higher uninstall rate. - **Data Visibility Challenges:** Analytics data from web views can be difficult to track and integrate with native app analytics. This makes it harder for businesses to understand how users are interacting with the app and make data-driven decisions. - **Compromised User Experience:** The look and feel of a web view can often clash with the native design of the app, creating a disjointed user experience. ### Beyond the Wrapper: The Benefits of Native Development Moving away from web view wrappers and towards native app development using frameworks like Flutter can offer significant advantages: - **Enhanced Performance:** Native apps are typically faster and smoother than web view apps, providing a more responsive user experience. - **Full Feature Set:** Native apps have full access to device features, allowing for a richer and more interactive app experience. - **Improved Analytics:** User data from native apps can be easily tracked and integrated with other analytics tools, providing valuable insights into user behavior. - **Seamless User Experience:** Native apps can be designed to look and feel like an integral part of the operating system, creating a more cohesive and enjoyable user experience. ## Making the Right Choice The decision of whether to use a web view wrapper or develop a native app depends on several factors, including the specific needs of the app, the available resources, and the desired user experience. However, for businesses looking to create a high-performing, feature-rich app that provides valuable data and a seamless user experience, native development is often the better choice. Frameworks like Flutter can further streamline the development process and create apps that are truly native to both Android and iOS. By carefully considering the trade-offs between web view wrappers and native development, businesses can make informed decisions that lead to successful mobile app experiences. ### Building AI-powered games with Gemini and Flutter Source: https://verygood.ventures/blog/building-ai-powered-games-with-gemini-and-flutter/ At the Google Cloud Next 2024 conference, Very Good Ventures had the honor of taking the stage as an official Google Cloud Partner and sponsor. Our team shared insights on how to adventure the novelty of the power of AI and Flutter to build groundbreaking AI-powered games.  Here is their live session from Google Cloud Next '24: {% video url="https://www.youtube.com/watch?v=_NRDyKiVIa4" title="Video" %} Keep reading to discover the key concepts we discussed in further detail. {% video url="https://www.youtube.com/watch?v=GTVvu6YQomc" title="Video" %} ## Why build an AI-powered game? The idea of bringing together AI and Gaming is part of today’s rapidly evolving digital world and is revolutionizing enterprise development, enabling businesses to achieve more with less. Building an AI-powered game offers customized experiences tailored to each player's preferences and behavior, enhancing engagement and retention. It also helps doing More with Less: The capabilities of AI algorithms enable developers to achieve greater outcomes with fewer resources, accelerating development timelines and reducing costs. The combination of AI and gaming demonstrates what's possible in the digital environment and what we can hope to achieve. **What Flutter & Dart can do: **At Very Good Ventures, we have been shipping high-quality products while moving fast and having fun along the way. With Flutter and Dart, a small team can achieve big results in a short amount of time. Here are some key recommendations for leveraging Flutter and Dart in game development: - High Performance: Ensure a minimum of 60fps at all times for a seamless user experience across all devices. - Run Everywhere: With Flutter’s cross-platform capabilities, deploy applications on mobile, desktop, web, cars, or the Jumbotron of Las Vegas Sphere! - First-Party Support for AI Integration with Gemini: Seamlessly integrate AI capabilities into your game development process. - Dart in the Frontend, Dart in the Backend: Streamline development with a consistent language for frontend and backend reduces complexity, allowing us to focus on building great products. - Deploy and Run on Google Cloud with Ease: With robust infrastructure and support from Google's ecosystem, we can confidently deploy our applications and ensure they perform at their best. - Outstanding Development Experience: Flutter offers an exceptional development experience, with features like hot reload for rapid iteration, JIT & AOT compilation for high performance, and UI fidelity for pixel-perfect designs. - Open Source to Avoid Proprietary Solutions: We believe in the power of open source to drive innovation and collaboration fostering creativity and growth. - Everything is a Widget: The learning curve is extremely low since one of the most powerful aspects of Flutter is its simplicity. With everything being a widget, allows developers to quickly become proficient and start building amazing applications in record time. - Faster Iteration: With Flutter and Dart, developers can iterate rapidly, moving from concept to deployment in record time, while maintaining the highest quality standards.  - Have Fun While Building This Experience! Above all, we believe in having fun while building great products. With Flutter and Dart, development becomes not just a job, but a creative and enjoyable experience.  ## Enhancing Game Development with AI In recent years, the integration of AI into the gaming industry has revolutionized the way games are developed, played, and experienced: From generating game assets to enhancing in-game experiences, AI offers unlimited possibilities. AI-powered games leverage generative AI algorithms to create game assets efficiently and ethically. In-game AI functionalities enable dynamic puzzle generation and lightweight content creation. We can play around with AI tools that offer diverse capabilities, including recognition, classification, and media generation, opening new avenues for creative expression and storytelling in games. Developers can easily and efficiently create game assets with generative AI algorithms, such as art, dialogue, sound, and music, always using ethically sourced models and ensuring that artists are respected and their work is honored. Additionally, AI can assist in ideation by analyzing game design documents and generating achievements, NPC information, and other elements, streamlining the creative process. Gemini’s capabilities, along with the ease of usage that [Google’s Generative AI package](https://pub.dev/packages/google_generative_ai) provides, make [developing experiences with Flutter and Gemini](https://verygood.ventures/blog/integrating-gemini-ai-with-flutter/) a smooth and fast experience, helping developers and product makers from idea to production in record time. ## In-Game AI Use AI's influence extends beyond the development stage and permeates actual gameplay. Server-side AI functionalities, such as anti-cheat data analysis and prediction of massively multiplayer game traffic, contribute to maintaining fair and secure gaming environments. In-game AI models enable dynamic puzzle generation and lightweight content generation for added flavor. AI can also enhance graphics at runtime through hardware-level upscaling technologies like Nvidia's VSR AI Upscaling. ## AI Tools Capabilities AI tools offer various capabilities ranging from recognition and classification to media generation. Object recognition, speech recognition, and image classification facilitate immersive mixed-reality and virtual-reality gaming experiences. Conversational AI models, such as Google's Gemini LLM, enable natural-language interfaces for voice commands, enriching player interactions and expanding gameplay possibilities. Moreover, AI-generated content, including text, images, music, and even videos, opens new paths for creative expression and storytelling in games. ## Applications for Games and the Future of AI in Gaming AI upscaling techniques improve visual fidelity and performance, enhancing the overall gaming experience. The applications of AI in gaming are vast and diverse, ranging from object recognition to in-game procedural generation and AI upscaling techniques. As AI continues to advance, its potential in gaming remains limitless. Conversational AI models like LLMs are poised to redefine player-NPC interactions and narrative storytelling in games, offering exciting prospects for innovation and creativity. The integration of AI and Flutter into game development represents a transformative opportunity for developers to create immersive, engaging, and innovative gaming experiences. Want to learn more about what AI can do for digital experiences? [Contact us](https://verygood.ventures/contact-us/) and stay tuned to discover the latest news on all AI + Flutter related! ### From Idea to Full Stack Solutions: Using Dart and Google Cloud Source: https://verygood.ventures/blog/from-idea-to-full-stack-solutions-using-dart-and-google-cloud/ The Very Good Ventures team had the opportunity to participate in the Google Cloud Next 2024 event as an official Google Cloud Partner and sponsor.  We had the privilege to be on the stage at the Community Hub to talk about how Dart and Google Cloud can streamline a full-stack development journey going from idea to launch in record time. Below you can find a summary of the key insights we presented and we hope it will be useful for dev teams around the world.  Here is the live demo from Google Cloud Next '24: {% video url="https://www.youtube.com/watch?v=chDHV3EVXv8" title="Video" %} ## Unifying Development Teams through shared Technology Stacks  Very Good Ventures has been the Flutter and Dart ecosystem leader for over six years which has given us a wide range of experience in many different codebases and products. Throughout this time we have seen companies and products grow to meet the desires and needs of their customers. While this is key to making sure your business is successful, it also means that overall there is a lot of code to make a modern product successful.  In order to combat this, we have found that the more we can bring the frontend and backend teams together through a shared technology stack, the easier it is to move forward as one team. This quickly turns into a better experience for your end users as the cycle time between updates and feature releases becomes shorter. ## Typical Problems Teams Face In today’s software landscape, no matter what technology stack is being used, the problems faced typically end up being about the same.  - **More development, less resources** - As the business is always moving forward and asking for more with fewer available resources, the demand on development teams can easily become overwhelming.  - **Complexity in Solutions** - The main task of the business is always to solve problems quickly & deploy them to production with zero defects across multiple platforms to ensure the biggest impact. - **Hard to Focus on Value** - When you don’t have a solid base to build from, you’re constantly fixing issues. This makes it impossible to also concentrate on business and user value. - **How to Manage it all? -** With so many different systems, requirements, and dependencies it’s easy to lose track of responsibilities, especially across multiple development teams. But at the end of the day, we are Engineers. So how can we solve these issues with a technology solution that also benefits the business? ## Multiple Platforms, One Codebase With our extensive experience with Flutter from its start, we've witnessed its profound impact on teams and their productivity. We firmly believe that the efficiency of uniting frontend teams (typically Android and iOS but not limited to just mobile platforms) significantly saves time and resources throughout the project's development lifecycle. However, it's not uncommon for backend development to lag behind frontend progress due to its fast-paced nature. To address this challenge, we consider Dart for backend development, as it fosters team unity and matches the productivity achieved in frontend work.  While Flutter has been multi-platform from the start, Dart has purposely been platform agnostic. It’s just a language that can be used wherever needed. Given this flexibility, we invested in creating a framework called [Dart Frog](https://verygood.ventures/blog/dart-frog-1-0-release/) to try bringing the frontend and backend teams together. With the use of Dart Frog we see Engineering teams becoming not defined by the platform they write for, but by the product area they own. This is a cultural shift not just for the Engineering team but for the business overall. The guesswork of who does what part of an implementation is removed and instead of splitting up tickets, teams can get started doing the work quicker. ## Demo Application and Structure While we have been using Dart Frog for over a year now at VGV, and in successful projects including the Google News Toolkit and [I/O Flip](https://github.com/flutter/io_flip), it is still a bit of a new concept for some teams. In order to demonstrate how it can solve some of the issues we just talked about, we have created a simpler demo application, [VGV Recipes](http://recipes.vgv.dev).  While the application is just a basic Flutter app, meaning that it can easily be deployed to any supported platform, the real magic is the fact that the API server is developed right alongside the application in a shared repository. This means that items like the Dart Models can easily be shared between the two in order to keep them in sync at all times. This architecture also allows us to have a centralized point for our third party service integration. This means that we have the ability to keep our client application as light as possible and make quick and easy updates, fixes, and deployments via just our backend. ![VGV Recipes app architecture with shared Dart models between frontend and API](/assets/images/blog/from-idea-to-full-stack-solutions-using-dart-and-google-cloud/body-0.png) If a change happens, the contract between the application and the server doesn’t break. Even better, with the entire application stack running locally and using the magic of [Hot Reload](https://docs.flutter.dev/tools/hot-reload), you can easily make changes to preview and test with little to no downtime in your development process. ## Feature Addition Example So what does this look like in practice when a new requirement comes in? Let’s take a look at what it would take to add a new item to our recipes which we missed during our MVP, the total time a recipe takes to make. With Flutter and Dart we can break this into a few key steps 1. Add the field to the model - When we do this it will be done in the shared model between the frontend and backend keeping them in sync throughout the development process. 2. Modify the Backend Query - With the information available we just need to do a quick change to ensure that we are getting that information pulled into our response from the server. 3. Add a widget to the frontend - With the information needed being added into the response, it’s as easy as adding a Text widget to our UI with that new field. Through all of that, we didn’t have to restart the app or the API a single time! ## How To Get Started Hopefully, this way of thinking and example is a good illustration of how this shift in technology stack can help you in your current or future projects. Very Good Ventures has developed this perspective over numerous years and projects, and we see it as the next evolution of engineering teams overall. If you're interested in hearing more about this, don’t hesitate to [reach out](https://verygood.ventures/contact-us/) to have a quick conversation. ### Auth0 Flutter SDK vs API: Which one should you pick? Source: https://verygood.ventures/blog/auth0-flutter-sdk-vs-api-which-one-should-you-pick/ Auth0 is a flexible, drop-in solution designed to add robust authentication and authorization services to your applications, acting as the gatekeeper to secure access. Traditionally, securing an application involved developing both a frontend interface for user interactions, such as email and password authentication, and a backend infrastructure for securely storing sensitive information. Auth0 simplifies this process by providing a universal solution (Universal Login) that seamlessly integrates with your frontend, backend, and third-party tools, managing user identities with a secure, reliable infrastructure. Moreover, modern applications demand more sophisticated authentication methods beyond basic email and password login. This includes social provider authentication (e.g., Google, Apple), multi-factor authentication (MFA) methods like SMS codes or biometrics (e.g., fingerprint recognition), and more. Auth0 supports these advanced authentication needs out of the box, offering a streamlined experience for both developers and users. The way to access all these out-of-the-box features is through the Universal Login. It is a web page hosted by Auth0 where users can log in to your application. Auth0’s Universal Login defines your login flow. Whenever a user needs to prove their identity, your applications redirect to Universal Login, and then Auth0 will do what is needed to guarantee the user's identity. ![Auth0 Universal Login flow diagram showing redirect from app to Auth0](/assets/images/blog/auth0-flutter-sdk-vs-api-which-one-should-you-pick/body-0.png) Configuring Universal Login is dynamic (and does not require application-level changes) because all functionality is driven by the web pages served by the centralized Authentication Server. **Your applications benefit from any improvements Auth0 makes in the login flow without the need for you to change your code.** This means that when using Universal Login, you don't have to do any integration work to handle different methods of authentication. You can start off using a simple username and password, and then add other features, such as social login and multi-factor authentication (MFA). The Auth0 Dashboard allows you to customize the login page appearance and behavior creating a consistent, branded login experience. For advanced use cases, you can even add custom code to each page. Auth0 universal login includes the following features: - Username + Password Login - Socials Login - Passwordless/ One Time Password (code sent through email or SMS) - MFA challenge - Forgot Password - Sign Up By integrating Auth0 into your application, whether through its Flutter SDK or directly via the API, you can leverage this powerful solution, ensuring that your application's authentication and authorization mechanisms are both secure and user-friendly. ## Overview of Auth0 Flutter SDK 1. **What is Auth0 Flutter SDK? **Auth0 Flutter SDK is a package designed to seamlessly integrate Auth0 authentication and authorization features into Flutter applications. 2. **Key Features** ![__wf_reserved_inherit](/assets/images/blog/auth0-flutter-sdk-vs-api-which-one-should-you-pick/body-1.png) ^1^ Method exposed by the SDK which returns the user profile. Provides information such as id, email, name, phone number, etc. This collection of properties represents the authenticated user. ^2^ Refers to asking for fingerprint, face ID, pincode (depending on what’s supported by the device) before giving the user access to the app. Auth0’s implementation protects the stored token with the previously mentioned authentication methods. 3. **Pros of Using Auth0 Flutter SDK:** - **Ease of Integration:** Quick and straightforward to integrate with Flutter applications, reducing development time - **Pre-built UI Components:** Comes with ready-to-use authentication UI for the Universal Login, which can be customized to a certain extent as per the app design. - **Community and Support:** Access to the community and support from both Auth0 and Flutter developers. - **Ready to use solutions:**  In addition to the UI component, the Flutter SDK has pre-built solutions you can leverage in your app: - On mobile, it provides a Credentials manager for token storage and refreshing, as well as the ability to add LocalAuthentication. - On Web, the management and storage of credentials are handled internally by the underlying  [Auth0 SPA SDK](https://github.com/auth0/auth0-spa-js) including refreshing the access token when it expires. - Mobile also provides a convenient way to access API endpoints as methods (login, OTP, userProfile, MFA, token refresh, reset password, and sign up). ## Overview of Directly Using Auth0 API **1\. What Does it Mean to Use Auth0 API Directly? **The Authentication API enables developers to manage all aspects of user identity when using Auth0. It offers endpoints so users can log in, sign up, log out, access APIs, and more. The Authentication API is served over HTTPS. Developers will have to make their own implementation to interact with Auth0's RESTful APIs. **2\. Key Features: ** - Login (including UniversalLogin) ✅ - Logout ✅ - Passwordless / One Time Password (code sent through email or SMS) ✅ - SignUp ✅ - Change Password ✅ - User Profile ✅ - MFA ✅ - Management API ✅ **3\. Pros of Using Auth0 API Directly:** - **Custom Login:** Outside universal login, you can create your own custom UI and Login flow. This allows for full control over the authentication and authorization process. - Enables more complex scenarios and more control over those, beyond the pre-packaged solutions provided by SDKs. - **Programmatic Account Management:** Manage your Auth0 account programmatically through the Management API. This allows you to automate configuration of your environment. Most of the tasks you can perform in the Auth0 Management Dashboard can also be performed programmatically by using this API. ## Comparison: Auth0 Flutter SDK vs. Direct Auth0 API Use 1. **Ease of Use:** - SDK provides a higher-level abstraction with less setup time, ideal for standard use cases. - Direct API use requires more detailed implementation but offers more control. 2. **Customization and Flexibility:** - SDK has limitations in customization and might not support all Auth0 features. This is particularly true when talking about the Auth0 API, which the Auth0 Mobile SDK exposes but Auth0Web does not. That is true also for the mobile SDK which only exposes some of the Auth0 API methods. - Direct API allows for complete customization and access to the full range of Auth0 features. 3. **Maintenance and Support:** - SDK users benefit from updates and community support. **Notice that this could be a double edge sword since you depend on them making the updates.** - Direct API users need to manually manage updates and changes to Auth0 services. ## Conclusion Auth0 is a great tool to manage user authentication and authorization in your app. It can make your team avoid the cost, time, and risk that come with building your own solution. On one hand, leveraging the auth0\_flutter SDK will make it even easier and faster to integrate it into your app. On the other hand, directly using the Auth0 API will give your team more fine control over the solution and will allow for more custom or complex use cases. As you might have already noticed, the auth0\_flutter SDK exposes slightly different APIs for mobile platforms versus the web. So if your Flutter app is both mobile and web, you will have to interact with two different APIs (Auth0 and Auth0Web). Even though in general, they share almost the same features, they do still have slight differences that will need to be addressed when integrating it into your app. When deciding which approach is the best for a particular situation, I would ask myself the following questions: - **Does the Auth0 Universal Login experience align with the requirements of the project?** If it does, you probably are good to go by just using the Auth0 SDK since it’s easier and more straightforward to set up and use the universal login than the API. - **How much will I need to modify the experience, and can that level of modification be achieved by the customization options provided?**  If you need to make some modifications but it’s something we can achieve through the Auth0 dashboard, I would stick to the Auth0 SDK. If you need to make modifications outside the Auth0 dashboard, for example, customizing the Login flow outside the one defined by Auth0, you will need to resort to creating your own custom login using the Auth0 API. - **Will I use features outside the ones exposed by the SDK and do the exposed features correspond to my requirements?**  At this point, even though you are using the API methods, you could still use the SDK (at least the mobile one) that exposes some API methods. You just need to make sure that the methods exposed are enough to achieve the functionality you are looking for. If that is not the case, you probably need to do it through the REST API. - **Will I be working only on mobile or do I also expect a web version of the app?** If you expect your Flutter app to be for web and mobile, keep in mind that you will have to work with two different APIs of the SDK. This could potentially hinder the maintainability of the project. It could be that directly interacting with the REST API allows you to create a better abstraction for your needs. In the end, there is not one solution that fits all, but I hope these questions will point you on the right path. Other questions may arise in the pursuit of finding the best approach for your individual needs, so try to weigh the different pros and cons and make an educated decision. ### Behind the Unicorn Magic: Get to know the VGV Team Source: https://verygood.ventures/blog/behind-the-unicorn-magic-get-to-know-the-vgv-team-josh-musick/ ## Josh's Background J: I'm Josh Musick, Head of Design Services at VGV. Our primary focus is digital product design, with an emphasis on cross-platform and mobile applications.  I started working as a designer in the Web 1.0 era. Besides genuinely loving art and technology, I had an unexpected leg up in my entry into the profession in the form of a degree in philosophy. It gave me a strong foundation for working with abstract concepts and novel problem-solving, which were invaluable at a time when so much was still undefined about how technology might fit into our lives.  Throughout my career, I've had the opportunity to work on a diverse range of projects—web, mobile and desktop apps; enterprise-scale design systems, data platforms, hardware interfaces, and experience strategy—across a diverse array of verticals—fashion, media, e-commerce, finance, telecommunications, heavy industry, manufacturing, automotive, aerospace, building systems—for clients of all sizes—startups, NGOs, and SMBs and large enterprises. My experiences at frog and more recently, Raytheon Technologies, have equipped me with a broad skill set and a deep understanding of the intersection between design, technology, and business. ## Role at VGV J: My role at VGV is to lead the design team and make sure that the products we create meet the highest standards of quality and usability. We work hard to ensure that all of our designs can achieve user, business, and brand goals alike and that anything we create can be effectively used by our clients as their product grows over time.  I think what sets us apart from many competitors is our end-to-end approach. We’re a very good blend of a design consultancy like frog, where I worked for more than a decade, and a pure engineering services firm. Basically, we can handle any design problem from discovery and initial conception all the way through to product delivery. **How do you ensure VGV’s design process works for clients?** J: We try to keep our design process as user-centered as possible, but a thing that makes VGV different is the effort we put into client collaboration, which is at the heart of our design process at VGV. We work closely with cross-functional teams to ensure that our designs are strategically aligned and contribute to the overall success of our projects. We work hard to intimately understand what clients are trying to do and the constraints they’ve faced in trying to do it. We try to communicate as clearly as we can throughout the design process so that we’re certain we understand client objectives and the client understands how and why we’re making the decisions we are and the tensions we’re trying to resolve. This makes it more likely our designs are on target on the first pass and, if they’re not, subsequent feedback cycles are efficient and effective. All in all, it keeps us moving quickly.  Our ultimate goal is to build great products that users love and organizations can grow and manage effectively.  I believe that, at its best, design can deliver experiences that make people smarter and more capable of doing what they want to do in the world. When design works well, it addresses real-world problems through clear hypotheses that can be validated with data. When we’re able to measure outcomes, we can know our designs work for users and actually deliver intended experiential and business results. **Can you describe a VGV project you're particularly proud of?** J: One project that stands out to me is the [Flutter News Template](https://verygood.ventures/blog/flutter-news-toolkit-an-advantage-for-publishers-powered-by-vgv/). This project not only showcased our ability to deliver high-quality products rapidly but also had a significant impact on businesses worldwide. Seeing our work being used and appreciated by a diverse range of organizations is incredibly rewarding. We built something that was broadly extensible and usable by different kinds of businesses with various business models. On top of that, we did it the VGV way, really rapidly with consistently high-quality delivery. I'm also proud of the collaborative nature of our work at VGV. We have the opportunity to work with a wide range of clients and industries, tackling complex design problems and making a positive impact on the world around us. **What challenges do you find in your day-to-day role, and how do you address them?** J: One of the most common challenges we face in our role is aligning client aspirations with the realities of product development. This often involves managing expectations and finding creative solutions to technical or resource needs. Building strong relationships with clients and understanding their unique needs is key to overcoming these challenges. Another challenge is supporting teams with varying levels of design maturity. Some clients may have limited design resources or experience, which can lead to challenges in implementing design solutions effectively. Our approach involves providing support, guidance, and resources to help these teams succeed. **Are there any emerging technologies or trends you find particularly exciting?** J: There are two trends that I am particularly excited about: AI and low-code/no-code tools.  AI holds immense potential for revolutionizing the way we design and interact with digital products. From personalization to automation, AI-powered technologies can enhance user experiences in ways we never thought possible. I’ve seen demos recently where the capabilities are astonishingly rich. It fundamentally changes what a software experience can do for a person. We are at the very beginning as well; a lot of very smart people are working very hard on core capabilities and the services that deliver them. It feels like the smartphone revolution all over again, I think we’ll completely reimagine our relationship with computing over the next 5-10 years.  AI also will dramatically impact how software is made, which is something VGV cares about a great deal. It’s why we’re also interested in low- and no-code tools. A lot of enterprise use cases require bespoke interfaces customized to a particular task. It’s just not sustainable to build all of these in a discrete one-by-one fashion. For designers, these are a logical extension of design systems. Like design systems, these tools are meant to democratize the design and development process and make it more accessible to teams of all sizes. How these tools unfold, where they get adoption, and the specifics of how they work are an area of great interest for the team.  ## Looking Forward **What aspirations do you have for the impact of your work at VGV in the future?** J: My first goal is to build a great team; and continue helping my teammates to become better designers and innovators. For clients, I want to bring great products to market that smartly leverage emerging technologies, deliver key business results, and leave their users happy and productive. If we do things right, we have the opportunity to make a lasting impact on the industries we serve and the lives of the people who use those products. For VGV more broadly, I’d like to continue accelerating the rate at which we can deliver dev-ready designs. We’ve developed a pretty extensive playbook of methods that really increase our velocity, and I want to continue expanding upon that work. We generally want to make the organization work better and better. Since our design team is somewhat new, not everyone in the company has an intuitive sense of our capabilities yet.  I want everyone to understand the value Design brings to programs and to find synergies with our talented Engineering and Product teams to further optimize how we deliver.  I'm excited to see what the future holds as we continue to push the boundaries of design and technology. ## Who is Josh beyond VGV? - Josh is a dad, designer, creative leader and educator. - He spent decades working in NYC at Organic, frog design, Raytheon Technologies’ Digital Accelerator in Brooklyn and teaching at the School of Visual Arts’ MFA Program in Interaction Design. - He currently resides in Columbus Ohio with his wife and their two kids. He likes to cook for his family, usually listening to music, podcasts and standup comedy while he does it. - At this point in his life, Josh is intensely boring and he feels pretty good about it. ### Web App vs. Mobile App Development: What's the Difference? Source: https://verygood.ventures/blog/web-app-vs-mobile-app-development-whats-the-difference/ When your organization releases a custom application, it's important to get that software to the largest audience possible. Today, that means targeting multiple platforms without straining your development resources. But where does the web fit in? A web application, accessed through an app user's web browser, has a very low barrier for entry. While an Android or iOS app has to be downloaded through the Google Play Store or Apple App Store, a web app is always there — just enter a web address and go. So, should you make a web app part of your outreach strategy? Is this a suitable specific platform for you to target alongside Android and iOS? What if you want to develop MacOS or Windows desktop app versions of your product? Getting some clarity about what web apps can accomplish for you, and the best practices for developing them, can help you plan your strategy. And, if you do decide that building a web app is right for you, you'll be pleased to find that it's easier than ever to develop for the web alongside other platforms, thanks to the current generation of multi-platform development frameworks, [prominently including Flutter](https://flutter.dev/multi-platform/web). First, it's best to lay down some definitions. How does a web app stack up next to a conventional mobile application? ## Web App and Mobile App: Compare and Contrast Developing a web application has the same general purpose as creating a mobile app: providing a positive, friction-free app user experience and a way to interact with your brand. When you want to give your audience the chance to access an app experience without downloading anything, building a web app is the clearest way forward. Despite the fact that web apps are accessible through an ordinary web browser, it's important to note that they are indeed more like applications than standard web pages. This means they're not necessarily very useful for search engine optimization or inbound marketing, as Flutter's developers note — they're there to [provide a dynamic experience](https://docs.flutter.dev/platform-integration/web/faq) and an accessible user interface. As app experiences, in general, become integral parts of companies' outreach activities, web apps ensure these apps have the maximum possible reach. When considering whether your organization's app should have a web version, it's worth asking how much extra traffic you could capture by building the new product. It's worth noting that under a multi-team native app development model, expanding to the web would require extra headcount and engineering effort, which could take away focus from other platforms. In the era of multi-platform development ushered in by Flutter and similar frameworks, however, the math has changed. [Flutter's nature as a build-once, run-anywhere framework](https://verygood.ventures/blog/benefits-of-flutter-for-cross-platform-app-development/) foregrounds time- and money-saving single-team models. When you can create a powerful web app from the same codebase as the Android and iOS versions, your team gains the primary advantage of a web app — a large new potential audience with an extremely low barrier to entry — without the drawback of staffing up a new, siloed app developer team. *Find out how an* [*App performance is intrinsically linked to the customer experience today.*](https://verygood.ventures/blog/very-good-app-development-boosts-digital-customer-experience-heres-how/) ## Multi-Platform Development: Bringing Web and Mobile Apps Together So, just how simple is it to create a web version of a multi-platform app when developing in Flutter? Thanks to the fact that the framework specifically supports building web apps, this process is an easy add-on to mobile app development. Web app support has been added to Flutter since its debut in 2018, along with desktop app support for MacOs, Windows and Linux. The process for web app creation is the same as creating an app for any of Flutter's other supported platforms. In addition to creating a new app for multiple platforms, including the web, it's also possible to retroactively add web support to existing apps initially created with older versions of Flutter. With one codebase for the web version of the app as well as any and all other versions, it's possible to run a more efficient build process, getting the application into production sooner. This process — of releasing a fully featured application across multiple platforms without taking engineering hours away from quality assurance and additional feature development — allows you to reach the maximum possible audience quickly while delivering a high-quality user interface across all channels. ### Platform Differences and Development Musts There are a few extra considerations that go into [getting a multi-platform app ready to run on the web](https://docs.flutter.dev/platform-integration/web/renderers). For instance, engineers need to select which rendering engine they'll use to display content. The Flutter team recommends sticking with the defaults: the HTML renderer (which minimizes download size) when accessed on a mobile browser, the CanvasKit renderer (which optimizes performance) for desktop and laptop browsers. There is also the matter of [the way images are handled](https://docs.flutter.dev/platform-integration/web/web-images) in web apps. There is more freedom regarding image display options in both the desktop and mobile versions of apps compared to web applications. Flutter offers several recommended solutions, depending on where and how the developer team decides to host the necessary images. There are a few other minor differences — for example, Flutter's hot reload feature does not work in web browsers at the moment. However, the basics are unchanged across each different platform, meaning the same streamlined Flutter engineering process that works for mobile device app development will also help your organization deliver web experiences. *Learn more about our Very Good approach to* [*multi-platform app development best practices*](https://verygood.ventures/solution/build-an-app/)*.* ## Developing Ideal Multi-Platform Apps with Flutter Choosing to develop a multi-platform app with Flutter comes with advantages that extend from the pre-build visualization process to the ongoing maintenance phase once the app is in production. As long as your team is willing to embrace the relevant best practices, the resulting application can become a tentpole of your communications for years to come. Some of the specific advantages of engaging in web app development as part of a multi-platform Flutter strategy include: ### Quicker path to launch Whether you're bringing an existing mobile app to web browsers or launching a whole new application on multiple platforms at once, less time leading up to launch is a major advantage. Working on a single codebase, with one unified development team, is a way to add speed and efficiency. ### Simple conversion from web to multi-platform If your organization has an existing web app, it already has much of the business logic, authentication and UI necessary to run across platforms. In Flutter, it's simple to add dynamic scaling to the UI, allowing you to reach mobile audiences without extensive rewriting. This presents a significant advantage as it leads to substantial cost savings for the company, given that this task typically consumes the most time to complete. ### Easier bug fixes When there are multiple codebases for each different platform's version of an app, patching a single problem depends on coordination between teams that may be misaligned and siloed. The extra time and effort expended on squashing bugs can then make it harder to execute a feature roadmap. ### Scalability Over Time A codebase built on Flutter with best practices tends to be simple, elegant and "boring." This focus on straightforward code reduces tech debt and can allow your team to add new features and capacity to a web or mobile application, delivering a great user experience year after year. ### Access to a Strong Community The community devoted to creating open-source components for Flutter is large and helpful, ensuring that a developer is never alone when trying to add novel new functionality to a web app. The Google Flutter team, too, is highly responsive to feedback from engineers regarding features and updates. ### Full Support Across Platforms The web, MacOS, Windows and Linux — each different platform is fully featured for Flutter app development, rather than acting as an afterthought to iOS and Android. This is one area where Flutter differs from React Native — for that framework, functionality for non-mobile platforms was added by the community. ### Possibilities Beyond Mobile Devices and PCs While developing a web application helps your app reach a wide swath of your audience, sometimes there's a need to go further, for example, creating a version of a smart device control panel or even a vehicle console. Multi-platform development with Flutter can encompass these touchpoints. ### Powerful User Interface Capabilities Apps today have to be attractive and perform well to catch users' attention. Apps built in Flutter take full advantage of the power of their respective platforms, as if they were native mobile apps, despite being built as a single codebase in the Dart programming language. *Want to see Flutter app development in action? See how to develop* [*Flutter apps for wearable devices*](https://verygood.ventures/blog/building-wear-os-apps-with-flutter-a-very-good-guide/)*.* ## Ready to Develop High-Quality Web and Mobile Apps? The audience for your next app, whether it's made up of internal users or external customers, will be happy to engage with powerful, consistent software built in Flutter. This will remain true over time as the single codebase and build-once, run-anywhere nature of the framework enable effective updates and future-proofing. Now it's time to ensure the process is carried out according to the best practices of multi-platform app development. Whether your development team is entirely in-house or you engage with third parties on your projects, it may be worth getting expert opinions, consultation and support as you bring Flutter into your workflow.  Shifting a present native app to Flutter, or adding the web as a specific platform for an existing piece of software, could be a valuable introductory product. Alternatively, you could dive in with a brand-new multi-platform concept. The choice depends entirely on your needs and those of your audience. ### Integrating Gemini AI with Flutter Source: https://verygood.ventures/blog/integrating-gemini-ai-with-flutter/ Now, with the new [Beta version](https://blog.google/technology/ai/google-gemini-next-generation-model-february-2024/) of the Gemini Dart SDK plug-in, developers can seamlessly integrate AI capabilities into their Flutter applications. Let’s dive into the power and best practices of Gemini AI to introduce new functionalities to create much richer digital experiences. Having access to Google's generative AI SDK allows new possibilities in app development, as well as enhancing existing functionalities. We will showcase how to integrate Gemini AI into a Flutter app. ## Getting an API key Before diving into the code, we have to get an API key from [Google AI Studio](https://makersuite.google.com/app/apikey). If you are having issues trying to access Google AI Studio with a work account, you can either contact your administrator to enable Early Access apps or use a personal Gmail account, since those have the access enabled already.  ![Google AI Studio interface for creating a Gemini API key](/assets/images/blog/integrating-gemini-ai-with-flutter/body-0.gif) ## Adding the SDK to an app We will be using the [Google Generative AI SDK for Dart](https://pub.dev/packages/google_generative_ai), which is already available in pub.dev. To use the package in your app, add the dependency to your pubspec.yaml file. ```dart dependencies: google_generative_ai: ^0.2.0 ``` Remember to check the [package's latest instructions](https://github.com/google/generative-ai-dart?tab=readme-ov-file#using-the-sdk-in-your-own-app) on how to add it to your project if you are having issues. ## Best practices Now that we have the package available in the app, let's see how to use it effectively.  ### Creating a Gemini client At VGV we believe that a [layered architecture](https://verygood.ventures/blog/very-good-flutter-architecture/) is the best way to build maintainable, testable, performant, and scalable code. Following this pattern, we will create a GeminiClient that will be responsible for interacting with the Generative AI SDK and parsing the responses as needed. ```dart class GeminiClient { GeminiClient({ required this.model, }); final GenerativeModel model; Future generateContentFromText({ required String prompt, }) async { final response = await model.generateContent([Content.text(prompt)]); return response.text; } } ``` In this example, we're just showing how to generate content from a text prompt, but we encourage you to play with other types of content such as images with the Content.multi() constructor, or even starting a chat session with Gemini with the  \_model.startChat() method! ### Provide the API key securely The API key accesses the model on your behalf, you should keep it safe and private. One way of doing so is by providing it as an environment variable when running our app. First, we will build the logic in our app to retrieve it. ```dart void main() { final geminiApiKey = Platform.environment['GEMINI_API_KEY']; if (geminiApiKey == null) { throw Exception('Gemini API key not found'); } final model = GenerativeModel(model: 'gemini-pro', apiKey: geminiApiKey); final gemini = GeminiClient(model: model); runApp( Provider( create: (_) => geminiClient, child: const App(), ), ); } ``` Then, when running your app, provide the key. ```dart flutter run --dart-define="GEMINI_API_KEY=" ``` ### Organize and add context to prompts There are plenty of use cases for generative AI to be used in an app, but don’t miss the opportunity to combine user prompts with additional context. Let’s say that you have a story-teller feature in your app powered by Gemini. The user can prompt a summary of the plot or some events that the story should have, but you, as the developer, can add some context to enhance the story and add an overarching theme before sending the request to Gemini. This and other techniques are explained more deeply in the [Gemini API documentation](https://ai.google.dev/docs/prompt_best_practices). ## Let’s see it in action ![Flutter app demo generating an AI story with Gemini from a text prompt](/assets/images/blog/integrating-gemini-ai-with-flutter/body-1.gif) ### Maximizing Success: The Power of Integrated Design and Development Agencies Source: https://verygood.ventures/blog/maximizing-success-the-power-of-integrated-design-and-development-agencies/ [*This article was posted on Laura’s blog first*](https://www.linkedin.com/pulse/maximizing-success-power-integrated-design-agencies-laura-hughes-i9uic%3FtrackingId=n7TC3aWSp5IXUhotzuByVQ%253D%253D/?trackingId=n7TC3aWSp5IXUhotzuByVQ%3D%3D)*.* In today's competitive digital landscape, businesses are constantly seeking ways to stand out and deliver exceptional user experiences. One often overlooked yet crucial factor in achieving this goal is the collaboration between design and development teams. **Integrated agencies that offer both design and development services under one roof are proving to be invaluable partners for companies looking to thrive in the digital realm.** Since adding design expertise to our team at Very Good Ventures, we've experienced a profound improvement in our ability to streamline projects and meet client goals with greater speed and precision. This strategic move has allowed us to enhance communication, foster seamless collaboration, and deliver consistently exceptional results. In this article, we'll delve into why businesses are increasingly turning to integrated agencies like ours to enhance their digital presence. From streamlined communication and seamless collaboration to consistent brand experiences and long-term partnership opportunities, we'll uncover the myriad benefits of choosing an integrated design and development agency like Very Good Ventures. ## Streamlined Communication Effective communication is the cornerstone of any successful project, and integrated agencies excel in this regard. With designers and developers working side by side, communication barriers are eliminated, leading to clearer requirements, faster decision-making, and ultimately, smoother project execution. This seamless collaboration ensures that everyone is aligned from the outset, resulting in fewer misunderstandings and delays. ## Seamless Collaboration Gone are the days of passing design mockups back and forth between different vendors. Integrated agencies facilitate seamless collaboration between designers and developers throughout the project lifecycle. From initial concept ideation to final product delivery, these teams work hand in hand, ensuring that design decisions are implemented faithfully and technical constraints are addressed proactively. This close partnership fosters a culture of creativity and innovation, where ideas flow freely, and the end result is greater than the sum of its parts. ## Consistent Brand Experience In today's omnichannel world, maintaining a consistent brand experience across all digital touch points is paramount. Integrated agencies are well-positioned to deliver on this front, thanks to their holistic approach to design and development. By aligning brand guidelines with technical implementation, these agencies ensure that every interaction with the brand is cohesive, memorable, and on-brand. Whether it's a website, mobile app, or digital marketing campaign, customers can expect a seamless experience that reinforces brand identity and builds trust. ## Efficiency and Cost-Effectiveness Time is money, and integrated agencies understand the value of both. By offering design and development services under one roof, these agencies eliminate the need for multiple vendors and the associated overhead costs. This streamlined approach not only saves time but also reduces project management complexity, allowing resources to be allocated more efficiently. As a result, projects are completed faster, on budget, and with fewer headaches along the way. ## Flexibility and Adaptability In today's fast-paced digital landscape, flexibility is key. Integrated agencies are adept at adapting to changing project requirements and client needs thanks to their agile workflows and cross-functional teams. Whether it's accommodating last-minute design changes or pivoting in response to user feedback, these agencies are nimble enough to keep pace with evolving project dynamics without missing a beat. ## Quality Assurance and Testing Quality is non-negotiable in the world of digital product development, and integrated agencies take quality assurance seriously. By involving designers and developers in the testing process from the get-go, these agencies ensure that products are thoroughly vetted for usability, functionality, and performance. This collaborative approach to quality assurance results in higher-quality products with fewer bugs and issues, ultimately leading to greater customer satisfaction and loyalty. ## Long-Term Partnership Opportunities Beyond individual projects, integrated agencies offer the opportunity for long-term partnerships that extend far beyond the initial engagement. By becoming trusted advisors to their clients, these agencies can provide ongoing support and guidance on digital strategy, helping businesses navigate the ever-changing digital landscape with confidence and clarity. In conclusion, the benefits of choosing an integrated design and development agency are clear. From streamlined communication and seamless collaboration to consistent brand experiences and cost-effective solutions, these agencies offer a compelling value proposition for businesses looking to succeed in the digital age. By harnessing the power of integration, companies can unlock new opportunities for innovation, growth, and success. ### Exploring Accessibility and Digital Inclusion with Flutter Source: https://verygood.ventures/blog/exploring-accessibility-and-digital-inclusion-with-flutter/ ## What is Digital Accessibility?  Accessibility is the practice of making software usable for people with disabilities. It enables them to perceive, understand, navigate, and interact with software, allowing them to be active and contributing members of the digital world. ## Why is it important to be aware of accessibility? According to the World Health Organization (WHO), an estimated 1.3 billion people experience permanent disability. This represents 16% of the world's population or, in other words, one in six of us. This number is very significant: When creating a new application, it's important to consider whether people with disabilities will be able to use your product or if they will face difficulties. Some examples of permanent disabilities are: - Loss of motion or strength in a body part - Loss of Hearing or sight - Loss of limb Additionally, **temporary disabilities** can affect how the user interacts with the product over a period of time, so we must be careful with these cases as well. Some examples of temporary disabilities are: - Broken bones - Back/neck injury - Surgery rehabilitation Technology teams should care about creating products for everybody, regardless of whether they have disabilities or not, which they can use and enjoy. ## About WCAG 2.1  [The Web Content Accessibility Guidelines (WCAG) 2.1](https://www.w3.org/TR/WCAG21/) are guidelines for making web content more accessible. This allows users with disabilities to use these products by including accommodations for blindness and low vision, deafness and hearing loss, limited movement, speech disabilities, photosensitivity, learning disabilities, cognitive limitations, etc. To follow these guidelines, applications should be: - **Perceivable**: The information and components of the user interface must be presentable to users in a way that they can perceive them. - **Operable**: User interface components and navigation must be viable. - **Understandable**: Information and the operation of the user interface must be understandable. - **Robust**: Content must be robust enough that it can be interpreted by a wide variety of user agents, including assistive technologies. ### Conformance Level These guidelines are differentiated into the following levels: **Level A (basic accessibility)**: The least strict. If your product does not pass this level you have a big accessibility problem that must be solved. At this level, there are 30 criteria that the product must meet, for example: - Non-text content (images and videos) must have a text equivalent. - Forms must include labels or instructions. - Assistive technologies, such as screen readers, must be able to access content. - The information must not be conveyed through shape, size, or color alone. **Level AA (Strong Accessibility):** This is the recommended conformance level. At this level, the content of the product would satisfy all the criteria between Level A and Level AA. Some examples at Level AA are: - Elements that affect navigation should be consistent across the site. - Content should be organized on headings - this will be very important when the user uses the screen reader. - The visual presentation of text and images of text has a [contrast ratio](https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio) of at least **4.5:1**, except for the following: **Large Text** - [Large-scale](https://www.w3.org/TR/WCAG21/#dfn-large-scale) text and images of large-scale text have a contrast ratio of at least 3:1. **Incidental** - Text or images of text that are part of an inactive [user interface component](https://www.w3.org/TR/WCAG21/#dfn-user-interface-components), that are [pure decoration](https://www.w3.org/TR/WCAG21/#dfn-pure-decoration), that are not visible to anyone, or that are part of a picture that contains significant other visual content, have no contrast requirement. **Logotypes** - Text that is part of a logo or brand name has no contrast requirement. **Level AAA, the highest level of accessibility**: This is the highest level to meet in our product, but this level is quite difficult to meet in all products, depending on different factors like budget, time, etc. Of course, you can implement some of them even if you don't fully meet the level AAA, everything counts. Some examples are: - The contrast ratio between text and background is at least 7 to 1. - Extended audio descriptions should be provided for pre-recorded videos. For more information and all the different levels' criteria, please visit the [WCAG 2.1 documentation](https://www.w3.org/TR/WCAG21/#intro). ## What does Flutter say in its official documentation? Flutter is very aware of accessibility, so it provides developers with good documentation to help us understand what is needed to implement accessibility (a11y) in our applications, and how to do it, and also provides us with a checklist so we can know if we are meeting the criteria. ## Accessibility release checklist Here is a non-exhaustive list of things to consider as you prepare your app for release: - **Active interactions**: Ensure that all active interactions do something. Any button that can be pushed should do something when pushed. For example, if you have a no-op callback for an onPressed event, change it to show a SnackBar on the screen explaining which control you just pushed. - **Screen reader testing**: The screen reader should be able to describe all controls on the page when you tap on them, and the descriptions should be intelligible. Test your app with [TalkBack](https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback&hl=en_US) (Android) and [VoiceOver](https://www.apple.com/lae/accessibility/iphone/vision/) (iOS). - **Contrast ratios**: The contrast ratio should be at least 4.5:1 between controls or text and the background, except for disabled components. Images should also be vetted for sufficient contrast. - **Context switching**: Nothing should change the user’s context automatically while typing in information. Generally, the widgets should avoid changing the user’s context without some sort of confirmation action. - **Tappable targets**: All tappable targets should be at least 48x48 pixels. - **Errors**: Important actions should be able to be undone. In fields that show errors, suggest a correction if possible. - **Color vision deficiency testing**: Controls should be usable and legible in colorblind and grayscale modes. - **Scale factors**: The UI should remain legible and usable at very large scale factors for text size and display scaling. ### Large fonts We need to ensure the layout has enough space to render all of its content when the font size is increased. In Flutter, text size calculations are handled automatically, but for example, the icon size is not handled automatically so I created an AppIcon widget that adapts to the current font size. As you can see in the example, we need to use textScaleFactor from MediaQuery to scale based on the fontSize. If you want to know more about textScaleFactor , this is an interesting [article](https://docs.flutter.dev/release/breaking-changes/deprecate-textscalefactor) you can check out. ```dart class AppIcon extends StatelessWidget { const AppIcon({ required this.icon, required this.fontSize, required this.label, super.key, }); final IconData icon; final double fontSize; final String label; @override Widget build(BuildContext context) { final textScaleFactor = MediaQuery.textScalerOf(context); return Icon( icon, semanticLabel: label, size: textScaleFactor.scale(fontSize) * 1.5, ); } } ``` *Code sample to change textScaleFactor* ## Demo font scaling ![Flutter app demo showing UI scaling with increasing font size settings](/assets/images/blog/exploring-accessibility-and-digital-inclusion-with-flutter/body-0.gif) *Demo font scaling.* ### Screen readers These are tools that allow visually impaired users to receive spoken feedback on-screen content and interact with the user interface using gestures on mobile and keyboard shortcuts on desktop. There are different types depending on the device the user is using: - **Android**: TalkBack. - **iPhone**: VoiceOver. - **Browsers**: - **Mobile browser**: VoiceOver or TalkBack. - **Desktop browser**: VoiceOver or JAWs & NVDA. - **Desktop**: VoiceOver or JAWs & NVDA. ### Sufficient contrast Sufficient contrast is useful for users with various visual impairments when viewing the interface on devices in extreme lighting conditions, such as direct sunlight, because it makes text and images easier to read. The [W3C recommends](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html): - At least 4.5:1 for small text (below 18 point regular or 14 point bold). - At least 3.0:1 for large text (18 points and above regular or 14 points and above bold). ## How to Implement Accessibility in Flutter Implementing a11y in Flutter is easy. It is recommended to implement it from the beginning of the application because it will be easier, but if you can't, don't worry, you can also do it later. Flutter widgets generate an accessibility tree automatically. However, if your app needs something different, you can customize it using the Semantics widget. This widget annotates the widget tree with a description of what the widgets mean. Let's do an example of using semantic widgets. This example has been tested on a real Android device. Remember it is important to use real devices instead of simulators to test accessibility. There are some widgets that you can use to add accessibility to your application. It will depend on what you need at all times, some of them are: - **Semantics**: is a widget that annotates the widget tree with a description of the meaning of the widgets. This widget has a lot of properties, [you can check them here](https://api.flutter.dev/flutter/semantics/SemanticsProperties-class.html). ```dart class ItemFavoriteButton extends StatelessWidget { const ItemFavoriteButton({ required this.dog, super.key, }); final Dog dog; @override Widget build(BuildContext context) { final favoriteDogs = context.select((HomeBloc bloc) => bloc.state.favoriteDogs); final isFavorite = favoriteDogs.contains(dog); final onTapHint = isFavorite ? 'Remove ${dog.title} from favorites' : 'Add ${dog.title} to favorites'; final label = isFavorite ? 'Button to remove ${dog.title} from favorites' : 'Button to add ${dog.title} to favorites'; const iconLabel = 'Heart icon'; return Align( alignment: Alignment.centerRight, child: Semantics( liveRegion: true, button: true, label: label, selected: isFavorite, onTap: () => _updateFavorites(context), onTapHint: onTapHint, child: IconButton( icon: AppIcon( icon: isFavorite ? Icons.favorite : Icons.favorite_border, fontSize: 14, label: iconLabel, ), onPressed: () => _updateFavorites(context), ), ), ); } void _updateFavorites(BuildContext context) { context.read().add( UpdateFavoriteRequested(dog: dog), ); } } ``` In this example, we are using the Semantic widget to add accessibility to an ItemFavoriteButton widget. In this case, we are using some properties of this widget such as: - **Button**: It represents a button in this subtree. - **Label:** Provides a description of the widget. - **Selected:** It indicates that this subtree represents a widget that can be selected/unselected. - **onTap:** handler for the SemanticsAction.onTap operation. It can be triggered by double-tapping in VoiceOver or TalkBack. - **onTapHint:** the hint of the onTap action. In the following example of the ItemCard widget we will see more types of widgets that we can use to add accessibility to your application: - **MergeSemantics**: This widget is very useful with screen readers because it allows you to stack other widgets together and read them all at once; In more technical words, it is a widget that marks a subtree as a single node for accessibility purposes. - **IndexedSemantics**: Creates a widget that annotates the first child semantics node with an index. Semantic indexes are used by **TalkBack/Voiceover** to make announcements about the current scroll state. ```dart class ItemCard extends StatelessWidget { const ItemCard({ required this.dog, super.key, }); final Dog dog; @override Widget build(BuildContext context) { return Card( clipBehavior: Clip.hardEdge, elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), margin: const EdgeInsets.all(8), child: MergeSemantics( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Flexible( child: IndexedSemantics( index: 0, child: ItemCardImage( image: dog.image, label: dog.imageLabel, ), ), ), const ExcludeSemantics(child: SizedBox(height: 8)), Flexible( child: IndexedSemantics( index: 1, child: ItemCardTitle(title: dog.title), ), ), const ExcludeSemantics(child: SizedBox(height: 16)), Flexible( child: IndexedSemantics( index: 2, child: ItemCardDescription(description: dog.description), ), ), const ExcludeSemantics(child: SizedBox(height: 16)), Flexible( child: IndexedSemantics( index: 3, child: ItemFavoriteButton(dog: dog), ), ), ], ), ), ); } } ``` - **ExcludeSemantics**: This widget will exclude your child's reading when using screen readers. ```dart class NoFavoritesView extends StatelessWidget { const NoFavoritesView({super.key}); @override Widget build(BuildContext context) { return Center( child: MergeSemantics( child: Semantics( liveRegion: true, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'No favorites, please go to Home Page to add more ' 'dogs to favorites', textAlign: TextAlign.center, ), const ExcludeSemantics(child: SizedBox(height: 16)), ElevatedButton( onPressed: () => context .read() .add(ChangeSelectedIndexRequested(index: 0)), child: const Text('Go to Home Page'), ), ], ), ), ), ); } } ``` Also, some of the Material widgets have properties to help simply implement a11y, for example with semanticsLabel on the Text or Image widgets. ```dart class ItemCardImage extends StatelessWidget { const ItemCardImage({ required this.image, required this.label, super.key, }); final String image; final String label; @override Widget build(BuildContext context) { return Image.asset( image, fit: BoxFit.fill, semanticLabel: label, ); } } ``` ### Demo with TalkBack (screen reader on Android) If you would like to see the demo and hear how **TalkBack** helps the user identify the different parts of the application, [visit the project readme file here](https://github.com/VGVentures/accessibility_example/blob/main/README.md). [Demo Talkback](https://github.com/VGVentures/accessibility_example/blob/main/README.md) **Test your Semantics widgets**  Here we are going to learn how to test a widget that uses semantics and also check if this widget meets the [guidelines](https://api.flutter.dev/flutter/flutter_test/AccessibilityGuideline-class.html) to be an accessible widget: - [androidTapTargetGuideline](https://api.flutter.dev/flutter/flutter_test/androidTapTargetGuideline-constant.html), which checks that tappable nodes have a minimum size of 48 by 48 pixels. - [iOSTapTargetGuideline](https://api.flutter.dev/flutter/flutter_test/iOSTapTargetGuideline-constant.html), which checks that tappable nodes have a minimum size of 44 by 44 pixels. - [textContrastGuideline](https://api.flutter.dev/flutter/flutter_test/textContrastGuideline-constant.html), which provides guidance for text contrast requirements specified by [WCAG](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef). - [labeledTapTargetGuideline](https://api.flutter.dev/flutter/flutter_test/labeledTapTargetGuideline-constant.html), which enforces that all nodes with a tap or long press action also have a label. In this case, we are going to test the ItemFavoriteButton that I added earlier in the example. First, we need to check if the widget meets the guidelines: ```dart testWidgets('meets guidelines', (tester) async { final handle = tester.ensureSemantics(); await tester.pumpApp( ItemFavoriteButton(dog: dog), homeBloc: homeBloc, ); // Checks that tappable nodes have a minimum size of 48 by 48 pixels // for Android. await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); // Checks that tappable nodes have a minimum size of 44 by 44 pixels // for iOS. await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); // Checks that touch targets with a tap or long press action are labeled. await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); // Checks whether semantic nodes meet the minimum text contrast levels. // The recommended text contrast is 3:1 for larger text // (18 point and above regular). await expectLater(tester, meetsGuideline(textContrastGuideline)); handle.dispose(); }); ``` Then we need to check if the widget matches the semantics: ```dart testWidgets('matchSemantics when add to favorites', (tester) async { final handle = tester.ensureSemantics(); await tester.pumpApp( ItemFavoriteButton(dog: dog), homeBloc: homeBloc, ); final item = find .ancestor( of: find.byType(IconButton).first, matching: find.byType(Semantics), ) .first; expect( tester.getSemantics(item), matchesSemantics( hasTapAction: true, isButton: true, isLiveRegion: true, onTapHint: 'Add ${dog.title} to favorites', ), ); handle.dispose(); }); ``` As you can see, you need to use a tester.ensureSemantics, it is required to call to dispose method at the end of the test. To test the onTap property in the semantic widget, you need to use the performAction and SemanticsAction.tap, you can see the example below. ```dart testWidgets('onTap semantics', (tester) async { final handle = tester.ensureSemantics(); await tester.pumpApp( ItemFavoriteButton(dog: dog), homeBloc: homeBloc, ); final item = find .ancestor( of: find.byType(IconButton).first, matching: find.byType(Semantics), ) .first; final semantics = tester.getSemantics(item); tester.binding.pipelineOwner.semanticsOwner!.performAction( semantics.id, SemanticsAction.tap, ); verify(() => homeBloc.add(UpdateFavoriteRequested(dog: dog))).called(1); handle.dispose(); }); ``` ## Check your semantic widgets with the debugger If you want to visually check how your semantic widgets are working, you can use the showSemanticsDebugger property on your MaterialApp widget. ```dart class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return MaterialApp( showSemanticsDebugger: true, // add this line theme: ThemeData( appBarTheme: const AppBarTheme(color: Color(0xFF13B9FF)), colorScheme: ColorScheme.fromSwatch( accentColor: const Color(0xFF13B9FF), ), ), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: BlocProvider( create: (context) => HomeBloc()..add(DogsRequested()), child: const HomePage(), ), ); } } ``` This is the result. ![Flutter app with showSemanticsDebugger overlay showing semantic widget boundaries](/assets/images/blog/exploring-accessibility-and-digital-inclusion-with-flutter/body-1.png) *HomePage with showSemanticsDebugger* ## Accessibility example app - Github You can [check the full example here](https://github.com/AnnaPS/accessibility_example). ## Interesting resources about accessibility ### Talks - [Building in Accessibility with Flutter](https://www.youtube.com/watch?v=bWbBgbmAdQs) - Google devs - Flutter Interact '19 - [Stop treating accessibility as an afterthought: concrete steps to build inclusive apps](https://www.droidcon.com/2023/08/07/stop-treating-accessibility-as-an-afterthought-concrete-steps-to-build-inclusive-apps/) - Manuela Sakura Rommel - FlutterCon ‘23 - [Accessibility features and how to implement them in Flutter](https://www.youtube.com/watch?v=sO97YsPxwsw) – M. Wróblewski - MonteTalks: Mobile - [Accessibility in Flutter](https://www.youtube.com/watch?v=Ywgt3cRKyrs) - Lara Martín & Miguel Beltran - Skills Matter ### Articles - [A deep dive into Flutter’s accessibility widgets](https://medium.com/flutter-community/a-deep-dive-into-flutters-accessibility-widgets-eb0ef9455bc) - Muhammed Salih Guler - [Flutter: Crafting a great experience for screen readers](https://blog.gskinner.com/archives/2022/09/flutter-crafting-a-great-experience-for-screen-readers.html) - Shawn Blais - [Material Documentation: Understand accessibility](https://m2.material.io/design/usability/accessibility.html#understanding-accessibility) - [European Accessibility Act](https://ec.europa.eu/social/main.jsp?catId=1202) - [United Nations: Article 9 - Accessibility](https://www.un.org/development/desa/disabilities/convention-on-the-rights-of-persons-with-disabilities/article-9-accessibility.html) - [Mobile Experiences For Businesses: How To Boost Accessibility And Revenue](https://www.forbes.com/sites/forbescommunicationscouncil/2023/02/28/mobile-experiences-for-businesses-how-to-boost-accessibility-and-revenue/?sh=732b5e386a2b) - Vincent Phamvan - Forbes Council Member This is just an example of how to start implementing accessibility in a Flutter application, there are more things to consider to meet at least Level AA conformance. ### Very Good Ventures: Official Google Cloud Partner at Google Cloud Next 2024 Source: https://verygood.ventures/blog/very-good-ventures-official-google-cloud-partner-at-google-cloud-next-2024/ Our mission has always been to help companies ship world-class user experiences with quality, speed, and productivity at the forefront. And this year, we are ambitious in expanding our capabilities to help more companies with their software development and user experience needs.  To that point, we’re excited to announce that Very Good Ventures has officially become a Google Cloud Partner, marking a significant milestone in our journey as the leading Flutter consultancy. With this partnership, we extend our commitment to helping companies deliver world-class applications while leveraging Google Cloud's robust infrastructure and cutting-edge technologies. The partnership opens doors to exciting opportunities, particularly in assisting Google Cloud customers in crafting intuitive user interfaces for their AI-powered and cloud-based applications. This means more than just growth; it signifies a leap forward in product development, empowering businesses to innovate and thrive in their digital transformation efforts. [Check out our latest whitepaper to see how Flutter can help your business innovate!](https://hubs.ly/Q02kW1ZG0)  Accelerating our commitment, Very Good Ventures is thrilled to announce our official sponsorship of Google Cloud Next 2024, where our team of experts and leaders will be present and active on the show floor. For our team, it's a great opportunity to engage with some of our clients and the community in the vibrant ecosystem of technology enthusiasts and visionaries. We’re eager to learn how Google Cloud customers are harnessing technologies like Gemini and Flutter to redefine user experiences and build next-generation applications.  And there’s more: Very Good Ventures is proud to showcase its expertise in user experience and application development through two speaker sessions at the Google Cloud Next Community Hub. These sessions will feature our world-class experts building real world applications with technologies like Gemini and Flutter. These sessions promise to delve deep into the intricacies of leveraging AI and cloud technologies to create the best user experiences. At Very Good Ventures, we are a curious, innovative, and passionate bunch of unicorns. We’re built to dream and fly. Google Cloud Next 2024 and its attendees embody these attributes well, and we can’t wait to explore the limitless possibilities we can achieve together. We can’t wait to see you all in Las Vegas at booth #1160! ### How to run a Very Good app development team Source: https://verygood.ventures/blog/how-to-run-a-very-good-app-development-team/ Custom app development has become an area of focus for companies of all types. Even if your business has never considered developing an app before, it could be an ideal moment to start. Your own [custom application](https://verygood.ventures/blog/how-to-build-a-custom-app-the-very-good-way/) is a great way to connect with your customers and is therefore extremely valuable. This demand for app development, with every business turning into a software company of sorts, comes with a few added requirements. You may find yourself assembling a software development team for the first time in your organization's history. When pursuing your app idea, should you hire internal talent or outsource some functions? What kinds of roles and responsibilities should your new personnel take on? How can you set your product managers, designers and engineers up for success with the way you organize their workdays? Fortunately, finding the answers to those questions doesn't have to be overly challenging. With the right combination of technology and expert input, you can get started building and running the successful mobile app development team of your dreams. *Want to see how a well-trained team works on a specific design assignment? Check out how we* [*develop apps for wearable devices*](https://verygood.ventures/blog/building-wear-os-apps-with-flutter-a-very-good-guide/)*.* ## How Do You Pick a Framework for Your App Development Team? You should have a development framework in mind for your app *before* hiring engineers for your team, rather than staffing up and then making your choice. Why? Because your framework will determine what programming language your app will use, as well as the ideal organizational structure for your efforts. The person to make the choice should ideally be a knowledgeable director or seasoned engineer. If your company doesn't have someone on staff who fits that description, you can focus on bringing them in first, then build out a supporting team. If you choose to code a native app for each of your targeted platforms, you'll need to assemble multiple teams, each specializing in one device or operating system. Skipping a platform simply isn't an option today, especially when it comes to mobile operating systems — releasing just an Android app or an iOS app means leaving out a huge potential market. Barring cases when you're developing for a very specific group of devices, like company-issued smartphones, you'll need to target both of the major mobile operating systems, at the least. ### The Multi-Platform Approach to App Building (and Team Building) This requirement to target multiple platforms, along with the added value of releasing additional versions of the app for the web, MacOS, Windows, Linux or a wearable device operating system points to a new option. Multi-platform development, using a framework such as Flutter or React Native, simplifies the team-building process. There's no iOS developer or Android developer role in this unified model; everyone is on the same team. Read more about the different [multiplatform frameworks to build your app. ](https://verygood.ventures/blog/flutter-and-react-native-learn-about-the-leading-multi-platform-frameworks/) Working with just one team and one codebase allows you to unite personnel under one roof, breaking down the silos that keep them from communicating effectively. This unified structure can bring about a whole new mindset — rather than trying to get one version of an app to work, the experts are pouring their efforts into building a great product users love to interact with. Working with a single codebase means training all engineers in the use of one chosen programming language. Such a training engagement can prepare the team to work together, build powerful software and maintain the application over time. *Interested in educating your personnel for multi-platform app development?* [*Schedule training*](https://verygood.ventures/solution/flutter-training/)*.* ## What Are the Different Models of App Development Teams? Remote work has changed everything. That statement applies across industries and around the world, and app development is no exception. When it comes time to build a successful mobile app development team, you can choose between in-house, outsourced and hybrid models, getting the exact combination of skills you need for your project. Your options for creating a software development team include: - **Hiring and training in-house talent:** If you see app development as a core piece of your business moving forward, it may be worth hiring your own app designers and developers. Finding specialized training for these new hires helps them work together effectively and follow best practices. This model grants you the maximum level of control and oversight, though it does add permanent salaries to your books. See how we help companies deliver their apps by sharing best practices and [training teams in Flutter](https://verygood.ventures/success-stories/betterment/).  It's worth noting that successful app development is not a short-term project. Even if you manage to launch your new app relatively quickly, there will be a need for ongoing updates as your people address bugs and follow the feature roadmap. You can invest in third-party training to bring your internal app developer group in line with cutting-edge best practices. - **Supplementing internal talent with third-party experts:** Software developer skills shortages are a natural part of software development today. After all, with so many organizations seeing the value of custom app development, it can be challenging to find enough designers and engineers to fill out a team. Even if your team isn't shorthanded,  leveraging the expertise of third-party consultants, designers, and developers can play a pivotal role.  External contributors can bolster your team by fulfilling specific tasks - such as shifting to a new framework or preparing for launch — or by taking on a more flexible, open-ended role. One common strategy to maximize value is to work with third-party experts during the intensive building of a new application, then turn the long-term management of the software over to your in-house team. - **Outsourcing development:** Working with a full third-party mobile app development company is an option for companies of all sizes. Whether you're a small business that wants access to a full-scale mobile development team without making numerous hires or a large company in search of a ready-made engineering unit, you can turn to outsourcing. Working with experts who understand app design and engineering best practices is a critical consideration when forming a partnership with a third party. You should make sure you're working with top software developer groups to give your app the best possible chance of sustainable, scalable and future-proof success. Once you've decided the balance between in-house and external app building, it's time to set the parameters for your project and oversee the team members as they get to work. *Learn how the VGV team builds* [*enterprise apps with Flutter*](https://verygood.ventures/blog/vgv-approach-to-application-architecture/)*.*  ## How Do Responsibilities Break Down within an App Development Team? The size of your overall mobile app developer group will depend on factors such as your targeted platforms, intended audience and chosen development framework. The more people are involved, the more chances there are for misalignment and miscommunication — a unified team with a small group of subject-matter experts can outpace multiple teams with dozens of members, while also keeping app development costs low. You must manage the team composition carefully, however. Too high a concentration of experts can result in bottlenecks around processes such as code review. An application development team is based on the collaboration and communication between a few different roles. Each of these is important to the overall finished product, and it's worth giving them the tech tools and guidance they need to cooperate and share information. - **Product manager:** Lays out the vision for the application and keeps the rest of the team on task, taking responsibility for the direction of the project. - **Product designer:** Works on elements that make up the user experience, including the user interface, ensuring the app is visually appealing, satisfying to interact with and intuitive for its users. - **Development engineer:** Performs the actual coding work in the relevant programming language, putting together the app's unique functionality. May be broken down into specialties like frontend developer, backend developer and QA engineer. While these groups have distinct responsibilities, they'll ideally work toward a highly unified goal based on the initial app idea and plan for the mobile application. If there's a deficit at any of these roles in your business's local talent pool, it's a good idea to invest in training, third-party consulting or both. *Learn more about our day-to-day* [*mobile app development process*](https://verygood.ventures/solution/build-an-app/)*.* ## How Can You Keep Your App Development Team Productive Over Time? It's important to remember that a dedicated app development team's work doesn't end with the build phase. The scale and maintenance phases of operating your custom mobile application are critical times, and the continued effectiveness of your team will make or break the software's long-term prospects. The key decision to help your team succeed over the long term is often made back at the very beginning: When companies choose to embrace multi-platform application development, they provide an easier road for each mobile app developer. The process of studying bug reports and user experience feedback, then iterating and performing QA app testing, is dramatically easier when there's a single unified team focusing on one codebase. Freed from the need to coordinate across functional silos, engineers can keep up with patches while also working away at new features to follow the road map. Ongoing training and employee education are also essential for the continued success of mobile development teams. Even an experienced app developer or app designer should learn new tricks and discover the latest best practices in their fields, especially considering the speed at which software development is changing and evolving. Your efforts to keep your team strong over the months and years after launch can pay off in continuing engagement based on a positive user experience, maintaining a strong bond between your brand and its audience. ## Get Started Forming Your Ideal App Development Team Starting a new project from scratch isn't the only time you can benefit from staffing up a new dedicated app development team. Shifting complex app components to a new framework can also go well when handled by a fresh group of experts. Even ongoing maintenance for existing software can benefit from having extra expertise on call. Whether your ideal model is outsourcing, consulting or a fully in-house team building strategy, you can get started right away. If you're not certain which approach is the best fit for you, it may be time to reach out and discuss the matter with our expert team. ### Achieving a Very Good App Launch Source: https://verygood.ventures/blog/achieving-a-very-good-app-launch/ They say you never get a second chance to make a good first impression. That's true in life — and in app development. An application launch is the moment when your engineers' work starts to turn into value and return on investment, and making the most of this time is worth planning, focus and attention. By learning the best practices and proper procedures around an app launch, you can increase your chance of success in this make-or-break moment: This is your opportunity to present a new and exciting product to your user base for the first time, and the general impressions of the new app can set its trajectory going forward. Before delivering into the specific steps of launching your app, it's worth the reminder of just why your app launch is such a monumental time and worth planning for. ## Why is a Successful App Launch So Important? An application starts with an idea. Companies today use apps to deliver specific features and functionality to specific audiences. Perhaps the app is a way to access e-commerce features. Maybe it's an internal app used for employees to communicate with one another. Or, it could be the control panel for a new vehicle or a branded gaming app. Once you know what the app will do and who will use it, it's time to form a strategy for capturing an audience. The long-term success of a web or mobile application depends upon its user base. Securing a large audience of engaged users should be the objective that drives your thinking during the pre-launch and launch processes. Without a solid user base, it doesn't matter how powerful your app may be or how appealing its user interface is — when there's no one using a piece of software, it won't produce a return on investment. Locking in these users is simpler when your application launches with a full feature set from the beginning. A lack of functionality may limit an app's reach beyond initial app downloads. Launching with a wide feature set but without adequate testing and quality control for those features can have the same result. Audiences today are made up of people who use applications every day and have high standards regarding app user experience. You may not get a second chance to impress these users. ### The Power of Multi-Platform Development: Reaching a Wider Audience, Faster One point to consider when developing an app today is the fact that your audience will be split across a variety of preferred platforms. Even if you're developing a strictly mobile application, it's essential to target both iOS app and Android app users. If you also want to target Windows, MacOS and the web, the complexity grows. Using a [multi-platform framework](https://verygood.ventures/blog/flutter-and-react-native-learn-about-the-leading-multi-platform-frameworks/) such as Flutter is a way to remove the complications of targeting an audience that spans devices and channels. Flutter allows you to work with a single codebase, with one app developer team. Under such a model, there's less room for time-consuming disagreements or divergences between siloed developers. With all your people working on a single project, it's easier to release simultaneously across platforms, gaining the maximum possible exposure for the new application. The overall headcount necessary is also drastically reduced when focusing on a single codebase, keeping your budget under control. *Read our guide to learn more about* [*maximizing the value of your Flutter-based app*](https://verygood.ventures/whitepaper/business-value-of-flutter/) *before, during and after launch.* ## Step By Step: What Goes Into a Mobile App Launch? Once you've committed to a framework for your app launch and set out your intentions for the software's target audience and intended purpose, it's time to work methodically through the steps that will guide the software from concept to real, downloadable product. In the months and weeks leading up to a mobile app launch, this means: - **Designing and creating a functionality roadmap:** This is when a general app idea regarding purpose and capabilities crystallizes into a real, actionable plan. What features are essential at launch? What functionality will be added in the months ahead, and what are the targeted dates for each new development milestone? - **Testing, prototyping, surveying and iterating:** With the roadmap locked in, it's time to engage in the meat of development. Creating new builds of the app and iterating based on testing, research, surveys and feedback is an intensive process that is simpler when working with a multi-platform, single-team model. - **Setting a release date and launching:** As development proceeds, it's time to set a feasible launch date. Earlier is better, as it helps companies start drawing a return from their applications, but the product must be in a stable, functional state to avoid alienating early adopters. Directly following the release, the work continues for mobile app development teams. Their next steps include: - **Performing urgent maintenance and upkeep:** Squashing any critical bugs in the wake of a launch is an essential process. Even following the most rigorous pre-launch testing, there's room for issues to pop up. The quicker they're resolved, the easier it will be to retain users. - **Following the functionality roadmap:** Once the app is running in its initial state following launch, it's time to follow through on the promises made in the product roadmap. Of course, the quicker the team is about implementing fixes, the sooner it's possible to pivot to new feature creation. - **Responding to app user feedback:** While the feature roadmap created before launch may be detailed and meticulous, there should be flexibility. After all, once users start interacting with the application, their feedback and the results of app analytics can point the way to valuable new features. ### The Power of Multi-Platform Development: Shortening the Path to Launch The effort to release an application as quickly as possible is important from a balance sheet perspective — during development, you're investing resources, with no return on that investment yet. Multi-platform development allows your team to focus on a single codebase and accelerate the time to launch, adding speed without cutting corners or sacrificing quality. ## What Are the Best Practices of an App Launch? So, how can you maximize the chances of a smooth, effective launch for your latest software product? There are a few priorities to keep in mind amid the high-speed push to launch your app. These include: - **Maintaining a fully tested codebase:** Quality should never be a secondary priority, even when you're moving quickly during the launch window. From the very beginning, using code and widgets with automated test coverage as close to 100% as possible is a valuable approach. Learn more about our [approach to code coverage](https://verygood.ventures/blog/road-to-100-test-coverage/) and why we focus on 100% in this blog post.  - **Considering scalability:** Developing a new app can feel like all the work is leading up to the launch, but the real test for the software may come in the following years, as new features roll out according to the roadmap. Scalability should be a consideration from day one. - **Marketing your launch:** Getting an audience's attention is an essential step when it comes to locking in app downloads. This means your branding and mobile app marketing strategy are integral pieces of the overall application launch process. Your mobile app should have strong, consistent branding, and your business should commit to app promotion. - **Releasing smoothly into app stores:** Getting your new product listed in the Apple App Store and Google Play Store is more than just a formality. You should have a strategy to ensure your app is accepted by the platform owners, then implement app store optimization for the app store listing and monitor user feedback during and post launch. Working with a quick-start code generator is a way to bake best practices into your app launch workflow and get to the rewarding parts of app development quickly, knowing there is a strong framework underlying your efforts. ### The Power of Multi-Platform Development: Removing Silos and Reducing Extra Work Having only one team working on your app ensures there is less extraneous work or disconnect between the various versions. This allows more effort to go into observing best practices, all with a smaller headcount than would be necessary for native development. *See how our* [*Very Good Start code generator*](https://verygood.ventures/solution/very-good-start/) *can help you achieve a fast — and stable — app launch.* ## How Do You Scale Up After Launch? The scaling process involves obliging a few different priorities simultaneously. Post launch, your app developer team must: - Address bug reports and implement fixes. - Follow the functionality roadmap. - Implement user interface changes and updates based on feedback. Keeping up with your audience's needs and wants regarding your app's user experience is an important component of the scaling process. Retaining interest and keeping a strong user base over time isn't automatic, and this is where you can lock in the gains made during a successful launch. ### The Power of Multi-Platform Development: Simplifying the Update Process A single codebase is extremely impactful in terms of updating your app effectively over time. When teams have to update multiple natively developed versions of an app, there is a risk of introducing unexpected consequences, and the update process can tie up resources that would otherwise be devoted to following the roadmap and implementing useful new features. ## Ready To Launch Your App? When developing your next app, you can give yourself a head start on a successful launch by working in Flutter and following best practices. The effort that goes into your launch and preparation can pay off for years to come as the app becomes an integral part of your digital brand identity. You can even team up with experts who can help you build effective practices into your launch. ### The Shortest Path to AI Everywhere, is Flutter Source: https://verygood.ventures/blog/the-shortest-path-to-ai-everywhere-is-flutter/ The tech world is fascinating. Since the 1950s, every decade has experienced a breakthrough that has expanded the limits of what humans can do, how we communicate, analyze data, perform tasks, and experience art and joy.  In my lifetime, I have experienced 2 major transformations that shaped how we live our lives: (1) Access to the web, it becoming mainstream and (2) equipping everyone with a smart device. These two milestones have democratized access to knowledge for everybody, connecting the world digitally and creating new lines of business and potential that were not feasible before. Today, in 2024, AI is part of our daily routines and has taken over the *tech conversation* in the last 2 years, from ChatGPT to the latest Google innovations: [Vertex AI](https://cloud.google.com/vertex-ai?hl=en) as a platform where anyone can be an AI developer, and [Gemini](https://blog.google/technology/ai/google-gemini-ai/), Google’s most powerful multi-modal AI model.  For the longest time, AI was only accessible to researchers, data scientists, and subject matter experts. Google’s AI solutions change the story's course, allowing every tech-savvy person to access a vast catalog of AI-focused tools ready to add value with very little knowledge.  ## Building High-Quality AI Experiences at Scale with Flutter We've experienced this firsthand at Very Good Ventures, where a small team of Flutter engineers developed in record time [Ask Dash,](https://verygood.ventures/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/) a generative AI application that uses [Vertex AI Search and Conversation](https://cloud.google.com/vertex-ai-search-and-conversation?hl=en) trained in Flutter’s documentation to provide natural and meaningful responses to any question Flutter related. It's very easy to imagine how this technology applied to other experiences, such as customer service, real-time support, custom search engines, or digital assistants, can add tremendous value to an organization, increasing revenue, loyalty, and customer satisfaction.  In the race to bring new AI experiences, [Flutter](https://flutter.dev/) is a key technology that enables access to any operating system and/or screen. The days when multiplatform development meant sub-par experiences are gone. Flutter allows organizations to [*ship faster and safer*](https://verygood.ventures/whitepaper/business-value-of-flutter/)*:* it puts quality at the center of development, creating beautifully crafted experiences in record time at scale. Generative AI projects like [I/O Flip](https://verygood.ventures/blog/how-its-made-i-o-flip/) and [AskDash](https://verygood.ventures/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/), built by our team of unicorns in partnership with Google, prove that Flutter is the best answer to bring your AI experiences to any platform quickly and with high quality! Even ambitious open-source projects like [AutoGPT](https://github.com/Significant-Gravitas/AutoGPT) have picked Flutter as the de-facto framework to bring AI experiences to everyone on any platform. In a world where business value is determined by data and what we do with it, and where data will be accessible to everyone, what will make the difference is our ability to build rich and engaging experiences in record time and the best framework to deliver those results today is Flutter. ## Introducing AI in Your Organization: New Opportunities & Challenges for Flutter  While all the improvements in Artificial Intelligence can create new opportunities for your organization, they also represent a risk. This means bringing those experiences to all the platforms where your product is deployed—e.g., web, iOS, Android—otherwise, your product risks falling behind. Customers today demand rich and smooth experiences, and they always find alternatives to get what they want when they want it. Flutter allows organizations to cut the time and cost of bringing AI experiences to production: whether it is the opportunity to create a new AI-powered app like [Valkyrie.ai](https://verygood.ventures/success-stories/valkyrie-ai-race-performance/), where AI is used to determine the performance of vehicles racing, or using [add-to-app, like Betterment did](https://verygood.ventures/blog/betterment-flutter-revitalized-our-codebase/), to enhance the features of your application without the need for a full rewrite, one thing is for sure certain: Flutter is the fastest and most successful way to bring new AI experiences everywhere. ### Behind the Unicorn Magic: Get to know the VGV Team Source: https://verygood.ventures/blog/behind-the-unicorn-magic-get-to-know-the-vgv-team-3/ ## Jorge's Background J: I hold three engineering degrees, a bachelor's in Telecommunications Engineering from Spain, a Master's from the Technical University of Madrid, and another Master's in Information Technology and Management from IIT held in Chicago. My career in the US started with three years at SPR, a consulting company in Chicago, where I worked with companies like Orbitz (now part of Expedia), Groupon, and Enova, delving into mobile, Android, iOS, Ruby on Rails, etc.  BMW then entered the picture, offering an opportunity to lead the global Android team. Transitioning to Flutter, I initiated a research and production team, marking my involvement with Flutter since 2018. Upon joining VGV, it has been a continuous exploration of Flutter through collaborations with Toyota, Visa, Accenture, AutoNation, and other major clients. ## Role at VGV **Can you describe what you currently do and how you found out about VGV?** J: I'm the Head of Engineering at Very Good Ventures, with a primary focus on various platform technologies, particularly Flutter these days. Celebrating my fourth anniversary with VGV this month, I've spent three and a half years as the Head of Engineering. Recently, my focus has extended to exploring AI technology and Cloud strategy, among other areas. Before joining VGV, I was a Tech Lead for mobile at [BMW](https://flutter.dev/showcase/bmw). My responsibilities included researching strategies to provide consistent value for iOS and Android across all markets. This led us to choose Flutter, and through this, I crossed paths with VGV, especially after speaking at the Flutter Droidcon conference in New York back in 2019.  The initial connection with the team and their work, particularly with projects like Hamilton, drew me in. Eventually, when exploring professional options beyond BMW, VGV was top of mind due to the instant connection I felt with the team, the technology they used, and the impressive client portfolio they had. **Can you describe a project you're particularly proud of?** J: I take immense pride in our collaboration with Google, where we've undertaken projects like I/O Flip, I/O Pinball, Photo Booth, and Super Dash, among others. These projects exemplify our creativity and ability to deliver in record time. Working with Google is exciting, as they consistently challenge us to innovate. Another big partnership I am very proud of is with [Toyota](https://flutter.dev/showcase/toyota), where we are integrating Flutter into the next generation of Toyota and Lexus vehicles. **Are there any emerging technologies or trends you find particularly exciting ​​what trends sort of freak you out?** J: I find technology itself not concerning, but rather how we utilize it. Artificial intelligence, in particular, presents a vast range of opportunities for individuals and companies. Exploring the potential of AI in enhancing customer connections, brand engagement, and developer capabilities is where the excitement lies. Flutter, from my point of view, represents an opportunity to bring AI to screens worldwide, and the evolving landscape is intriguing. **What challenges do you find in your day-to-day role, and how do you address them?** J: The primary challenge lies in the evolving nature of my role. While formal training equips professionals with technical skills, it doesn't prepare them to be business leaders. My role has transformed every six months, from a Tech Expert to a Business Director, without sacrificing core skills. Balancing organizational results, talent optimization, and client experience is crucial. The key is sharing knowledge, learning continuously, and trusting the team to excel in various areas. **How crucial is teamwork and collaboration in your projects?** J: Teamwork is my top priority. In a consultancy setting, our product is the collective talent and experience of the team. Starting with a team of five at VGV, we've grown to 60-70 individuals -and that’s only counting in my team- handling multiple projects. Teamwork is essential for scaling a consultancy company, and it remains a driving factor in our success. The challenge is finding ways to optimize collaboration as the team continues to evolve. ## Looking Forward **What aspirations do you have for the impact of your work at VGV in the future?** J: My focus is not solely on titles or roles, but on facing increasingly complex challenges. As an immigrant, the appeal of the work and challenges has been more significant than specific titles. The excitement lies in continually tackling sophisticated challenges and providing value that partners cannot achieve alone. I encourage my team to aspire to take my role -and I mean it with a smile on my face! That’s a way to foster a culture of continuous improvement and growth. That’s what will set a continuous hunger of getting better each day.  VGV is more than just a workplace for me; it's a collective effort to build something meaningful. Since the early days with a small team, the culture of collaboration has been a testament to VGV's values. The goal is to make VGV an everlasting software agency, recognized globally for its quality and confidence. This journey involves daily problem-solving, idea generation, and personal growth. The culture of continuous improvement allows us to achieve higher milestones collectively, making me proud to be part of VGV. ## Who is Jorge beyond VGV? - Born and raised in Madrid, Spain - Hobbies: Jorge loves to play all kinds of musical instruments, like electric and acoustic guitars, bass, mandolin, ukulele, harmonica…and he also builds them! - He loves watching soccer matches and thinking about the different strategies around them. Go Real Madrid!  - You can also find a chill Jorge when he cooks: He claims to make the best Spanish food, especially paella, in the Midwest! ### Benefits of Flutter for Cross-Platform App Development Source: https://verygood.ventures/blog/benefits-of-flutter-for-cross-platform-app-development/ No matter what your plan is for your new custom application, one thing is clear — it'll have to work well on multiple platforms. After all, you can't afford to leave behind the huge percentage of potential users with either iOS or Android devices. The question now becomes whether you should opt for native development or cross-platform development. Running native development teams for each targeted platform may seem at first to be the most straightforward way to handle the issue, but in recent years, the alternatives have become more prominent. The appeal of native development via multiple teams of engineers has faded as frameworks like [Flutter](https://verygood.ventures/resources/flutter-development-agency-faq/) have enabled the development of powerful apps with a single code base. So, in a world where multi-platform apps are as robust and capable as native mobile applications, is there a reason to maintain the siloed work structures and high headcounts of native app development? For many companies, the answer is no. There's another wrinkle to consider, as well: App development that encompasses platforms beyond mobile, such as MacOS, Windows, Linux and the web, is an increasing presence in companies' custom app plans. Developing for two platforms with a single codebase is efficient. Developing for *five* platforms, so much more so. Considering the state of the custom app development space points to a clear conclusion: not only is cross-platform mobile application development a good idea, but it has evolved into the even more meaningful concept of multi-platform development. To cope with this new landscape, it's time to find a reliable multi-platform development framework. ## How Cross-Platform Became Multi-Platform: App Development Today There are some circumstances where an app could be made available on only one platform — for instance, an internal custom app created for company-issued smartphones or tablets — these are vanishingly rare. In all other cases, organizations need to create apps for multiple platforms that deliver a uniformly strong user experience. In addition to targeting the most common mobile device and computer operating systems and the web, there are also cases where it's worth creating a version of an app for a more esoteric platform, such as an internet of things (IoT) device. Each new platform a brand targets would significantly increase the complexity of native application development, calling for a whole new group of engineers to craft the user interface. Reaching the widest possible audience should be a major priority for app development teams. This leads naturally into multi-platform development, and companies have been grappling with various strategies to help them meet that need. ### Multi-platform vs. Multiple Teams: Evolving App Development The rise of cross-platform development and its evolution into multi-platform development to encompass even more platforms is a reaction to all the challenges and complications of native apps developed for multiple operating systems. The most obvious reason to opt for multi-platform development is **cost**. It's impossible to ignore the fact that setting up multiple mobile application development teams working on separate code bases requires a significantly larger headcount than working on a single codebase in Flutter. Add in the ongoing upkeep costs of maintaining all versions of the app over time, and the expenses increase further. **Siloing and communication issues** can pose additional challenges. Achieving perfect alignment between the various teams working across different platforms is difficult, and this can cause delays to bug fixes or new feature roll-outs. There's also the issue of **scalability** in general. Maintaining one codebase over time is a simpler feat than working on multiple versions. This is critical for apps that are intended to grow and evolve organically over time, providing positive user experiences for years to come. To overcome the challenges and seize the advantages, brands are shifting to multi-platform software development. Their choice of app development framework can set the tone for how these shifts go. Flutter is a frequent selection — and the preferred option of our Very Good team. ## Benefits of Flutter as Your Cross-Platform App Development Framework Why work with Flutter instead of React Native or another competing platform? This choice is one that companies have to make on a case-by-case basis. For instance, there may be a strong preference among available software engineers for one programming language or another. With that said, our team has been involved with Flutter development since the framework's debut in 2018. In fact, we built the official [Hamilton mobile app](https://verygood.ventures/case-studies/hamilton/) — the first app built with Flutter outside of Google. We are very familiar with the ins, outs and impactful features of the Google-powered offering. *Get more info on our* [*Very Good approach to developing apps*](https://verygood.ventures/solution/build-an-app/)*.* ### Power Under the Hood: Powered by Dart Flutter uses the modern, open-source programming language Dart and is designed to be pleasant and intuitive to use, with a strong open-source community component. Its unique hook as a platform comes from how the codebase works across devices: Software engineers write their code in Dart, using native code for each targeted platform. It's truly possible to write code once, and then have it run on each platform, creating a beautiful and consistent user interface. ### Support for the Web and Desktop Operating Systems While app development today primarily means "mobile app development," there is value in reaching users beyond what's possible with iOS app and Android app development. This is an area where Flutter shines, as over the years its development team has added support for web app development alongside MacOS, Windows and Linux. This allows the single codebase to go further and reach a potentially huge new audience. ### A Helpful Community and Assistance from Google Some development cases are esoteric and open up the need for new packages, plugins or capabilities. Flutter's relative newness as a framework is countered by its vast app developer community creating new open-source UI components. The team at Google is also responsive to user feedback and constantly pushing Flutter in new directions.  ### All The Advantages of Multi-Platform Development Beyond the specifics of Flutter, it's important to remember that simply by choosing a multi-platform app design and software development strategy, organizations are saving themselves time, money and effort.  Choosing Flutter isn't just an option for companies starting the development of a new app from scratch. It's also possible to shift existing software to Flutter one component at a time. This consolidation helps companies lock in a scalable, low-overhead future for their most important applications. *Learn more about what happens when an* [*app developer shifts to Flutter*](https://verygood.ventures/solution/flutter-transformation/)*.* ## Best Practices for Cross-Platform App Development Today The technical framework is one component of successful app development. The other is the practices that go into the actual design and development of the software, from pre-release conceptualization through ongoing post-release support. Teams that are careful every step of the way, taking full advantage of the ease granted by working in Flutter, are setting themselves up for long-term success with the digital products they build. There are best practices to remember and stick with throughout the development process, phase by phase: ### The Build Phase Building an app begins with a product roadmap and a quality assurance framework. Having these in place before development really gets underway is a good way to ensure the resulting application stays on track leading up to launch and for years afterwards. An organized and well-regimented single-codebase development process can get an app to launch in a relatively short time without compromising the feature set — an important consideration because the app can't start to generate a return on investment until it launches. ### The Scale Phase Launching an app following multi-platform development, with a shorter timeline and more features at launch than would be possible through native application building, gives companies a head start on the scale phase. From here, it's time to stick to the roadmap and roll out additional features. With one unified team and plenty of available mobile development resources, it's relatively easy to follow a best-practices approach to early scaling up. Capturing and retaining an audience is easier when the app has launched feature-rich and accessible on different platforms like the web, and there are fewer support needs. This leaves team members free to keep developing. ### The Maintenance Phase Maintaining an app on an ongoing basis comes with its own challenges — difficulty retaining engineering talent, the need to keep up with bug fixes while also rolling out new features and more. Using Flutter helps companies take a suitable approach to multiple issues. For instance, having a single mobile development team often prevents silos and misalignment from slowing down the bug-fixing process. Dealing with simple tech stacks and running with a smaller headcount are factors that limit the impact of skills shortages. *Dive more into* [*how Flutter can help you scale your products*](https://verygood.ventures/whitepaper/business-value-of-flutter/?utm_source=twitter&utm_medium=social&utm_campaign=tbv_flutter)*.* ## Reaching Audiences on Every Platform The best time to start developing with Flutter is right now. An organization that plans to launch a flagship custom mobile application in the months ahead can set that project up for success, while a business that is already maintaining an app in production can shift features to Flutter for easy upkeep. With a multi-platform approach, it's easier than ever to future-proof an application and keep its user experience stronger for longer, even as audience expectations steadily rise. ### Top Android Expert Joins VGV: Toffer Jakobsen Source: https://verygood.ventures/blog/vgv-expands-technology-leadership-with-top-android-expert-toffer-jakobsen/ At Very Good Ventures, we’ve built our success around being an early adopter of Flutter. But, perhaps even more important than our Flutter expertise has been how we deliver software and the impact we have on development teams. Our commitment to high development standards and consistent quality-centric practices has been a hallmark of our team. Our [offering](https://verygood.ventures/our-services/) is based on deep and differentiated expertise and experience. When we move into new areas at VGV, we do so with intentionality, and it is essential that we quickly ramp up world-class digital outcomes. With that in mind, we are very excited to announce that [Christoffer “Toffer” Jakobsen](https://www.linkedin.com/in/christoffer-jakobsen/) has joined the VGV team as a Principal Engineer focused on Native Android development. Toffer joins VGV after more than 13 years at the renowned agency, ustwo, where through working with some of the world’s top brands he built different Android experiences, specifically with wearables and Wear OS, making him a formidable Android engineer. Toffer joins VGV at a time when we are expanding our service offering, built on our core [Flutter expertise](https://verygood.ventures/solution/build-an-app/), to bring the “VGV Way” to as many aspects of our clients’ technology ecosystem as we can. We’ve been adding Native App Development to our toolbox alongside web, back-end, QA, and of course multiplatform development with Flutter. Now with Toffer joining VGV, our Android capability will be drastically accelerated as we will build around a leading expert in the field. ## Beyond Flutter: Why VGV is venturing into Native app development As we expand our offerings, including Native app development, we believe there are two primary reasonable ways to develop apps in 2024: (1) using pure Native technologies, or (2) multiplatform technologies. It’s no secret that we love Flutter. There are many advantages to multiplatform development, and in those cases we firmly believe that Flutter is the right choice — no other framework or solution provides the same benefits or versatility. Simply put, Flutter is our only recommendation when multiplatform is the choice. We also understand that in some cases, Native can be the right choice — especially if a team can effectively manage the additional cost and complexity. Additionally, while Flutter has been growing rapidly, the vast majority of apps have been built with Native technologies. As we have built trust among our clients with our Flutter work, we have been routinely asked if we can engage with other products, teams, and technologies — including native. Engineering and product development transcends any one product or platform, and indeed our multiplatform Flutter expertise means we have touched far more than mobile apps. The real secret sauce is how we use the tool, not the tool itself. As we move into new areas and grow our team with world-class talent, VGV is bringing our successful and proven engineering and product development approaches to more arenas. It all sums to more impact and better results for our team and our clients. ### Welcome to the team, Toffer! ### Learn About the Leading Multi-Platform Frameworks Source: https://verygood.ventures/blog/flutter-and-react-native-learn-about-the-leading-multi-platform-frameworks/ When you need to capture the attention of a wide-ranging digital audience today, what do you do? Increasingly, the answer is to offer an application. But how can you make sure your app reaches the widest possible audience without over-complicating the development process? The answer lies in using multi-platform frameworks for development, allowing you to target mobile application users across iOS and Android while simultaneously offering versions for MacOS, Windows and the web, all from a single codebase. While multi-platform framework use is becoming increasingly popular, there are some notable differences between developer approaches. Namely, organizations each have their own preferred technology tools. The leading offerings are Flutter, from Google, and React Native, from Meta. Which one of these is right for your particular needs? That answer will come down to a few specific factors, and the resulting decision will set the course of your multi-platform development going forward. ## Why Choose a Multi-Platform Framework? The ultimate objective of using Flutter and React Native is the same — to create a single development workflow dealing with a single codebase that can reach all relevant platforms. That means before choosing between the two frameworks, it's worth considering why your business should use either one in the first place. The reason to opt for multi-platform development comes from evolving preferences around how audiences interact with digital content and how companies create it. Businesses want to present the highest-possible-quality digital experiences for their user bases, but those users view the content on a wide range of devices and operating systems — iOS, Android, MacOS, Windows and the web are among the most important channels. They expect the same high-quality experience across each touchpoint. Skipping major platforms isn't an option, because it pays to reach the largest possible audience. Should your company split its development team into multiple sub-groups of native developers to develop apps for each platform? That represents a traditional approach to development, but it's prone to problems around cost, speed and general communication. Shifting development to a multi-platform framework such as Flutter or React Native alleviates the issues and delivers: - **An end to functional silos:** With all developers united around a single codebase, you can focus all your personnel on the same set of issues without maintaining a massive headcount or understaffing any specific platform's team. This approach allows engineers to work on specific areas of the user interface from end to end rather than being caught up in polishing a specific single-platform native app. - **A quicker approach to updates:** Whether you're fixing bugs or adding new features, development, coding and quality assurance processes are faster when there's a single codebase involved. - **Lower upkeep requirements:** Maintaining multiple codebases can become unwieldy quickly, with a significant investment going into squashing bugs across platforms — this may hamper the company's ability to effectively roll out new functionality. - **Improved communication:** If development is split between individual teams for each platform, it's easy for differences to spring up around cadences and development styles, and for this disconnect to slow down the overall process of maintaining an app. Unifying around a single framework, codebase, and UI development team helps your organization stick to its roadmap for your application, responding to user feedback, correcting bugs and rolling out features on time. So, let's learn a bit more about each framework. ## All About Flutter You might have heard about [Flutter](https://flutter.dev/) before, essentially because it is one of the most popular frameworks used by development teams around the world. For those who need an introduction to it, it's a user interface platform created and maintained by Google using the [Dart programming language](https://dart.dev/). It's meant to be approachable, open source friendly and powerful enough to build beautiful, natively compiled, multi-platform apps from a single codebase.  The [philosophy of Flutter](https://docs.flutter.dev/resources/faq) centers on the principles of control, performance and fidelity. This means a Flutter developer should be able to access each layer of a system and create apps that deliver highly usable and appealing experiences for the end user. Flutter's creators highlight the challenge of running separate software development teams for multiple platforms due to the associated high costs. However, recognizing the importance of reaching a broad audience, they emphasize the need for a solution. Flutter was designed to bridge this gap and enable development for multiple platforms more efficiently. ### The History of Flutter Flutter had its initial release in December 2018. Version 2 debuted in March 2021, with version 3 following in May 2023. The framework has received new capabilities, features and functionality along the way. These updates have largely involved its ability to work more effectively with different platforms. Developers can now create native-quality apps for MacOS and Windows, for example. You can read more about [Flutter’s history](https://verygood.ventures/blog/flutters-history-so-far-all-the-biggest-milestones/) so far in this blog post.  ### Flutter Under the Hood The basic theory behind Flutter app development involves using the up-to-date Dart language. There is a thin layer of C/C++ code over that base, allowing the apps to run on various platforms. The primary rendering of Flutter apps happens in the platform's own engine, rather than tapping the resources of individual operating systems or web browsers or using native components. A new engine is on the way, which promises even greater power. ### The Flutter Ecosystem The Flutter app development community is made up of developers creating and customizing widgets. Flutter is [open source](https://vgv.dev/), and there are many ways for a Flutter developer to get involved in the ecosystem by making feature requests or bug reports, or by contributing code to the overall project. Google supports [pub.dev](https://pub.dev/), an official repository where engineers can share the most useful packages for Flutter and Dart. The Flutter Ecosystem highlights especially useful and high-quality offerings. *Read our whitepaper for more insights into the* [*value of multi-platform development for businesses. *](https://verygood.ventures/whitepaper/business-value-of-flutter/) ## All About React Native [React Native](https://reactnative.dev/) is a platform for building user interfaces created by Meta, the company formerly known as Facebook. It's based on the React JavaScript library and it’s designed to empower mobile app development by delivering the potential of native development and native components, but with a shared codebase. React uses components from the React web framework, brought into a multi-platform context. React Native app development is intended to allow developers to work more quickly than they would in true native development with teams split between operating systems, yet still access native features of each device. React Native is meant to be quick and easy, and it's possible to port just some features to the framework. Native application component development, creating UI elements that work on multiple platforms through wrapping, makes up the core of the React Native app development experience. The idea of one codebase with some React wrapped native UI components is primarily meant for mobile development, though the community has created options for PC operating systems and the web. ### The History of React Native The first release of React Native came in 2015, followed by frequent updates from a team at Meta. It originated as an outgrowth of the React web development framework, allowing developers who had been working with the tool set for web projects to expand into the mobile space via multi-platform methodology.  It became the framework of choice for Meta itself as well as companies such as [Microsoft and Amazon](https://reactnative.dev/showcase) for the iOS and Android apps, with the latter converting in 2016. ### React Native Under the Hood React Native development uses JavaScript as its basic language and is based on calling devices' native APIs, giving basically the same functionality that would be available with a native app. Indeed, the React components are designed to wrap native code and get them to function on various operating systems. The connection between native code and JavaScript is accomplished via a bridge module, which can come with performance hits. ### The React Native Ecosystem React Native is an open source framework, and it welcomes contributions from its community. It was the involvement of the React Native developer community that helped the framework go beyond its iOS and Android roots and create separate, ported GitHub repositories for Windows, MacOS and web applications. ## Flutter and React Native: Where do they diverge? Both Flutter and React Native exist to solve the same basic issue — it's too expensive and time-consuming to reach modern standards of high-quality app development without a single, unified codebase. They're also both based on open source principles and welcome community involvement. The technical details of their approaches, however, are different. ### Underlying language The major split between the older React Native (2015) and younger Flutter (2018) involves the contrast between JavaScript and Dart. The former is a venerable language, which means there are plenty of developers who know and understand it well, but it also comes with [some legacy limitations](https://reintech.io/blog/advantages-and-disadvantages-of-using-javascript) such as speed and error handling issues. Dart was specifically chosen due to its modern, streamlined nature. ### Interaction with native user interfaces React Native, true to its name, is known for using React primitives to render native platform UI. Flutter, on the other hand, is based on creating the equivalent of native performance while rendering within the platform's own engine. These two styles are both designed to accomplish the same goal, namely, creating positive user experiences with a single codebase. ### Approach to platforms other than mobile While Flutter is designed to be truly multi-platform and go beyond iOS and Apple to encompass the web, MacOS and Windows, React Native is a web framework at its core which first moved to mobile. With that said, both of the frameworks welcome contributions from their community, and this has enabled React Native to receive support for those platforms through ports. ## Ready to Start Multi-Platform Development? So, which path should you take with multi-platform software development on a single codebase? The answer can come down to a few key factors, such as the languages your developer talent pool is most familiar with. Traditionally, this favors JavaScript, as frontend developers often work in Java and default to the language for backend work. With that said, in recent years, developers who have learned Dart for frontend work are finding themselves increasingly capable of [committing to full-stack Dart development](https://www.infoq.com/presentations/dart-back-end/), aided by new tools. You can dive into [Dart Frog](https://dart-frog.dev/), a backend framework for Dart developed by the VGV team that can be used by any developer wanting to build a scalable backend.  You can also select your framework based on alignment with your views around the best way forward from a mobile development perspective, or gather inspiration when engaging with the open source community.  Our team of experts can help you clarify your next steps and get started on a new approach to application UI development, whether you're most excited to shift an existing app or start a project from scratch. ### How to build a custom app the Very Good way Source: https://verygood.ventures/blog/how-to-build-a-custom-app-the-very-good-way/ If you are trying to build a channel to create a connection with your customers, or want to show off your very good product for e-commerce, you should start thinking about developing a custom mobile app, web app, desktop app, or all of them. You might be asking: Why is software development the right way to reach your audience? In short, custom apps bring your brand a convenient digital space to connect with people. Building these, you can also alternatively give your own employees a communication tool. In either case, external or internal, your app is a direct, branded channel that lets you set the parameters of the user experience. But be cautious, not every app project will drive ideal results. You need to approach custom mobile app development with focus and best practices to make sure your software is an actual useful tool for your audience, rather than something they'll simply ignore. And we know that’s not the desired outcome after putting together substantial efforts and a budget when investing in it.  When it's handled in a serious, focused way, custom app development puts your brand in front of a massive user base, allowing you to connect with people across a wide variety of devices, screens and touchpoints, in any channel that makes sense for your brand. ## What's the state of custom app development? Custom app development has become part of the fabric of business. As the tools to develop high-quality custom applications have become more widely available, software has expanded outside the technology field to reach companies of all kinds. These apps can become the face of the brand, which means these internal software development efforts have to reach a high level of polish, with a heavy focus on user interface design and the overall user experience. A great custom app development effort will be able to achieve: - **Adoption:** Marketing an app launch and ensuring the software has a solid feature set from day one are both important priorities tied to establishing a strong initial user base. - **Usage:** Good UI leads to good UX. Features should be optimized to take advantage of a given device and create seamless, convenient experiences. - **Engagement:** How do you keep users engaged with your app for the long term? By both correcting bugs and rolling out new capabilities according to a roadmap. One of the most potentially significant evolutions is the ever-increasing sophistication and power of multi-platform frameworks such as [Flutter](https://verygood.ventures/blog/flutters-history-so-far-all-the-biggest-milestones/), which you probably have heard about. These product design and development tools have taken the potentially sprawling and siloed process of app development and focused on it.  To reach the widest possible audience, your custom app development efforts should focus on more than one platform. Even if you're solely interested in custom mobile application development, you'll find your user base fragmented between iOS and Android. Therefore, you have to choose whether to develop natively for each platform or embrace a multi-platform model. The multi-platform approach is powerful because it allows you to unite your team around a single codebase. A unified codebase means you can deliver full functionality with a lower headcount and proceed more efficiently through both pre-release development and the execution of the app functionality roadmap. The rise of Flutter and similar frameworks have enabled app developer teams to create powerful, responsive and attractive application experiences without fragmenting teams or duplicating code. Whether you're [shifting components of an existing app into Flutter](https://verygood.ventures/solution/flutter-transformation/) or experimenting with custom app development for the first time, embracing a unified team and monorepo architecture allows you to increase collaboration and code-sharing.  *Learn more about the way our experts* [*develop apps*](https://verygood.ventures/solution/build-an-app/)*.* ## How do you get started with app development? The commitment to a development model — single-team or multiple — should be an early consideration in your app development journey. As you prepare to begin active development, you need to consider the purpose of the app and its target audience. What kinds of platforms do you envision releasing your app on? The more targeted platforms you add, the more time and effort you can save by collapsing the siloed structure and unifying the development team and codebase. In addition to iOS and Android, other potential platforms include MacOS, Windows, web, and embedded operating systems on custom hardware. Developing for multiple platforms is a compelling reason to use cross-platform tools. Current versions of Flutter enable Mac, PC and web development, allowing you to make the most of these platforms' resources and create a positive user experience. Before coding gets underway, mobile apps require conceptualization, along with the creation of a product roadmap. The whole journey, from app idea to minimum viable product status, launch and iteration over time should be mapped in advance. Moving quickly during early pre-launch steps is important. The sooner the app launches, the faster it can start producing a return on your development investment. An app that hasn't launched yet is just a cost center, while one that's available in the market can generate value. Prioritizing time-to-market over a high-quality mobile app with a good user experience can have devastating consequences. Even at launch, an app must be convenient and pleasant to use. A low-quality experience can drive users away. Operating a single development team that works in a single codebase is a way to shorten the development timeline and get to that ROI, without compromising quality. *Discover why our teams use* [*Flutter for product design and development*](https://verygood.ventures/whitepaper/business-value-of-flutter/?utm_source=twitter&utm_medium=social&utm_campaign=tbv_flutter)*, and why more companies are doing the same.* ## How do you manage the development of a custom mobile app? As custom mobile app development progresses, the team responsible should stick to best practices that allow them to combine quality with speed. A unified team working with a multi-platform framework and a single codebase can be intentional about their approach to development excellence. App development requires a strong commitment to developing a number of processes, including: - **Developing a reliable backend:** A technologically sound codebase built on simple principles enables your app to differentiate itself from the pack by showcasing a reliable and pleasant user experience - **Designing a powerful UI for better UX:** A responsive, effective UI that’s optimized for the platforms it appears on can keep users engaged, helping the app stand out from competitors' offerings. - **Incorporating hardware capabilities:** While native app development for multiple platforms was traditionally the only way to squeeze more performance out of device hardware, Flutter delivers the same performance by compiling directly to machine code. - **Planning and executing on a roadmap:** Having a list of planned features and rolling them out on schedule is a key piece of retaining an engaged audience. - **Marketing the app and orchestrating the launch:** Launching an app requires handling numerous tiny details, including listing in the Google Play store and Apple App Store, alongside mobile app marketing. - **Maximizing QA, testing, reporting and analytics:** Continuously making impactful updates, revisions and additions to fix bugs and optimize UX over time is just as critical as following the roadmap, and is considerably easier with a unified codebase. Whether or not you’re developing your first custom software offering, having a strong mobile app development process in place can represent the difference between a successful and disappointing launch. *Read our whitepaper for an in-depth look into the many stages of* [*building an app*](https://verygood.ventures/solution/build-an-app/)*.* ## How can you lock in long-term success for your app? Long-term support for an app represents two distinct kinds of work that can come into conflict with each other, especially because time and resources are always limited. Your team must keep up with bug fixes and quality control while also rolling out new features, either from the roadmap or user' requests. A single team approach with a unified codebase can be far more effective than running multiple native app development teams. The process of fixing bugs, when split between different codebases and groups with their own practices and priorities, can become unwieldy, slow and expensive. When simply keeping up with fixes and updates is too demanding, new feature development can easily fall by the wayside. Working with a single codebase becomes a way to enable a promising future for the app. Putting out fires takes less time, which enables growth and scalability. ## Ready to start on your next custom mobile app? If it's time to begin your organization's next mobile app — or to start shifting existing components of custom applications to Flutter for easier updates and scaling going forward — you have a few immediate priorities. Your strategy should reflect market conditions and audience interests, alongside your specific goals for the project. You don't have to go it alone in the effort to build a custom mobile application. There are third-party experts available to assist your team in a variety of capacities, from consulting and advisory roles to hands-on custom software development work. Connecting with experienced professionals can help you infuse your app development process with best practices. ### Solving Super Dash's Rendering Challenges: Eliminating 'Ghost Lines' Source: https://verygood.ventures/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/ During the development of Super Dash, we encountered a significant challenge known as “ghost lines”- rendering artifacts that shouldn’t be rendered, causing unwanted noise in the game. In this blog we’ll examine the origins of the issue, and explore a solution for smoother gaming experiences.  ## Identifying the Problem To better illustrate the problem, we created a  small map where the ghost lines are easily noticeable: ![Tile map with visible ghost line rendering artifacts between tiles](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-0.png) *Can you spot the ghost lines?* You probably were able to spot the rendering issues, but for a closer look, wee have zoomed into a couple of occurrences: ![Zoomed-in view of ghost line artifacts between adjacent tiles](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-1.png) Before we dive into how to solve this, let's understand how the map is built and why the issue happens. ## Understanding the map construction Both our example map, and the one in Super Dash are [tile based](https://en.wikipedia.org/wiki/Tile-based_video_game). This game development concept relies on the idea that your game component, or in this case, the map, is composed of tiles that are laid out in a grid. Those tiles come from a Tileset, which is nothing more than a palette of tiles that the developer will use to compose the final component. For our example, this is the tileset: ![Tileset image containing all tile sprites used to compose the game map](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-2.png) Note how everything seen  in the game screenshot comes from this single image, and the patterns that form the hills are simple repeating tiles taken from the tileset. There are many ways and tools to create tile based maps, one tool commonly used in the gamedev community is [Tiled](https://www.mapeditor.org/). Tiled allows you compose maps using the concept presented, and the format of the maps that it generates is supported by practically all game engines around, including Flame. ### This is how Tiled works in practice: ![Tiled map editor composing a map by placing tiles from the tileset](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-3.gif) ## The Ghosts In computer science,the precision of floating-point numbers is a long-discussed topic. We will not go in depth on this for the sake of keeping the focus of this article, just know that computers have limitations in representing real numbers precisely. (If you want to learn more about that subject check out this Wikipedia's article on [how computers store and deal with real numbers](https://en.wikipedia.org/wiki/Floating-point_arithmetic).) . That precision limitation can eventually cause rounding errors and since our tileset is a single image, with many tiles that are composed, when selecting a portion of it from tileset and rendering that tile in the game, such rounding errors might cause the rendering engine to wrongly "pick" or "leave out" a pixel from a tile. This effect is known in game development as Pixel Bleeding. ## Ghost busting! And how can we solve that? Well, there isn't much we can do about the floating-point number precision as that is still a big area of study in computer science. So we need to be conscious of that issue, and work around it! One interesting way to work around those rounding errors, is to simply create an empty spacing between the tiles! Since the rounding errors usually will get the pixel right next to it, if we add a spacing on the tiles, even if a rounding error happens, the texture sampling will pick a transparent pixel. Seems simple enough, let's give it a try. To make our life easier, we developed a small tool called [Tile Patcher](https://github.com/VGVentures/tile_patcher) that given a tileset, it will automatically add the spacing for all the tiles. Lets see it in action: ![Tile Patcher tool adding spacing between tiles in the tileset](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-4.gif) Now we need to tell Tiled that our tileset has a spacing of 2 pixels: ![Configuring 2-pixel tile spacing in the Tiled editor](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-5.gif) And if we run our game again: ![Game map still showing transparent grid lines after adding tile spacing](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-6.png) Wait, something doesn’t seem right, there are even more lines now! That is true, but take a closer look, you will notice that those artifacts that were showing on top of the plants, in which the small plant tile would "pick" the bottom pixel from the white flower, are not showing anymore! And these consistent lines showing in the grid are now a result of the rounding errors picking the transparent pixel between the tiles. But we can use those rounding errors to our advantage! These transparent pixels being picked up, happens only on tiles that need to perfectly fit to another tile on one of its sides. Given that premise, we can patch all tiles that can be placed tidily to others to instead of adding that transparent pixel, repeat its last pixel, so when a round error happens, it will pick up a repeated pixel and that line will be filled! Happily, that Tile Patcher tool also include a feature to patch tiles, so that is what we will do, select every tile that can have one of its borders side by side to other tile and ask for the tool to patch it: ![Tile Patcher patching border pixels on tiles that sit adjacent to other tiles](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-7.gif) Basically what we are doing here is to patch the side of the tiles which will be tidily to other tiles, so the tiles in the center will have all its sides patched, the ones on the left side will have all patched, except for the left ones, and the same logic applied to the other ones. By understanding the nuances of tile-based maps and addressing precision issues with tools like Tile Patcher, we successfully eliminated ghost lines in Super Dash, creating a more polished and seamless gaming experience. By approaching the issue in this way, we ensure that rounding errors work in our favor, enhancing the overall visual integrity of the game.  ![Final game map rendering with no ghost lines after tile patching](/assets/images/blog/solving-super-dashs-rendering-challenges-eliminating-ghost-lines-for-a-seamless-gaming-experience/body-8.png) And that is it, if we run our game again, everything will be working fine! ### How we built it: Ask Dash - A generative AI Flutter application Source: https://verygood.ventures/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/ [*This article was first published on the Flutter Medium blog.*](https://medium.com/flutter/how-we-built-it-ask-dash-a-generative-ai-flutter-application-79a836ced058) As part of the [Google Cloud Applied AI Summit](https://cloudonair.withgoogle.com/), the Flutter and Vertex AI teams collaborated with Very Good Ventures to create an AI-powered Flutter demo app, [Ask Dash](https://github.com/VeryGoodOpenSource/dash_ai_search), using [Vertex AI Search and Conversation](https://cloud.google.com/vertex-ai-search-and-conversation) by Google Cloud. Vertex AI Search and Conversation empowers you to build and deploy search and conversational applications quickly with little to no experience in AI. Flutter was a great way to build a beautiful, customized search experience to show how both products can be used to build powerful applications in only a few short weeks! Vertex AI Search and Conversation allows you to create applications that interact with your data with personalized responses demonstrating the power of generative AI. Most importantly, it gives you full control over what data your application accesses and indexes so you can control what information is surfaced to which user. All application data and user interactions are stored in your own cloud instance and are never used to train Google’s underlying machine learning models.  Since we used Flutter to build the demo app, we decided to use Flutter documentation as training data. We worked with the Google Cloud team to train the model specifically on the Flutter and Dart developer documentation to provide generative AI responses to questions like: What is Flutter? What platforms does it support? And what is hot reload? While much of this data is readily available in public AI models, this demo showcases how you can train a model on just your own data to create powerful AI experiences. This article takes you through how our partner, Very Good Ventures, built a Flutter web application and how we connected the app in the Cloud console. ## How we built the Flutter web app The idea of creating a search application trained on the Flutter docs was straightforward. In fact, the [official Flutter documentation](https://docs.flutter.dev/) already provides a simple search experience that delivers relevant page results for questions on Flutter. However, when conceptualizing what to build, we wanted to demonstrate how Flutter can be used to create visually appealing interactive experiences that are fun and engaging. ![Ask Dash app interface with animated search and AI-generated response](/assets/images/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/body-0.gif) ### Creating interactive animations in Flutter Flutter empowered the team to implement a wide array of animations seamlessly. Its rich set of animations, coupled with the flexibility of widgets, allowed us to create transitions, engaging motion effects, and fluid user interactions. From creating animated loading states while generating the results, to a Dash sprite that waves when the answers appear, Flutter provided flexibility to turn what could be a basic text response into something fun for users to interact with. ![Animated Dash sprite waving alongside loading states during AI generation](/assets/images/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/body-1.gif) ### Visualizing natural language search results Unlike a traditional search experience, Vertex AI Search provides a natural language response to the question asked. The answer is generated with AI sourced from various pages within the Flutter documentation and presented as a summarized response alongside cards that display the relevant pages used by the AI. Each card provides the title to the page and a description so that the user can flip through the cards to get more context on the AI response. ![Vertex AI Search results with citation cards flipping to source pages](/assets/images/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/body-2.gif) Additionally, within the natural language response, Vertex AI Search provides a link to the source of each sentence in the response—giving the user a more granular understanding of where each part of the response originated. In the demo, clicking on the number next to each sentence flips the cards to the relevant source page. ### More engaging in less time Going into the project, we had a tight deadline to launch the demo at the [Google Cloud Applied AI Summit](https://cloudonair.withgoogle.com/). Flutter’s efficient development and ease-of-use, significantly expedited the development process for the team of two working on the application. It provided the necessary tools and framework to build complex animations efficiently, enabling our team to build and launch this demo in just under the two weeks leading up to the event. ## Connecting the Flutter app to the Vertex API Integrating our front end web application with the Vertex AI Search API was achieved with a simple request using the [\`http\` package](https://pub.dev/packages/http). Without requiring any previous experience in building AI, Vertex AI Search provided answers to user-generated questions as JSON responses that were parsed and displayed within animated widgets. This allowed the team to focus on Flutter development to create an engaging experience out of the generated data. ### Setting up generative AI search in your application Setting up Vertex AI and hosting the API for our Flutter app was also straightforward.. In our case, we used [https://docs.flutter.dev/](https://docs.flutter.dev/) as our data source and set it up directly in the Google Cloud console. As a Google Cloud customer, getting started with Vertex AI requires just three steps: ### 1\. Create a data store This is your website's digital library, holding all the information you need to generate the AI model based on just the root URL. Google Cloud crawls your website for relevant data and creates a data source for you to query. To set this up in the Google Cloud Console,select **Search and Conversation**. Choose **Data Stores** and then **New Data Store**. Opt for **Website URL** as the source and provide your website's URL. ### 2\. Access your data Next, create an app in Cloud Console to navigate the data indexed by the model and link it to the data store you created earlier. Under **Search and Conversation**, choose **Apps** and then **New App**. Select **Search** as the type and give your app a name that reflects its purpose, like Ask Dash. ### 3\. Craft a Cloud Function Finally, create a Cloud Function. This is the API wrapper that exposes your Vertex AI data to other applications. In the Console, go to **Cloud Functions** and select **Create Function**. That’s it! From there, use the API in your front-end application as you would any API to send requests and receive formatted responses that your application can display. To test it out, head to the **Function** page and select **Testing**. Enter a JSON object with a “search\_term” key for your question (such as “hot reload”), and see a detailed response containing a natural language summary, relevant citations, and concise summaries of the referenced pages. Learn more about how to get started with [Vertex AI Search](https://cloud.google.com/generative-ai-app-builder/docs/try-enterprise-search) in Google Cloud’s documentation. ## Generative AI applications built in Flutter To see Ask Dash in action and learn more about how we built it, check out the video session from the [Google Cloud Applied AI Summit](https://cloudonair.withgoogle.com/), where Alan Blount, a Product Manager for Google Cloud, breaks down the build process to show the potential of Vertex AI Search in a Flutter application. Check out the [open source Flutter code](https://github.com/VeryGoodOpenSource/dash_ai_search) for the demo and get started with your own AI search experience in Google Cloud Console. Ask Dash is just the start for how Flutter can power interactive Generative AI experiences in applications. We can’t wait to see what you build! ### Secure Your GCP: The Power of Least Privilege Source: https://verygood.ventures/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/ During my journey to become a Professional Cloud Developer certified by Google Cloud, I found key insights that I will be sharing in this blog. For over three years, I have been using Firebase as my preferred Backend as a Service (BaaS) in the projects I work on. The fact that Firebase runs on Google Cloud was a decisive factor that influenced my decision to take the exam. Despite my prior experience with services like Firestore or Cloud Storage, I took a course to deepen my understanding of services I wasn't familiar with, such as Compute Engine or App Engine. One notable aspect that caught my attention during my certification study was the Principle of Least Privilege, which I’ll explain in this blog. I’ll also go through a very important service offered by Google Cloud called Identity and Access Management and how you can use it to achieve this principle. ## Principle of Least Privilege: What is it? The Principle of Least Privilege (PoLP) underscores the importance of providing a resource, be it a Cloud Function, a Compute Engine instance, or a cluster operating on Google Kubernetes Engine, with only the minimal permissions required for its proper functioning. You may be wondering why you should follow this principle while not giving extra permissions to your resources located on GCP. Let's delve into the reasons why this practice is significant. For instance, think about a Cloud Function tasked with reading a file from Cloud Storage. Adhering to PoLP dictates that you provide solely the read permissions required for that specific function, avoiding unnecessary permissions such as write access. If you grant permissions to your resources that are not needed, you’re introducing potential security risks because you’ll be providing access beyond what is essential for the task at hand. ## Identity and Access Management (IAM) Identity and Access Management is a service offered by Google Cloud that gives you the ability to verify that a certain identity (it could be a user, a group of users or even an app) has the necessary permissions to access a particular resource (a virtual server, a database, etc.). Through something called “roles”, you assign a set of permissions to an identity. GCP offers three types of roles: **Basic roles (or primitive roles)**: these roles are not recommended for production environments. - **Viewer**: Read only access. - **Editor**: Read and write access. - **Owner**: Besides having read and write access, you can also manage roles and permissions and you can even change the billing configuration. **Predefined roles**: Roles that are created and managed by Google for different purposes. **Custom roles**: If the predefined roles offered by Google are not sufficient, you can create your own. Now that you know the difference between the roles offered by GCP, we can continue describing how to achieve the PoLP using a simple example. ## Implementation examples Let’s consider the Cloud Function I talked about earlier in this blog. Its end goal is to read a file from a bucket located on Cloud Storage. By default, all Cloud Functions are given the same exact service account that has the Editor role. Let’s remember, that role besides including all read permissions, can modify existing resources.  Although the Cloud Function will perfectly run with that service account, it would be violating the PoLP. If we take a look at the IAM permissions related to Cloud Storage, the Cloud Function would only need the *storage.objects.get* permission to run as expected.  Let’s see how you can replace the permissions given by the Editor role with the one you need (we won’t cover how to deploy a Cloud Function and we will assume that it was already deployed): 1. The first step would be to search for “IAM” in the Google Cloud search bar. 2. Once you’re there, you’ll have to select the “Service Accounts” options located on the left side. 3. Here you’ll see all the service accounts that your project has configured. To create a new service account, you have to press the “Create service account” button located at the top (beneath the search bar). 4. After its initial configuration where you set the name and a description (the ID will be populated automatically but you can change it), Google Cloud will ask for the role you want to assign to that service account. Here we will use a predefined role offered by Google called “Storage Object Viewer” that has the *storage.objects.get* permission we need. ![GCP IAM service account creation with name and description fields](/assets/images/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/body-0.png) ![Assigning Storage Object Viewer role to the new service account](/assets/images/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/body-1.png) 5. You can avoid the last step since we don’t need it for the correct execution of our Cloud Function. All you have to do is click on the “Done” button located at the bottom. 6. Once the service account is created, you’ll set it listed. An email address was associated with it for identification purposes. Now, we have to search “Cloud Functions” in the search bar. 7. Search for the Cloud Function you’d like to edit. 8. Click on the “Edit” button located at the top (beneath the search bar). 9. Click on the “Runtime, build, connections and security settings” dropdown to open it. 10. Under the “Runtime” tab, click on the Service account dropdown and search for the service account you created on the previous step. ![Cloud Function runtime settings with service account dropdown selection](/assets/images/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/body-2.png) 11. You’ll then have to click on Next and then Deploy. The Cloud Function would be redeployed with the updated service account. And **that’s it**! The Cloud Function has associated with it the newly created service account and it follows the PoLP principle! Now let’s suppose you need to implement a new requirement: The Cloud Function has to write some results after reading the file. The good news is that you don’t have to create a new service account. Google Cloud offers the possibility to edit service accounts and add or remove permissions as needed. 1. Go to IAM. 2. Under the IAM tab, search the service account you want to edit by its email address and click on the pencil icon located at the right. ![IAM console with edit pencil icon to modify service account roles](/assets/images/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/body-3.png) 3. Click on the “Add another role” button and search for the Storage Object Creator role. This is also a predefined role that has the *storage.objects.create* permission. ![Adding Storage Object Creator role to the existing service account](/assets/images/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/body-4.png) 4. Click on it to add it to the list and then click on the “Save” button. The window will close and you’ll see the new role associated with the service account. ![Updated service account with both Viewer and Creator roles listed](/assets/images/blog/principle-of-least-privilege-one-of-googles-best-security-practices-for-your-gcp-resources/body-5.png) Great! The service account was updated and it now has the necessary permissions to both read and write objects to a Cloud Storage bucket. ## Concluding Notes The Principle of Least Privilege stands out as a key security practice recommended by Google Cloud. Its implementation is straightforward and facilitated by IAM, which provides highly detailed control over your resources. Following this practice provides you with the assurance of knowing that in the event an unwanted person gains unauthorized access to a service account associated with any of your resources hosted on Google Cloud, they will only have a limited set of permissions, thus constraining their actions significantly. I hope this blog gives you a better understanding of how to improve the security of the resources you’ve located on GCP. Happy coding! ### Behind the Unicorn Magic: Get to know the VGV Team Source: https://verygood.ventures/blog/behind-the-unicorn-magic-get-to-know-the-vgv-team-2/ ## Toms's Background T: I initially started as an engineer, working with a startup in the mobile web browsing space. This journey led me to Nokia when the startup was acquired. Over time, I transitioned to product management, finding a passion for the business side of technology. My career spans over 14 years with significant time working for Nokia and Microsoft in their mobile phones division. Additionally, I spent six years at BMW, where I ran the Tech office in Chicago, working in mobile but also in the automotive space. It was at BMW that I was introduced to Flutter, a technology that I knew I wanted to stick with. I found it very interesting as a technology and it seemed to solve a very real problem I have spent a lot of time in my career trying to manage, especially when it came to supporting lots of platforms. ## Role at VGV **Can you describe what you currently do and how you found out about VGV?** T: I am currently the Director of Product Development at VGV, managing our internal investments and products, which include our Open-Source tools and projects for both our internal teams and the broader community. At BMW, we faced a challenge with feature work going primarily with the iOS app, creating a lag in the Android app updates leading to customer dissatisfaction and bad reviews. We explored various technologies and eventually settled on Flutter due to its rapid improvements around mid-2019. The result was the successful launch of the My BMW app in 2020, showcasing Flutter's prowess. During this period, we engaged with Google and the Flutter community, which led us to discover the work being done by VGV.  In early 2020, some teammates from BMW joined VGV, and that further sparked our interest. I was impressed by VGV’s work and that’s why I ended up joining the team given the exciting technology ecosystem and the prominent growth trajectory.  **What are the common challenges you face in your role, and how do you approach solving them?** T: Balancing the needs of our clients with our internal goals is a key challenge. We have our internal initiatives and client projects, and finding the right equilibrium is crucial. However, when we have strong partnerships, there's an opportunity for knowledge sharing, mutual growth, and a symbiotic relationship. This balance is evident in our investments in open source and tools, which ultimately accelerates our overall progress, while building up our tooling and expertise around certain processes. We aim to have that symbiotic relationship so we along with our partners can grow together.  **What are some of the projects you've worked on at VGV that you're particularly proud of?** T: When I joined VGV, the Program Management team wasn’t yet established as an idea or a department. My initial challenge was to build this team from scratch, developing processes and gaining buy-in, particularly from existing clients. Transitioning a growing company from zero to a fully functioning department was significant. It involved standing up new processes and ensuring our clients embraced these changes. While it's relatively easier to implement changes with new clients, securing buy-in from existing clients requires strategic efforts. Over the years, I've played a crucial role in joining and supporting the growth of the team and also engaging in various Google projects. One standout project for me was I/O Pinball, where my engineering background proved valuable during crunch time. Despite the challenges, the project had a successful outcome, and it's always gratifying to witness the positive reactions and further solidify our relationship with Google. **Are there any emerging technology or business trends that you get excited about?** T: I know it might sound cliché, but my interest in AI and LLM AI models is not driven by the fear of them taking away our jobs. I'm fascinated by the potential to redefine how we approach our tools. Over the past decade, software engineering has evolved into more of a craft or even an art form, with a shift away from mundane tasks. The goal is to eliminate repetitive tasks so that we can concentrate on tackling complex problems and building solutions that truly resonate with our clients or end users. I've been particularly impressed with the ability of these tools to streamline problem-solving. For instance, asking an AI to examine code and provide guidance on a challenging issue can save valuable time. What might have taken an engineer seven hours can now be addressed in just five minutes, allowing them to move on to more impactful work. It's about supercharging our existing efforts rather than negating the value of the work we already do. ## Looking Forward **What aspirations do you have for the impact of your work at VGV in the future?** T: A lot of my background has been at larger companies like Nokia and Microsoft, where I worked on projects with a global scale and products catering to millions of users. I've experienced the challenges and rewards of large-scale releases and broad-reaching impacts. As VGV continues to grow, I see the potential for us to achieve a similar expansive reach, whether it's through our own projects and products or in collaboration with our clients—many of whom are already well-established global brands. In the earlier part of my career, I may have taken the global scale for granted while working for these large corporations. However, having had the opportunity to work with smaller clients, I've come to appreciate the unique dynamics of their niches. Guiding clients, whether big or small, from one point to another is something I find enjoyable. Witnessing the growth of our clients and hoping that our tools and products will follow a similar trajectory, transitioning from a smaller scale to making a significant impact or achieving global growth, is where I focus . It's a fun story to tell, especially when you've been successful in the journey. ## Who is Tom beyond VGV? - Born and raised in Chicago suburbs - Graduated from DeVry University and Keller Graduate School with a degree in Computer Information Systems and an MBA - Hobbies: Given his daily work behind a computer, Tom tries to have pretty active hobbies across a range of climates: Triathlons during the summer, having completed more than 15 races, including a 70.3 Ironman. In the winter, he participates in Curling, a sport he has been playing since 2017. Additionally, he enjoys Scuba Diving any time of the year, especially since it typically means travel to a warm climate. With over 100 hours of underwater time, he tends to always be planning the next road trip with his wife. ### From Idea to Game: Building Super Dash in Just Six Weeks Source: https://verygood.ventures/blog/how-we-built-the-new-super-dash-demo-in-flutter-and-flame-in-just-six-weeks/ [*This article was first published on the Flutter Medium blog.*](https://medium.com/flutter/how-we-built-the-new-super-dash-demo-in-flutter-and-flame-in-just-six-weeks-9c7aa2a5ad31) Super Dash is a new Flutter demo game built from the [Endless Runner Flame Template](https://github.com/flutter/games/tree/main/templates/endless_runner) that launched with the recent update of the Flutter [Casual Games Toolkit](https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440). We partnered with the Flutter team at Google to develop a multiplatform game for Android, iOS and Web from idea to launch in just six weeks with Flutter and Flame. Inspired by nostalgic classics like Super Mario, players run in a side-scrolling platformer game as Dash, who collects acorns and eggs to maximize her score and progress through levels. The game can be played on a [desktop browser](https://superdash.flutter.dev/) and is available for mobile on the [Apple App Store](https://apps.apple.com/app/super-dash-flutter-game/id6473050393) and [Google Play](https://play.google.com/store/apps/details?id=com.verygoodventures.super_dash). The code for the game (including mobile platforms) is open-sourced and available in the [super_dash](https://github.com/flutter/super_dash) repo on GitHub. ![Super Dash gameplay with Dash collecting acorns in a side-scrolling level](/assets/images/blog/how-we-built-the-new-super-dash-demo-in-flutter-and-flame-in-just-six-weeks/body-0.gif) ## How to play Dash runs through each level collecting Acorns (10 points) and Eggs (1000 points) to increase her score. Press the spacebar or tap the screen to start the game and jump to avoid bugs and falling into pits to make it through the level. Grab the Golden Feather to transform Dash into a Phoenix, increasing her jump height and giving her an extra life. ## Fast and seamless game development using Flutter Flutter made it possible to go from idea to launch in just six weeks with just two developers on our team. This framework's ability to quickly iterate through changes, powered by its hot reload features, plus the platform’s composability, modularity, and available tools, allowed the team to move fast and quickly test and evolve the game alongside the design team work. It also accelerates the speed at which the team could build a responsive UI for mobile while targeting browsers and desktop too. This allowed us to dedicate more of our development time to the game itself and reduce the time spent on making sure the game worked across different devices and screens. Additionally, Flutter and Dart’s robust [package ecosystem](https://pub.dev/packages/flutter_bloc) and integrations with Google services like Firebase made it easy to find tools such as [Flutter_Bloc](https://pub.dev/packages/flutter_bloc), which enabled a seamless creation of game logic and state management. ## A Flame-powered Flutter adventure Super Dash uses [Flame](https://flame-engine.org/), an open-source game engine built in Flutter. While many casual games can be built with just Flutter alone, Flame extends Flutter’s game development capabilities for games that require a game loop, collision, and maps.  Super Dash extends the functionality of the base Flame Template available in the [Casual Games Toolkit](https://flutter.dev/games) with the [Leap project](https://github.com/kurtome/leap) - an open source project created by [Kurtome](https://github.com/kurtome), a member of the Flame Community. Before using Leap, you should have a basic understanding of Flame’s FlameGame, Camera, PositionComponent, and TiledComponent components. In the repo, you’ll find a custom tool we built for debugging the game, including a macOS version with various actions and utilities. We added a "Teleport to end" button to take Dash to the end screen without playing the entire level, and another button to make Dash immortal, allowing you to go through the entire level without restarting. You can find this tool in the [super_dash repo](https://github.com/flutter/super_dash) and try it yourself when building or extending this game. All physical objects have rectangular hitboxes that are defined by the object's size and position, and they don't have to be the same as the object's visual appearance, something that significantly improves playability.The team used [Firebase Distribution](https://firebase.google.com/docs/app-distribution) to easily share the app and the game’s test builds, especially for iOS, which sometimes tends to be quite complicated.  ## Designing an interactive Dash world  Super Dash's design is straightforward and it makes it easy to play. We used the open-sourced [Tiled](https://www.mapeditor.org/) tool to design the levels, and the [leap](https://pub.dev/packages/leap) package to integrate these into the game. We used the open-sourced Tiled tool to design the levels, and the leap package to integrate these into the game. Leap uses the the [flame_tiled](https://pub.dev/packages/flame_tiled) package package (where TiledComponent is implemented) and builds on it to add platformer physics and useful classes like PhysicalEntity for objects, enemies, and the player. The flame\_tiled package parses the files from tiled to render the map, layers, and objects. ![Super Dash level background with parallax layers designed in Tiled](/assets/images/blog/how-we-built-the-new-super-dash-demo-in-flutter-and-flame-in-just-six-weeks/body-1.png) *Background Static Image* ### LeapMap A component accessible through LeapGame.leapMap, the LeapMap manages the Tiled map and automatically constructs the tiles with proper collision detection for the ground terrain. ```dart void main() { runApp(GameWidget(game: MyLeapGame())); } class MyLeapGame extends LeapGame with HasTappables, HasKeyboardHandlerComponents { late final Player player; @override Future onLoad() async { await super.onLoad(); // "map.tmx" should be a Tiled map the meets the Leap requirements defined below await loadWorldAndMap('map.tmx', 16); setFixedViewportInTiles(32, 16); player = Player(); add(player); camera.followComponent(player); } } ``` To add custom behavior you can access the layers through LeapGame.leapMap.tiledMap and integrate your own special behavior for tiles or objects. #### Customizing layer names and classes You can ask Leap to use different classes, types, or names. To do so, build and pass a custom LeapConfiguration to the game. The following example builds a custom LeapConfiguration: ```dart class MyLeapGame extends LeapGame { MyLeapGame() : super( configuration: LeapConfiguration( tiled: const TiledOptions( groundLayerName: 'Ground', metadataLayerName: 'Metadata', playerSpawnClass: 'PlayerSpawn', hazardClass: 'Hazard', damageProperty: 'Damage', platformClass: 'Platform', slopeType: 'Slope', slopeRightTopProperty: 'RightTop', slopeLeftTopProperty: 'LeftTop', ), ), ); } ``` We worked with [HOPR](https://www.hopr.tv/) Studio on the design and artwork for the game. To create a more seamless workflow, we established a common language or path to describe item behaviors. For example, adding an "I" indicated infinity, meaning the item would behave the same way repeatedly (moving from left to right or vice versa, etc). This made it easier for everyone to understand the game's goals between teams. Once the map was ready, we incorporated it into the game development. ![Super Dash game instructions showing controls and collectible items](/assets/images/blog/how-we-built-the-new-super-dash-demo-in-flutter-and-flame-in-just-six-weeks/body-2.png) *Super Dash Game instructions* ### What’s next The Super Dash demo was released as part of the November 2023 update to the [Flutter Casual Games Toolkit](http://flutter.dev/games) as an example of a multiplatform game using Flame. By highlighting that we went from idea to launch in just 6 weeks, we hope to showcase how Flutter can help you be more productive and reach more users across a variety of platforms. Check out the [open source code](https://github.com/flutter/super_dash) to see how we built it. [Play the game](https://superdash.flutter.dev/) now on a desktop browser or download the mobile app on [Apple App Store](https://apps.apple.com/app/super-dash-flutter-game/id6473050393) and [Google Play](https://play.google.com/store/apps/details?id=com.verygoodventures.super_dash)! Can you beat the high score? Learn more about our partnership with Google for game development by reading about how we worked together to release [I/O Flip](https://verygood.ventures/blog/how-its-made-i-o-flip/) and [I/O Pinball](https://verygood.ventures/success-stories/i-o-pinball/) in the past months! ### Road to 100% Test Coverage Source: https://verygood.ventures/blog/road-to-100-test-coverage/ At Very Good Ventures, we incorporate the “Very Good” concept into our services like [Very Good News](https://verygood.ventures/solution/very-good-news/) and [Very Good Start](https://verygood.ventures/solution/very-good-start/) and I would like to extend it to testing. This isn't just a nod to our company’s name; it underscores our belief in the significance of testing quality over quantity, steering away from a mere focus on metrics. This is the main topic I talked about when I had the opportunity to be a speaker at [Fluttercon Berlin](https://verygood.ventures/blog/very-good-ventures-goes-to-fluttercon-2023/) and [Flutterconf Latam](https://verygood.ventures/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/) this year.  ![Oscar Martin presenting on test coverage at Flutterconf Latam](/assets/images/blog/road-to-100-test-coverage/body-0.jpeg) *On the stage at Flutterconf Latam in Medellín, Colombia* Let's delve into some strategies and insights we employ at VGV for effective testing. ## Why (Very Good) Tests Are Important The main question you might ask if you are new to testing is **“Why are tests important? Is it not enough to go through my app manually and verify there are no bugs?”.** Hopefully, I can answer those in this blog post for you. But the first thing you need to know about testing is that you will need a process to ensure that your app quality is gonna be increased: It’s not just about adding a final step in your app development phase. If you start changing your mindset about testing you will see yourself avoiding stressful situations and you will start enjoying the path. How? Let me highlight three things -among others-  that we at VGV consider important about testing: 1. **It makes you think about your architecture.** Most of the time a hard test is linked to poor architecture. So, the more you test, the more you will feel confident about your code architecture. But we will get beyond this point further in this blog.  2. **100% test coverage doesn't mean 0% bugs.** You shouldn’t focus on the number itself, but on covering all possible scenarios. Let me share a very good tip: If for some reason a case was not covered and a bug is found, make sure to consistently add a test for that case to prevent its recurrence. 3. **The more you test, the more you will control all your business logic.** This is especially important when reviewing a new code base to see if it has tests. If not, it's probably a good time to add them while onboarding in the code. Your final goal should be to increase confidence in any future change in the code to deliver fast and safely. ## Architecture Matters As mentioned before, one key point in order to have a smooth approach to testing is having a good architecture. Especially for developers new to the practice, they might experience a big learning curve for testing, but in reality, most of the problems they will face with it will be related to bad architectural decisions.  This blog post is not intended to be focused on architecture, but here are some pieces of advice that will help with testing: - Split your code into layers that allow dependency injection. - Don't over complicate your classes and methods (SOLID) - Don't mix layers. For example: Don't put business logic into your UI. - Use libraries/packages that help you with testing. For example: Static methods are known for being quite complex to test. If you need more insights into how to build a good and scalable architecture for testing, you can take a look at this [blog post](https://verygood.ventures/blog/very-good-flutter-architecture/).  ## But, What Is Code Coverage?  Code coverage is a metric that allows you to identify which parts of your code have been exercised by a test. In Dart/Flutter, we measure that by lines of code. In order to collect the coverage we need to follow 2 simple steps: 1. Execute our tests collecting coverage ###### very\_good test --test-randomize-ordering-seed random --coverage As you might have noticed, we execute the tests in random order to verify we don't have side effects between those. 2. Visualize the coverage report ###### genhtml ./coverage/lcov.info -o coverage && open ./coverage/index.html ![HTML coverage report generated by genhtml showing line coverage results](/assets/images/blog/road-to-100-test-coverage/body-1.png) ## Why 100% and Not 80%? As I mentioned before, when you are doing tests, the last thing you should be thinking about is the metric or the number itself. You can have 100% coverage and it still is possible that your tests are not really valuable. Code coverage is only a metric, but If we could add a restriction to ensure that we cover all possible scenarios we would use that one instead. For example, consider a scenario where you have a button adding a bloc event upon clicking on it, while you may have a test that clicks on the button, it's crucial to ensure that the test verifies the execution of the underlying logic as well, because you probably have covered this button tap but the reality is that you are not testing anything.  See the example of covering the same code but not the same scenario below: ![Test covering a button tap without verifying bloc event execution](/assets/images/blog/road-to-100-test-coverage/body-2.png) ![Test covering the same button tap while also verifying the bloc event fires](/assets/images/blog/road-to-100-test-coverage/body-3.png) At the same time, if you start defining a number lower than 100%, you need to start making the hard decision on what areas you will not be covering. Should you skip models? Should you skip UI? Should you skip external library usage? People are surprised when we aim at VGV for 100% code coverage, and a recurrent question that was asked to me in both talks was about exceptions.  We only have a few exceptions, and most of them are related to auto generated code, for example, localized strings or assets since there is no added value about testing programmatically on those. ## Summing Up At Very Good Ventures, we have been putting all emphasis on prioritizing test quality over metrics, aiming for 100% test coverage, with a focus on comprehensive testing that ideally covers all scenarios. Emphasizing architectural best practices such as layer separation and simplicity, plays a crucial role while aiming to bring confidence in every code change, ensuring fast and secure delivery through meticulous testing practices. Feel free to reach out to me on my [LinkedIn](https://www.linkedin.com/in/omartinma/) or [Twitter](https://twitter.com/OscarMartinDEV) account to learn more! ### Very Good CLI License Checker Source: https://verygood.ventures/blog/very-good-cli-license-checker/ Software is typically subject to one or more licenses that dictate what you can and cannot do with a piece of code. When developing a Dart or Flutter project, you'll usually end up depending on packages hosted by [Dart's package manager](https://pub.dev/); these are associated with a license. We highly recommend verifying the **health** of any new third-party dependency you introduce to your project. Therefore, tracking the rights and restrictions that external dependencies may impose on your project is a crucial part of this verification. ![Pie chart of license distribution across 46,000 Dart and Flutter packages on pub.dev](/assets/images/blog/very-good-cli-license-checker/body-0.png) *Pie chart is a breakdown of the different license usages from around 46,000 Dart and Flutter packages hosted in pub.dev.* ## Understanding license detection In most cases, package developers use a canonical license to govern their software. A comprehensive list of commonly found licenses can be found at the [Software Package Data Exchange (SPDX)](https://spdx.org/licenses/) License list, managed by the SPDX Workgroup a [Linux Foundation](https://www.linuxfoundation.org/about) Project. If your favorite license is not there you can request to get your [license added](https://tools.spdx.org/app/submit_new_license/)! [Dart's package manager](https://pub.dev/) relies on [PANA](https://pub.dev/packages/pana) (short for PAckage aNAlyzer) to determine the license specified by the package author for their published package. It does so by scanning the license text specified by the package developer and comparing it with common licenses. If no confident match is found, it is reported as 'unknown.' ![PANA package on pub.dev with its detected BSD-3-Clause license highlighted](/assets/images/blog/very-good-cli-license-checker/body-1.png) *The pana package is shown as a pub.dev search result with its detected BSD-3-Clause license highlighted.* As expected, custom licenses are identified as unknown. However, sometimes a canonical license might be incorrectly formatted or not appropriately recognized by [PANA](https://github.com/dart-lang/pana). In those scenarios, we recommend filing an issue so the package owner is aware of the detection failure or directly informing [PANA](https://github.com/dart-lang/pana/issues), where appropriate. > [“It is important to report these issues, otherwise fixing these won't get a high priority in the near future”](https://github.com/dart-lang/pub-dev/issues/7129#issuecomment-1770264749) - [István Soós](https://github.com/isoos), Senior Software Engineer at Google working on PANA ## Automating license checking verification ![Terminal recording of very_good packages check licenses command output](/assets/images/blog/very-good-cli-license-checker/body-2.gif) *Terminal screen recording showing Very Good CLI’s license checking capabilities* Checking licenses for each dependency individually can be a laborious task. Therefore, [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli), an open-source command-line interface for generating scalable templates and executing helpful commands, provides a simple and straightforward command to automate this task, starting from  [version 0.17.0](https://pub.dev/packages/very_good_cli/changelog). After [installing Very Good CLI](https://cli.vgv.dev/docs/overview#installing), you can simply run the license checker within your project: ###### very\_good packages check licenses If you wish to only allow the use of certain licenses, you may use the --allowed option: ###### very\_good packages check licenses --allowed=MIT,BSD-3-Clause ###### ###### \# ✓ Retrieved 6 licenses from 6 packages of type: BSD-3-Clause (3), MIT (1), unknown (1) and Apache-2.0 (1). ###### \# 2 dependencies have banned licenses: html (unknown) and universal\_io (Apache-2.0). There are a few arguments you can pass to the command to tailor its behavior to your specific needs. For example, the --forbidden option denies the use of certain licenses or the --dependency-type specifies the type of dependencies to check licenses for. For a full documentation of all the available options refer to the official [Very Good CLI documentation website](https://cli.vgv.dev/docs/commands/check_licenses). ## Automating license checking verification with GitHub Workflows To guarantee the dependencies licenses are always checked it is a very good idea to make this part of your automated development pipeline. If you’re using GitHub we’ve made available a reusable open-source workflow to easily start license checking your dependencies. You can find more details about this new workflow at the [Very Good Workflows documentation](https://workflows.vgv.dev/docs/workflows/license_check); or quickly get started by adding the following license\_check.yaml file to your project’s .github/workflows directory. ###### name: license\_check ###### ###### concurrency: ######   group: ${{ github.workflow }}-${{ github.ref }} ######   cancel-in-progress: true ###### ###### on: ######   pull\_request: ######     paths: ######       - "pubspec.yaml" ######       - ".github/workflows/license\_check.yaml" ######   push: ######     branches: ######       - main ######     paths: ######       - "pubspec.yaml" ######       - ".github/workflows/license\_check.yaml" ###### ###### jobs: ######   license\_check: ######     uses: VeryGoodOpenSource/very\_good\_workflows/.github/workflows/license\_check.yml@v1 ### Highlights from FlutterConf Latam 2023 Source: https://verygood.ventures/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/ ![VGV team members at the sponsor booth at FlutterConf Latam](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-0.jpeg) *The VGV team at our booth* Last week we had the opportunity to attend FlutterConf 2023 in Medellín, Colombia. This two-day conference was packed with entertaining talks, insightful conversations, and of course, quality networking time with fellow developers. We were a platinum sponsor, and were lucky to be able to send seven speakers. Here are our takeaways from the conference. {% video url="https://www.youtube.com/watch?v=_p8j29GdD4E" title="Video" %} ## The Flutter community shines bright! At FlutterConf Latam, we connected with developers, designers, and passionate Flutter enthusiasts from around the world. We participated in informative keynotes, attended enlightening sessions, and engaged in discussions that pave the way for a future of innovation and collaboration within the Flutter community.  This time our team got the opportunity to meet and connect with new people: Flutter devs and enthusiasts from different parts of Latin America, such as Perú, Guatemala, Argentina, Brazil, Uruguay, Ecuador, and Bolivia, among others. Spanish was our primary language since most of our team came from native Spanish-speaking countries like Uruguay, Argentina and Spain. This fostered a positive networking environment, and exciting, spontaneous talks with the attendees. ![FlutterConf Latam attendees mingling between sessions in Medellin](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-1.jpeg) We might be a bit biased, but the atmosphere at FlutterConf Latam was undeniably electrifying. The Flutter community shines as an exceptional and dynamic developer community on the global stage. FlutterConf Latam only reinforces this fact: The enthusiasm and commitment demonstrated by the Flutter community are unmatched. The community's inclusiveness and supportiveness are genuinely remarkable, as developers from diverse backgrounds unite to celebrate their shared passion for Flutter and app development. The collective wealth of knowledge and expertise within the Flutter community benefits both newcomers and seasoned professionals alike. From the lively online forums and repositories to the numerous open-source projects, the Flutter community consistently serves as a source of inspiration and empowerment for developers worldwide. As a platinum sponsor, our booth was full of experiences and entertainment. This time we encouraged everyone to try their best on Pinball I/O, a game we developed using Flutter and Firebase along with Google for the Google I/O 2022 event. We had tablets available to play during the conference and very good trophies for the top three highest scorers of the day. ![Conference attendees playing I/O Pinball at the VGV booth](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-2.png) *Some of our I/O Pinball champs!* ![VGV I/O Pinball trophy awarded to top scorers at FlutterConf Latam](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-3.jpeg) *The Very Good Ventures I/O Pinball Trophy* Special, limited edition VGV swag was also available to take home: T-shirts with personalized messages (Very Good @ \_\_\_\_ ) and all kinds of stickers. We know now that our unicorn logo will be all over many places in Latam! ![Personalized Very Good at ___ t-shirts at the VGV booth](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-4.png) *Very Good @___ shirts were a hit!* ![Limited edition VGV stickers and swag at FlutterConf Latam](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-5.jpeg) *Some of our limited edition swag!* Our development team offered valuable code assessments, shared insights, and engaged in discussions about the Flutter ecosystem. We appreciate everyone who visited our booth, exchanged experiences, and shared their thoughts with us. These interactions were a truly enriching and invaluable experience that we carry with us as the most important takeaway from the event. ## Unicorns showcase the magic of Flutter  We had unicorns all over FlutterConf! Seven of them presented on different topics at the conference across a wide variety of Flutter topics — from testing to the culture of Flutter. We hope these presentations will motivate developers to explore the frontiers of Flutter development, crafting extraordinary user experiences for a global audience. **Jorge Coca**, our Head of Engineering, delivered the opening keynote on day one of the conference, discussing the engineering landscape. While the engineering field can often seem overwhelming with its abundance of buzzwords, Jorge's keynote primarily centered on the true heart of it all – the people!  ![Jorge Coca delivering the opening keynote at FlutterConf Latam](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-6.jpeg) *Jorge Coca* **Óscar Martín** explored the importance of writing a functional, robust and scalable code.   ![Oscar Martin presenting on robust and scalable Flutter code](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-7.jpeg) *Óscar Martín* **Bettina Carrizo** exhibited the advantages and drawbacks of creating a UI package for your Flutter app. ![Bettina Carrizo speaking about Flutter UI packages](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-8.jpeg) *Bettina Carrizo* **Renan Araujo** spoke about the transformative potential of Fragment shaders in UI design.  ![Renan Araujo presenting on Fragment shaders at FlutterConf Latam](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-9.jpeg) *Renan Araujo* **Erick Zanardo** exhibited the power of the Flame engine for game development by creating an exciting space shooter game using Flutter and Flame. ![Erick Zanardo demonstrating Flame game development with Flutter](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-10.jpeg) *Erick Zanardo* **Matias Leyba & Juan Manuel Silveira** explained how gestures work in Flutter, what the Gesture Arena is, and how they compete to declare a single winner.  ![Matias Leyba and Juan Manuel Silveira presenting on Flutter gestures](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-11.jpeg) *Matias Leyba & Juan Manuel Silveira* We even had a couple of VGV unicorns on the **GDE panel** talk.  ![Google Developer Experts panel with VGV team members at FlutterConf Latam](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-12.jpeg) *Google Developer Experts (GDE) Panel* ## Impressions from the team We're truly amazed by the incredible energy within the community. Witnessing firsthand the depth of inspiration we evoke within the community is truly remarkable. We were amazed by everyone who approached us to show their interest in working at VGV. We are glad we connected with all and hopefully, in the near future, we can keep broadening our workforce. While there were numerous impactful conversations, we’d like to highlight two in particular: A tech lead from a company in Colombia expressed, "Ever since I became acquainted with your work, you've transformed my life. I'm profoundly grateful to you for making my work life more manageable and productive." Another tech lead shared, "You've compelled me to reevaluate all the constraints my company had imposed on us. Since I've come to know you, there are no longer any excuses for not delivering on time and maintaining high standards." We love hearing great feedback like this from the community. In this era of social media, it's easy to underestimate the extent of our influence. A simple blog post or tweet can have a profound impact on people. Even if they're not direct clients, they contribute to the broader Flutter ecosystem. This is invaluable for the future of Flutter as a whole. We’d like to extend our gratitude to the Flutter community, and to everyone who participated in this conference, as well as the event organizers for putting together such a great conference!  ## The Future of Flutter ![Group photo of Flutter community members at FlutterConf Latam 2023](/assets/images/blog/very-good-ventures-highlights-from-flutterconf-latam-2023/body-13.jpeg) With the ongoing growth and flourishing of the Flutter community, we eagerly await the upcoming surge of revolutionary applications, captivating user experiences, and transformative advancements that will define the future of Flutter. Participating in FlutterConf Latam was truly invigorating! We eagerly anticipate forthcoming collaborations, thrilling projects, and the ongoing exchange of ideas within this lively community. Together, we embark on a journey driven by enthusiasm, creativity, and a unified vision to create exceptional mobile and web applications through Flutter. The most exciting times lie ahead, and the Flutter community is ready to lead innovation, spark inspiration, and mold the future of multi-platform app development. ### Flutter Can Transform Engineering Team Culture Source: https://verygood.ventures/blog/flutter-can-transform-engineering-team-culture/ In the constantly evolving world of software development, the significance of building team culture often takes a back seat to creating technological innovation and building product features.  This is a topic that, as the Founder and CEO of Very Good Ventures (VGV), I emphasized during my talk at [Fluttercon](https://verygood.ventures/blog/very-good-ventures-goes-to-fluttercon-2023/) '23 in Berlin, Germany, explaining why technology team culture is a significant factor in shaping the success and expansion of organizations — and how Flutter creates an opportunity for change. ![David DeRemer delivering keynote on Flutter and team culture at Fluttercon Berlin 2023](/assets/images/blog/flutter-can-transform-engineering-team-culture/body-0.jpeg) *Keynote “The Culture of Flutter: Transforming Orgs from Within” at Fluttercon Berlin 2023* In this article, I will delve into the insights I shared during the talk and explore how Flutter, the leading open-source UI software development toolkit, can be the catalyst that transforms team cultures and creates better outcomes. ## The Role of Culture in Organizations To understand how culture impacts teams and organizations, we must initially comprehend the essence of culture itself. To do so, I have been conducting research and talking with different professionals, partners, and business colleagues to have a better understanding of it.  For instance, Ben Horowitz^1^ defines a company’s culture as “The collective behavior of everyone in the organization, what people do when they’re not giving them a direction, and the organization’s way of doing things.” In simpler terms, culture is how people within a company collaborate, interact, and collectively pursue their goals, and it is enormously important. **Culture significantly impacts an organization's performance, and employee satisfaction.** The right culture can boost productivity, innovation, and morale, while the wrong culture can lead to employee turnover and hinder growth.  > “Culture can be one of the biggest multipliers to a team's performance across the board.” according to Christopher Gulliver, former Director of Engineering at Slickdeals and CEO & Founder at Lonch.  Following these definitions, it's essential for all organizations to remember that an investment in culture and how a team performs can lead to real business results and revenue growth. Research from the MIT Sloan Management Review^2^ indicates that nearly two-thirds of employees regard corporate culture as one of the most significant reasons for staying with their current employer. Moreover, Forbes^3^ reports that companies with robust cultures experience a staggering fourfold increase in revenue growth. So what opportunities exist for specific technologies, such as Flutter, to positively impact this crucial area? ## Flutter and Culture: Discovering a new catalyst You might be wondering: How does Flutter, a toolkit for building multi platform apps and user interfaces, relate to nurturing a healthy organizational culture? **At VGV, we’ve found an exciting connection: While Flutter is renowned for its efficiency and capacity to craft cross-platform apps from a single codebase, it tends to have a less often discussed yet profound impact on team cultures.**  Based on my own observations at VGV and experiences across a vast array of client programs, working with Flutter tends to build great team cultures — Flutter teams just seem to be happier. There’s something about the developer experience that is better and less frustrating. Flutter also helps transform team culture by bringing teams more closely together. This is a hugely important value of adopting Flutter. At VGV, we got into Flutter because it was just better and easier to build with it, but it ended up having a vast impact on our team culture.  ![VGV Flutter developer team group photo](/assets/images/blog/flutter-can-transform-engineering-team-culture/body-1.jpeg) *Part of the VGV Flutter developer team* Before going any further, I need to be precise — Flutter doesn’t create a great culture all by itself. Technology can’t make a great culture — it’s the people that create it. As a team, we create a culture by being who we are and by how we collaborate with each other. But Flutter - and really any technology - can play a central role by affecting how a team does its job.  I’ve found five ways Flutter is a catalyst for team culture. There are probably more, and these catalysts are not exclusive to Flutter, but the key is that these catalysts exist. Leaders who can take advantage of these catalysts can reap rewards far beyond the benefits of the technology. ### 1. Creating “The Opportunity” for Cultural Transformation: Flutter creates *the opportunity* for cultural change. There’s nothing uniquely specific about Flutter here other than the fact that it's often part of a transformative moment for a team. Flutter projects are generally brand-new products or a product re-platforming; teams are starting over, re-booting something, or investing in something new. This creates a reset moment, an opportunity to interrogate the culture, see how you are performing, your processes, your behavior as a team, etc. **A new Flutter initiative represents a chance to seize the opportunity and invest the energy to reboot your culture.**  Keller Williams, the largest real estate franchise in the USA, is a great example. They had a consumer app, where code was becoming a challenge, getting disorganized, hard to maintain, and did not have a lot of test automation. They wanted to improve their codebase and their app, but their team culture. Flutter presented the opportunity: > “We decided that the opportunity to break old habits — among Flutter’s other benefits — was a compelling reason to switch.” Chris Synan, VP Engineering, KW Consumer Engineering.  Flutter provided an opening for them to invest in redefining the team culture. They made a concerted effort to focus as much on improving how they work as the code they were improving. As part of the transition to Flutter, they gained a better-shared understanding of their codebase and how they could contribute to it effectively. That helped them have better awareness, clearer expectations, and better visibility into how long it would take to do things. Since then, “The team's culture has changed dramatically. The levels of pride and confidence have grown considerably.”, Chris said.  ### 2. Redefining Developer Identity:  Developers often identify themselves by the type of technology they work with. For instance, you may be an iOS developer, Android developer, or JavaScript developer. Since developers working with Flutter can build for almost any platform, Flutter dissolves these technology and platform partitions. **Flutter allows developers to concentrate on their value rather than the tools they employ.** This shift, from being pigeonholed by technology to being recognized as "Product Engineers," fosters team unity and cultivates a shared sense of purpose. Agtonomy is another company that has invested heavily in Flutter as a strategic capability. They are a company specializing in autonomous tractors and they picked Flutter for their full ecosystem of apps as well as using Dart for the backend. As a result of uniting the whole software stack: “We’re able to share code across apps, collaborate better, and make decisions faster since it’s all Flutter & Dart.” David Chen, Head of Software Services at Agtonomy. Their team can share and collaborate in a way that typically is not the norm. They’re not compartmentalized by platform, device, language, platform, etc.  > “Our people and skills are portable. Anyone can jump into any part of the ecosystem to improve the product. We don’t see ourselves as engineers specific to a single platform — we see ourselves as ‘Product Engineers.’” David mentioned. The Agtonomy team is just focused on building the product. They want to create the best outcome for the product and its customers. They are not focused on the platform, the language, or technical distinctions. This is one of the things Flutter does: Eliminates the barriers between team identities to realign the value the collective team is creating, which in turn strengthens the team culture. ### 3. Enhanced Cross-functional Collaboration:  Flutter promotes more effective collaboration across cross-functional teams. Traditional software development often engenders intricate and complex communication pathways with numerous teams and stakeholders, leading to delays and inefficiencies. **Flutter streamlines these interactions by simply reducing the number of nodes in the communication network, rendering it more straightforward for teams to collaborate seamlessly.** Realtor.com's^4^ mobile team found themselves reorganizing around domain teams after adopting Flutter. This restructuring eradicated bottlenecks and enhanced communication with stakeholders. It stands as evidence of how Flutter's developmental approach augments collaboration and can extend its influence beyond the codebase into the very fabric of the organization itself. Flutter changed how the organization is designed. Before Flutter, the Realtor.com mobile team was organized by platform, meaning iOS and Android, existing a brick wall right down the middle. Because of this separation, it became hard to scale: There were divergences in features and UI, different patterns, and they were solving the same problem multiple times, among other issues. By adopting Flutter and embracing the single codebase multi-platform paradigm, they could reorganize how their team was structured. So rather than organize by platform, they've dedicated themselves to specific features, uniting the teams and bringing them together by eliminating (now) arbitrary barriers. The exciting part is that Flutter’s true multi-platform nature — reaching far more than just two platforms like most alternatives — means this benefit is not just about a single codebase multi-platform for a mobile app.  It’s about multi-codebase multi-platform across a large ecosystem of apps with shared culture, shared behavior, shared value, shared beliefs, shared code, shared tools, and so on. **In this world where we have to move as fast as we can ship, any little thing we can do to help our team, to help our company, to help our sanity and happiness, we should take advantage of. Improved collaboration and communication can be a major boon, and Flutter is a big catalyst for that.**  ### 4. Amplified ROI for Cultural Investments:  Constructing a positive team culture needs time and effort; it’s not something you can just hope will happen. Leaders must invest significant resources into building documentation and tools, advocating standards, and communicating and aligning people in the organization. Flutter's productivity gains grant organizations the capacity to invest more in activities that build culture because the investment can be spread around more of the organization, increasing the ROI of those activities. At VGV, our exceptional team culture is closely intertwined with Flutter's contributions. In early 2020, we documented our core beliefs and values, a crucial step in fostering consistency and unity within our organization. These shared principles guided our codebase and facilitated seamless onboarding for new team members. Flutter's technical efficiencies played a pivotal role in enabling us to invest deeply in our culture. Unlike our previous experience with native iOS and Android development, Flutter streamlined the process, allowing us to build essential tools and processes without the burden of platform duplication. This efficiency unlocked the potential for mindful investments in our culture, making us the company we are today. In other words, **the increased focus and the ability of a team to accomplish more with less redundant work and less complex technology expenses means that there is more time available for investments in culture, and those investments can have a larger impact.** ### 5. A Supportive Community:  One of Flutter’s critical benefits is that it boasts a passionate and highly supportive community. I think the Flutter community is incredible, defined by its openness, collaboration, and positivity, rendering it an invaluable asset for organizations embracing Flutter. Teams and organizations are influenced by the communities they engage with. The culture of these communities rub off on the people that connect with them. Leaders need to evaluate not only the merits of a technology but also the potential impact on the team culture that a technology’s surrounding community may have. The Flutter community thrives on the unique qualities that set it apart. First and foremost, an undeniable enthusiasm has been present since Flutter's inception. **Positivity permeates the community, with members focusing on what Flutter can achieve rather than its imperfections. Supportiveness and collaboration are the cornerstones of this community. People are keen on building each other up rather than tearing others down, fostering an environment of teamwork.** Open source values and transparency are embraced wholeheartedly, not only by Google but also by the community at large. This strong sense of community manifests in various ways, from Google's early support to the dedicated Flutteristas, GDEs, and community groups, as well as the plethora of meetups, open-source projects, and online interactions. Teams working with Flutter bring these positive cultural elements into their own organizations. Contrast with some other development communities that are known to be more toxic and competitive and the implications become more clear. The Flutter community's strong positive ethos influences the culture of teams and companies that adopt the technology in a meaningful way. ## In the end, it all depends on the people... Building a solid culture is one of the most important things a leader can do, but it's hard to get it right and we need all the help we can get. Flutter alone won't do it for us, but it provides advantages and opportunities — and we need to act on these catalysts. Flutter presents opportunities for cultural metamorphosis, redefines developer identities, bolsters cross-functional collaboration, augments the ROI of cultural investments, and thrives within the nurturing embrace of an extraordinary community. I think Flutter is a rare gift and change agent to reset and transform team culture. Yet, as a community, it’s not talked about enough — when we advocate and sell Flutter, more people and more organizations need to call attention to this incredibly important value. Improving culture — it's the most important thing! In the long run, the cultural impact may have far more value than the written code. So, how do we actually create a good culture? The only way to do it, is to do it! Seize the opportunity and make change happen. ### Very Good Ventures at FlutterConf Latam 2023 Source: https://verygood.ventures/blog/very-good-ventures-at-flutterconf-latam-2023/ The highly anticipated [FlutterConf Latam](https://flutterconflatam.dev/#/home) is set to be held in the beautiful city of Medellín, Colombia, from October 25th to 26th, 2023. This event is poised to be a highlight for Flutter enthusiasts globally, with a particular focus on the Flutter community in Latin America. Flutter developers and technology experts will have the opportunity to come together, gain inspiration from industry leaders, enhance their knowledge of best practices, and engage in valuable networking opportunities. As Platinum sponsors, we are thrilled to be a part of it!   Our excitement for this event is magnified by our recent [merger with CreateThrive](https://verygood.ventures/blog/vgv-acquires-createthrive/). This merger has not only elevated us to the status of being one of the world's largest Flutter consultancy, but has also brought us closer to the heart of the Latam dev community. As Platinum Sponsors of FlutterConf Latam, we will have a booth at the event with live demos. Throughout the conference, you'll find a bunch of unicorns at our booth- we are always eager to meet with developers and engineers to discuss the latest developments in the Flutter world. We're excited to share our recent projects, exchange thoughts on the various keynote speeches, workshops, and tech sessions delivered by experts, and hear your impressions. Please swing by to meet the VGV team, and grab some unicorn swag!  Our team is hard at work preparing their talks full of useful information and insights to bring up to the FlutterConf stage. We will have seven unicorns presenting on stage! Last August, we had the privilege of participating in [Fluttercon in Berlin](https://verygood.ventures/blog/very-good-ventures-will-be-at-fluttercon-berlin-2023/), where six of our unicorns shared their expertise on various subjects, spanning from shaders to testing to engineering culture. Now, we eagerly anticipate new and exciting keynotes, sessions, and workshops, all led by the following team members: ## Unicorns take the FlutterConf stage!  Jorge Coca: **Always be growing! Scaling engineering culture with a smile on your face!** He will be exploring observability, monitoring, testing, dependency injection, state management, debugging, vertical and horizontal scalability. While the engineering landscape can be daunting with its myriad buzzwords, Jorge's keynote will focus on the true heart of it all – the people! Óscar Martín: **On the way to 100% code coverage.** Óscar will delve into the importance of writing functional, robust, and scalable code. Developers and companies are increasingly recognizing the significance of maintaining a codebase that is thoroughly tested to achieve these objectives. Code coverage serves as one of the available metrics to measure which parts of the code have been executed during tests. During this talk, there will also be an introduction to different types of tests, including Unit tests (for clients, repositories, blocs) and UI Testing (Widget tests). If you are interested in understanding the importance of tests and code coverage, as well as how to determine if you are using the right architecture for testing, don't miss this session  *This talk will be held in Spanish.*  Bettina Carrizo: **Widgets made easy: build your own UI library on Flutter.** In this presentation, Bettina will explore the advantages and drawbacks of creating a UI package for your Flutter app. She'll illustrate its potential with a real-world case study, showcasing its remarkable capabilities. You'll gain insights into an alternative approach to UI componentization and abstraction that's user-friendly, exceptionally reusable, and a breeze to test. This approach will enhance collaboration between developers and designers, enabling more efficient teamwork. Renan Araujo: **Flame Games + Shaders=mind blown.** Game development is the best way to put the power of fragment shaders into play. In this session, Renan will delve into various techniques, including bias and masking, to craft some of the most visually stunning 2D games you can imagine. Erick Zanardo: **Flame workshop: Space Shooter.** Join Erick for an exciting session where he will help you create a space shooter game using Flutter and the Flame engine. He will cover a wide range of topics, including sprites, animations, gesture and input handling, collision detection, special effects, mastering the game loop, assembling components, creating immersive parallax backgrounds, and much more!  By the end of this workshop, you'll walk away with a fully playable game that you can continue to enhance and personalize on your own. Don't worry if you haven't worked with Flame or Flutter before – no previous experience is required, although some familiarity with Dart can certainly help! Matias Leyba & Juan Manuel Silveira: The **“How to be a Gladiator in the Gesture Arena”** workshop offers an opportunity to share knowledge and experiences relating to Flutter’s gestures. In this workshop, Matías will explain how gestures work in Flutter, what the Gesture Arena is, and how they compete to declare a single winner. The presentation will also delve into the primary elements of the Gesture Arena, such as GestureRecognizers, RawGestureDetector, and more, to demonstrate how they function. With these concepts in mind, they will share experiences in which their team had to manage conflicts in an app where various gestures coexist simultaneously.  Finally, Matías will conduct a code review while implementing an example, culminating in an exercise showcase for attendees. *This workshop will be conducted in Spanish.* ### Flutter History & Timeline: Every Major Milestone (2017-2023) Source: https://verygood.ventures/blog/flutters-history-so-far-all-the-biggest-milestones/ When you trace the history of Flutter, you can see a new standard for multi-platform development coming into focus. Year by year, tracking the announcements, demos, and reveals at industry events provides important context into how far Flutter development has come and where it's going next. In a relatively short time, Flutter has become developers' go-to choice for building multi-platform apps. In the five years since 2018, the ecosystem around Flutter has grown and thrived, with ongoing support from Google. As of May 2023, the Flutter team reported that [more than one million published apps use Flutter](https://medium.com/flutter/racing-forward-at-i-o-2023-with-flutter-and-dart-df2a8fa841ab). Showing Flutter's dominance of the multi-platform space, that figure is greater than the proportion of new apps developed on all other multi-platform frameworks combined. We here at Very Good Ventures are especially proud and excited to chronicle the story of Flutter because we've been there every step of the way. We've had the incredible opportunity to have a presence at each of these milestone events, often collaborating directly with Google to showcase the power of Flutter. Now, from 2017 to 2023 and beyond, let’s take a look at the highlights of Flutter's time in the limelight. ![Timeline of major Flutter releases from 2017 to 2023](/assets/images/blog/flutters-history-so-far-all-the-biggest-milestones/body-0.png) *Timeline of major Flutter releases.* ## 2017: The Release of the First Commercial Flutter App [The Hamilton app](https://verygood.ventures/success-stories/hamilton/) was developed by VGV's founding team during their tenure at the now-closed Posse, with Hamilton being VGV's inaugural client. This significant collaboration led to the creation of the Hamilton app, marking a momentous milestone as the first Flutter-based app outside of Google. The efficiency and remarkable productivity of Flutter played a pivotal role in the app's development, enabling it to launch swiftly on both iOS and Android within a mere three months. ## 2018: The Beginning This was the year when Flutter made a major impact for the first time. Though the year-defining event occurred in December, there was plenty going on behind the scenes in the run-up to the show. From the very beginning, Very Good Ventures was there, providing hands-on support for Google in a high-stakes role. ### Flutter Live '18 (December 2018) At the Flutter Live '18 conference, [Flutter received its initial spotlight](https://verygood.ventures/blog/revealed-flutter-slides-at-flutter-live-18/), letting development teams know they had a new option for their multi-platform efforts. Google's team revealed that Flutter had reached version 1.0. This landmark was accompanied by showcases of features such as Flutter Desktop Embedding, showing that Flutter was not simply for Android and iOS mobile development. Flutter’s vision for the future involved true multi-platform development. Very Good Ventures helped Google pull off a "wow" moment by revealing that the keynote slides were running in a desktop Flutter app. [Flutter Slides](https://verygood.ventures/success-stories/flutter-slides/) was a collaborative project between Google and VGV, and the code was released for developers to explore. ## 2019: Mainstream Breakthroughs In 2019, developers proved that Flutter's ability to bridge the desktop and mobile worlds was ready for primetime. This was a year of debuts across multiple events, and once again VGV was at the vanguard. ### Google I/O '19 (May 2019) During the Google Developer Keynote at the 2019 I/O conference, the [New York Times KENKEN app made its debut](https://verygood.ventures/success-stories/new-york-times/). This puzzle application, developed by VGV in collaboration with The New York Times and Google, represented a significant milestone as one of the first major Flutter apps available on the web. ### Flutter Interact (December 2019) At the 2019 [Flutter Interact conference](https://verygood.ventures/blog/flutter-interact-recap/), Google unveiled Flutter 1.12. Features included in the new version included improved support for advanced features across iOS and Apple, along with upgraded support for add-to-app. The latter feature makes it easier to add Flutter modules to existing native platforms, letting developers try out Flutter in an existing native app, rather than starting from scratch. Other announcements at the show included the beta release of Flutter for web, which had been available to the community in an earlier release since May. The same event saw the alpha reveal of Flutter for macOS, further expanding the reach of Flutter-based apps. Buoyed by these releases and more, the Flutter development community grew and stayed active in 2019. ## 2021: Stability and Evolution Following a year of gatherings thwarted by the pandemic, 2021 is when the story of major Flutter events resumes. Of course, things were hardly "back to normal," and conferences were still virtual. With that said, there was a flood of releases and announcements throughout the year, and VGV was on hand to usher Flutter into its new era. ### Flutter Engage (March 2021) The big news out of Flutter Engage, held online in March, was the launch of Flutter 2. In the time between the 1.12 and 2 releases, the Google team had deepened their commitment to true multi-platform development. Flutter 2 supported six platforms: iOS, Android, web, macOS, Windows and Linux. In addition to the big Flutter 2 reveal, the development team also announced that Flutter on the web had achieved stability. The development team noted that rather than a static website-building tool, Flutter’s web support is primarily a way to make rich interactive app experiences truly multi-platform. The Dart language received an update, too, with the 2.12 release coinciding with several new DevTools launches. Dart 2.12 brought null safety, alongside early explorations into static meta-programming. Flutter Engage also showed off a theme that would be prominent in the years ahead: Flutter as the basis for experiences that go well beyond the standard mobile-desktop dichotomy. Toyota presented its new vehicle infotainment systems powered by Flutter and enabled by the Embedder API. ### Google I/O '21 (May 2021) Just two months after Flutter Engage and the release of Flutter 2, Google revealed Flutter 2.2 [at Google I/O](https://www.youtube.com/watch?v=S5aK3TIOnIw). With the release, null safety became the default setting for Flutter. Other announcements at the show included desktop development improvements, the release of new DevTools and a payments SDK update. The event also provided a powerful showcase of just how many teams are using Flutter. VGV was very much among these, teaming up with Google for a powerful new demo on the web: [I/O Photo Booth](https://github.com/flutter/photobooth). The app served as a showcase for building high-quality apps with Flutter and Firebase. ## 2022: Opening New Windows Despite the rapid roll-out of new Flutter versions and capabilities in 2021, the pace stayed rapid in 2022. The Google development team continued to move Flutter toward true multi-platform support, with a fully featured focus on Windows to go along with the existing mobile, web and Mac capabilities. ### Flutter Update: Windows (February 2022) Google displayed its commitment to Flutter's Windows compatibility with this [special-purpose virtual event](https://youtu.be/g-0B_Vfc9qM), called specifically to announce advancements in support for the platform. The 2.10 update for Flutter brought full  support for building Windows apps within Flutter. The Windows compatibility launched with hundreds of plugins and thousands of packages available, allow developers to build powerful software immediately. Now, engineering teams can include specialized desktop features alongside functionality that works across mobile and desktop platforms. ### Google I/O '22 (May 2022) The annual Google I/O conference once again delivered [major updates for Flutter](https://www.youtube.com/watch?v=OOHr8cn2Z0g). In 2022, this meant the release of Flutter 3. With this release, developers gained full support for building web and macOS applications in Flutter, following up on the addition of Windows three months earlier. In addition to expanding full development capabilities to all six platforms, the Flutter 3 update was accompanied by new Dev Tools, and the release of Dart 2.17, as well as deepening partnerships with other technology tools. To appeal directly to developers who may be wondering how to make practical use of Flutter, Google I/O also saw the launch of [Put Flutter to Work](https://flutter.dev/go/try). The demonstration app is meant to make it easy to see the power of Flutter by integrating a ready-made module into an existing Android, iOS, or web application. Google I/O in 2022 also highlighted using Flutter for game development with the release of the Flutter Casual Games Tool Kit. That pack of development tools is designed to help engineers use Flutter for game creation, an increasingly popular function. VGV worked in collaboration with Google on [I/O Pinball](https://pinball.flutter.dev/), a game built with Flutter and Flame, acting as a proof of concept for building beautiful and performant 2D games. ## 2023: Looking Forward With five years of steady progression and new features in the books, Flutter is in a position of strength as of 2023, picking up market share from other frameworks such as React Native. Developers' commitment to Flutter development is a key ingredient in maintaining momentum. ### Flutter Forward (January 2023) The Flutter Forward conference represented a chance to bring developers from around the world together to [celebrate the latest releases and updates](https://verygood.ventures/blog/vgv-at-flutter-forward/). Both Flutter 3.7 and Dart 3 alpha debuted at the show, alongside specific, targeted capabilities such as element embedding for the web. Two projects at the show involved input from Very Good Ventures. In collaboration with Google, the VGV team worked on [Flutter News Toolkit](https://medium.com/flutter/announcing-the-flutter-news-toolkit-180a0d32c012), an open source template to help developers create article-based apps, with capabilities such as monetization and search alongside frequently updated text content feeds. VGV also worked on [Holobooth](https://holobooth.flutter.dev/#/), an updated take on the I/O Photo Booth demonstration web app from 2021. This new version shows off integrations with Firebase, Rive and machine learning with MediaPipe and Tensorflow.js. ### Google I/O ‘23 (May 2023) The growing momentum of Flutter was apparent at [Google I/O](https://io.google/2023/) this year. With Flutter being relatively stable, the team concentrated on improving its existing capabilities, and streamlining its integrations. In addition to the exciting news that there are now over one million published Flutter-based apps, there is a renewed focus on exploring new technologies, particularly WebAssembly. WebAssembly (Wasm) brings the performance of native code to the web and enables Flutter to render three times faster in the browser.  Flutter 3.10 was also highlighted during the Developer Keynote. It provides numerous advantages for the web, including faster load times, enhanced shader performance, and element embedding.  VGV worked in collaboration with Google on [I/O FLIP](https://flip.withgoogle.com/), a card game designed by AI, which was showcased on stage. The game incorporates a number of Google tools, including new generative AI technologies. It uses Flutter, Firebase Cloud Firestore, and Cloud Run for the application. The card images were pre-generated with DreamBooth on Muse and card descriptions were pre-generated with the PaLM API. ### Fluttercon Berlin(July 2023) Fluttercon had developers, designers, and Flutter enthusiasts from around the globe gather in Berlin. The conference had keynotes, insightful sessions, and engaging discussions, setting the stage for a future of innovation and collaboration within the Flutter community.  Todd Volkert, Engineering Director for Flutter, gave an update at the event on some stats for Flutter during his [keynote](https://www.droidcon.com/2023/08/06/fluttercon-the-keynote/): there are now 6,00,000+ Flutter developers and 1,100,000 Flutter apps on the market. It’s clear that the Flutter community is just getting started! ## An Unlimited Future Between the steady reveal of new features, the helpful and creative developer community, and the growing momentum of companies adopting Flutter, the future looks promising. We look forward to continuing to show the world what’s possible with Flutter and working with clients on enterprise apps. ### Getting Flutter to work for you is simple, especially when there are industry-leading experts there to lend a hand and demonstrate the transformative power of Flutter. ### Very Good Plugins: Very Good Flutter Styles Source: https://verygood.ventures/blog/very-good-plugins-very-good-flutter-styles/ You can now effortlessly translate Figma color and type styles into Dart code, eliminating the onerous and manual process of design to developer style handoff. Say goodbye to countless copy-and-paste actions and hello to a streamlined workflow that saves time, ensures accuracy, and guarantees consistency. Explore how this incredibly useful plugin, developed by our talented design team, in close collaboration with developers, empowers product teams to quickly and easily generate theme files and stay aligned around the project’s styles. ## Why is This Important? Handing off styles between design and dev can be tedious, costly, and prone to errors. Material-based Flutter themes contain anywhere from dozens to hundreds of color values, along with 15 text styles each with around a half dozen properties. ![Generated Dart color style code from Figma displayed in VS Code](/assets/images/blog/very-good-plugins-very-good-flutter-styles/body-0.png) *Screenshot of the generated color styles in VS Code* Previously all of these styles were manually copied from Figma, translated, formatted, and reassembled into our codebases. That’s hundreds of copy & pastes for every app. We wanted a more automated way to handoff these values to save time and ensure accuracy. ## What We Did Working closely with developers, the VGV design team coded this plugin to take existing Figma styles and export them as Dart code, aiming to make handoff as simple and error-proof as possible. ![Very Good Flutter Styles plugin UI inside Figma with export options](/assets/images/blog/very-good-plugins-very-good-flutter-styles/body-1.png) *Screenshot of the Very Good Flutter Styles Plugin in Figma* The Very Good Flutter Styles plugin lets teams easily generate theme files throughout the project’s lifecycle. Now developers can compare versions of the exports to see which values have changed. As an added benefit, the plugin also ensures our teams are speaking the same language when it comes to style names.  Very Good Flutter Styles delivers enhancements we felt were missing from analogous options from the Figma community. For color, the plugin supports styles with 2 fill colors, which is typically the method for how Material surface levels are set up, and it will actually calculate the flattened value of those styles. The plugin also supports linear and radial gradients, and will export the values for each step. For text, the plugin will only show properties which are used. We also format the style names to lowerCamelCase, stripping out characters which are often used for organization which would break the names in code. ## But Wait, There’s More Dev Mode is one of the latest big feature enhancements in Figma. One of the best additions is that it allows developers to run plugins while they are inspecting designs. We included the necessary settings to ensure that the plugin works in Dev Mode as well, allowing non-editors users to easily export a documents’ styles for Flutter! ![Very Good Flutter Styles plugin running in Figma Dev Mode](/assets/images/blog/very-good-plugins-very-good-flutter-styles/body-2.png) *Screenshot of the Very Good Flutter Styles Plugin in Dev Mode* ## Enjoy That Extra Time! We're releasing these plugins to the Figma community to help other Flutter teams spend less time on handoff and more time on the exciting and fun challenges of product delivery. Using Very Good Flutter Styles is as easy as 1-2-3: 1\. Run the [Very Good Flutter Styles plugin in Figma](https://www.figma.com/community/plugin/1208619373237156795/Very-Good-Flutter-Styles) 2\. Click the Color Styles or Text Styles button to copy Flutter code to your clipboard 3\. Paste into your code editor ## Use Cases Whether you're starting from scratch or translating an existing app to Flutter, you can leverage Very Good Flutter Styles along the way. Say you have an existing design with defined text and color styles, you can run the plugin immediately and paste that code into your new codebase. Because the names in Figma are translated to code, the plugin ensures designers and developers are using the same language to describe styles in your app. ![Generated Dart text style code from Figma displayed in VS Code](/assets/images/blog/very-good-plugins-very-good-flutter-styles/body-3.png) *Screenshot of the generated text styles in VS Code* As the design matures, you may notice style values changing, new styles being added, or being removed. Instead of searching for those changes, you can run the plugin throughout the project lifecycle, and compare versions of the export using your code tools to easily identify what has changed. ![GitHub diff comparing exported style values between plugin versions](/assets/images/blog/very-good-plugins-very-good-flutter-styles/body-4.png) *Screenshot of a code diff in Github* If you are leveraging a Material-based UI Kit, you can export them as soon as the styles have your new values. In fact this plugin was built to export the hundreds of values which are baked into the [Material 3 Design Kit](https://www.figma.com/community/file/1035203688168086460/Material-3-Design-Kit). ### Training Clients in Flutter Source: https://verygood.ventures/blog/training-clients-in-flutter/ *“The art of teaching is the art of assisting discovery,”* [Mark Van Doren](https://en.wikipedia.org/wiki/Mark_Van_Doren) said once. Every student’s journey to knowledge is unique and different. This applies to Flutter as well. Whether you are a small startup trying to find your place in the world or a large corporation exploring new ways to provide value faster to your customers, your “journey to Flutter” might look different. All reach the same result: **performant and delightful experiences getting built in record time!** It's impossible to deny that agencies play a vital role in this mission. By working with various partners, agencies develop a unique point of view on how to successfully drive the adoption of new stacks, frameworks, libraries, and patterns. That’s what makes agencies serve as partners of incalculable value in the “journey to Flutter”: Their exposure to dozens of projects at the same time means that they've seen it all. *Agencies have already made the mistakes and learned how to recover, so they can keep you from falling into the same traps.*  ## How it All Started Back in 2018, Very Good Ventures (VGV) built the official [Hamilton](https://hamiltonmusical.com/) mobile app. This app was the first large-production app built with Flutter outside of Google. Since then, we have reunited some of the best talent worldwide and developed a vast amount of high-quality products. We’ve helped all kinds of companies to deliver outstanding Flutter products while upskilling their product teams. **Every time we do this, there remains one constant: Flutter.** We’ve partnered with more than 50 companies in their journey to Flutter. VISA, American Airlines, SlickDeals, Policygenius, Tyson Foods, and Accenture represent just a few that benefited from our [Training program](https://verygood.ventures/solution/flutter-training/). This five-day workshop focuses just on the fundamentals of Flutter, plus a multitude of office hours, code-pairing sessions, exercises, and code reviews. ## *If it works for us, it must work for our partners…* In 2018, when VGV started its journey to Flutter, little content on Flutter and Dart could be found. Still, its rapid success and growth demanded a new internal approach to upskill new developers joining the team. It didn’t matter if these developers were at the beginning of their professional journey or well seasoned. This resulted in our VGV Onboarding Program. This two-week internal program showcases how to become a VGV employee and obtain the necessary knowledge, processes, and standards on the effective use of Flutter and Dart. The *zi* succeeded and rapidly helped our engineers master Flutter and Dart! Many of our partners, curious about our success when welcoming new teammates at VGV, asked us if they could use the same onboarding program to upskill their developers: Thus, [Very Good Training](https://verygood.ventures/solution/flutter-training/) was born!  Similar to our approach when creating new [open source tools](https://github.com/VeryGoodOpenSource), we realized that if an internal initiative adds value to VGV, it will most likely add value to our partners. As we use our own approach internally, it is in our best interest to keep our training approach relevant, up-to-date, consistent, and effective. Our training program assumes that every developer has covered the fundamentals of object-oriented programming. These developers know classes, objects, functions, visibility, mutability, and installation of dependencies. After all, this training takes place in a professional setting. Still, students receive a list of prerequisite activities: As an example, we always request our students to watch the fantastic talk from [Brian Egan, Keep It Simple, State: Architecture for Flutter Apps](https://www.youtube.com/watch?v=zKXz3pUkw9A).  The goal is to let developers explore some of the most basic Flutter concepts so we can start our training on a solid foundation. Over the course of five days, we dedicate ourselves to upskilling our partners through our training program. Students attend presentations, do hands-on exercises and code labs, and have full access to a group of experts ready to answer any questions. At the end of the program, students will have completed a fully tested and functional complex application, available for Android, iOS, macOS, Linux, Windows, and the web. They will receive a certificate of completion for the Very Good Training program and be ready to take their company’s product to the next level in record time! ## Tips & Tricks for an Effective Flutter Training Program **It’s all about culture!** A trainer must be the first cheerleader of the group, must be patient and gracious, and be able to inspire and awaken the student’s minds. A trainer must be the ultimate example of a healthy, engaging, and empowered engineering culture. An excellent teacher can do magic with horrible content, but a horrendous teacher will do an awful job even with superb content.  **Be consistent and develop a point of view.** You can build  a successful Flutter product in many ways. This can be impressive unless you choke on so many options and don’t know which one you should pick. For example, consider state management. Many good options are available, but in our experience, invest in to one and build expertise on it. Since 2019, VGV has committed to use and invest in [flutter_bloc](https://bloclibrary.dev/#/). While we are familiar with many other tools, growing with flutter\_bloc has allowed us to build standards, upskill our developers, find consistent and develop scaling solutions that the whole team understands. **Allow yourself to be vulnerable.** Trainers are constantly exposed! While presenting a topic and sharing their knowledge, the audience can ask any question. This can be tremendously intimidating. A great trainer needs both an enormous amount of confidence in themselves and humility in their interactions. When they have an answer, they share it with grace, but when they don’t know something, they simply respond, *“I don’t know the answer yet, but give me a little bit of time, and we can figure it out together”* **Practice, practice, practice!** Balancing the ratio of presentations to exercises creates a successful training program. After all, engineers add value by writing code (well, many times by deleting code too!), not by watching presentations! The best educational approach uses lectures to *plant the knowledge seed*, while writing code allows students to solve the problem, *watering their knowledge plant. * ### Dart Frog 1.0 is here! 🎉 Source: https://verygood.ventures/blog/dart-frog-1-0-release/ Today we announce the first major release of [Dart Frog with v1.0.0](https://github.com/VeryGoodOpenSource/dart_frog/releases/tag/dart_frog-v1.0.0)! This release focuses on improving the developer experience with Dart Frog. Read on for some of the main features of the release, the history of Dart Frog, and why you should use Dart Frog in your Flutter projects. ![Dart Frog 1.0 release highlights including daemon, auth, and VS Code extension](/assets/images/blog/dart-frog-1-0-release/body-0.png) ## What’s in Dart Frog 1.0 Here are the main features and bug fixes: - **Daemon:** We believe that good tooling is at the heart of improving the developer experience. With this release, we introduce the Dart Frog daemon to make it easier for developers to build first and third party tools to manage, diagnose, and build Dart Frog projects. This will set the foundation for numerous items on our roadmap, including: Open API documentation generation, Dart API client generation, and health check endpoint for monitoring and debugging via IDE extensions and plugins.  - **Authentication:** We added a new package to the Dart Frog ecosystem: [**dart_frog_auth**](https://pub.dev/packages/dart_frog_auth), which provides a foundation to write authentication APIs. This package doesn't implement all of the authentication checking, since this is almost always specific to an application. Instead, **dart\_frog\_auth** gives you a head start for implementing authentication by providing helper middleware that already solves the common code for authentication, so you can focus on the specific implementation details. - **VS Code extension:** As part of our efforts to improve the Dart Frog developer experience, we have begun official support for VS Code as [requested by the community](https://github.com/dart-frog-dev/dart_frog/issues/286). Using the VS Code extension, you will be able to easily create a new project, new routes, and new middleware. There are more exciting features to come to the extension as the Dart Frog daemon is now available! You can already start using the [extension here](https://marketplace.visualstudio.com/items?itemName=VeryGoodVentures.dart-frog). - **Wildcard route support:** Wildcard routes allow developers to map requests handlers that will handle a wider number of variations of urls, such as the following wildcard route **/pages/\[...pages\]** will be able to handle **/pages/page/1**, **/pages/summary**, **/pages/news**, etc. This gives you a lot of flexibility to create dynamic namespaces. - **Environments:** Dart Frog allows developers to define different environments like staging, development, production, etc. We solved bugs that could cause confusion while working with different environments and also added [documentation](https://dart-frog.dev/basics/environment/) to address this. - **FormData support and file uploads:** With the support of the  multipart/form-data API, you can now easily upload multiple form data like data fields and files. Keep in mind that the request's MIME type should always match **application/x-www-form-urlencoded** or **multipart/form-data** for this to work. - **Custom init method:** With the custom init method, you can initialize anything on server start without having to worry about it being reinitialized on server hot reloads. This can be especially useful for setting up database connections, loading environment configurations, or whatever else a developer might need to do only once. [See the full changelog from stable to 1.0 here.](https://verygood.ventures/blog/dart-frog-1-0-release/) ## About Dart Frog Dart Frog is a fast, minimal backend framework for Dart. It can be used by any developer wanting to build a scalable backend, especially Dart and Flutter developers. It is built on top of [shelf](https://pub.dev/packages/shelf) and [mason](https://pub.dev/packages/mason) and is inspired by many tools including [remix.run](https://remix.run/), [next.js](https://nextjs.org/), and [express.js](https://expressjs.com/). Dart Frog is focused on optimizing the process of building backends which aggregate, compose, and normalize data from multiple sources. It’s meant to be a lightweight tool, not a fully-fledged backend solution. Even though it doesn’t bundle features like database access and data caching out of the box, it gives you freedom to use any of your preferred tools with Dart Frog. Dart Frog also provides a simple core with a small API surface area in order to reduce the learning curve and ramp-up time for developers. Having a unified tech stack enables you to share tooling, models, and more! ## Why you should use Dart Frog Here are some of the benefits that Dart Frog brings: - **Developer efficiency:** As a Flutter developer, you can expand your skills beyond frontend to full stack. You can also use some of the same tooling and workflows on the backend to ensure high-quality code (for example, you can use Dart-specific workflows on the CI to validate your code just like you would with Flutter apps). The learning curve is small, for example, building routes on Dart Frog feels very similar to creating a widget tree in Flutter. - **Shared code:** Sharing code between the front and backend means you can write functionality once and use it on both ends of an application. This is particularly helpful for use cases such as a server-authoritative game; in this case, all of the logic happens on the backend and is passed to the frontend for the UI to change accordingly. - **Middleware:** Working with legacy backends can be frustrating and slow down the development process. Dart Frog can be used as a backend for frontend so that the backend data is normalized and becomes easy for developers to use. This approach also gives more control to developers to do things like optimize and cache backend data. - **Cloud agnostic:** Dart Frog can be configured with any cloud service, so you can use whatever works best for you. There are currently docs for deploying to Google Cloud Run, AWS App Runner, and Digital Ocean App Platform. ## History of Dart Frog We’ve been blown away by the reception of Dart Frog since its [initial release in May 2022](https://verygood.ventures/blog/dart-frog/)! It was first released as an experimental backend framework, and after a few months, we upgraded it to stable with a few key features and bug fixes. Dart Frog has since been used in multiple projects in production, including the AI-designed card game [I/O FLIP](https://github.com/flutter/io_flip) and [Flutter News Toolkit](https://github.com/flutter/news_toolkit). It has also gained over 1,400 stars on GitHub and been incorporated into many articles, tutorials, and conference talks. ![Dart Frog GitHub repository with over 1,400 stars](/assets/images/blog/dart-frog-1-0-release/body-1.png) ### [**Try out Dart Frog v1.0! Get started on dart-frog.dev →**](https://dart-frog.dev/) ## A note on major releases There are a few schools of thought when it comes to major or minor releases. In our view, a major release has to meet a few criteria: - The source code is stable - The tool is well-documented - Our team is prepared to support the tool after release - Our team is able to provide continued support for our other [open source tools](https://verygood.ventures/resources/open-source-packages/) (Very Good CLI, etc.) ## Full changelog from stable release to 1.0 **dart\_frog** - feat: stable 1.0.0 release 🎉 - feat: provider support nullable values - feat: Add Response.movedPermanently() constructor - deps: upgrade to Dart ">=3.0.0 <4.0.0" - deps: upgrade to very\_good\_analysis ^5.0.0 - fix: Pipeline does not maintain RequestContext ([#605](https://github.com/VeryGoodOpenSource/dart_frog/pull/605)) - fix: Response.json() overwrites content-type header ([#596](https://github.com/VeryGoodOpenSource/dart_frog/pull/596)) - feat: add SecurityContext named argument to serve method - feat: add Response.stream - feat: add support for multipart/form-data ([#551](https://github.com/VeryGoodOpenSource/dart_frog/pull/551)) - deps: upgrade to Dart ">=2.19.0 <3.0.0" - deps: upgrade to very\_good\_analysis ^4.0.0 - feat: cache Request and Response body - feat: add formData to Request/Response - BREAKING fix: Request.json() and Response.json() return Future - feat: add x-powered-by-header to serve - fix: update Response.json headers to **dart\_frog\_auth** - feat: stable release 🎉 - feat: initial release 🎉 **dart\_frog\_web\_socket** - deps: upgrade to Dart ">=3.0.0 <4.0.0" - deps: upgrade to very\_good\_analysis ^5.0.0 - deps: upgrade to Dart ">=2.19.0 <3.0.0" - deps: upgrade to very\_good\_analysis ^4.0.0 - fix(deps): upgrade to shelf\_web\_socket ^1.0.3 - feat: initial dev release 🎉 **dart\_frog\_gen** - feat: detect conflict between dynamic routes and non-dynamic ones - feat: update to Dart 3 and Very Good Analysis 5 ([#681](https://github.com/VeryGoodOpenSource/dart_frog/pull/681)) - feat: adding wildcard detection to dart frog gen ([#691](https://github.com/VeryGoodOpenSource/dart_frog/pull/691)) - feat!: re-land "add route configuration validation to gen" ([#614](https://github.com/VeryGoodOpenSource/dart_frog/pull/614)) - fix: revert "add route configuration validation to gen" ([#628](https://github.com/VeryGoodOpenSource/dart_frog/pull/628)) - feat: add route configuration validation to gen ([#614](https://github.com/VeryGoodOpenSource/dart_frog/pull/614)) - feat: detect custom init method ([#564](https://github.com/VeryGoodOpenSource/dart_frog/pull/564)) - deps: upgrade to Dart ">=2.19.0 <3.0.0" - deps: upgrade to very\_good\_analysis ^4.0.0 - BREAKING fix: support for cascading middleware - RouteDirectory signature for middleware changed from MiddlewareFile? -> List - perf: exclude route directories with no routes - BREAKING feat: support mounting dynamic routes - BREAKING deps: upgrade to Dart ">=2.18.0 <3.0.0" - deps: upgrade to very\_good\_analysis ^3.1.0 **dart\_frog\_cli** - feat: add uninstall command - feat: add verify-only flag to update command - feat: add dev\_server domain to daemon - feat: daemon logger - fix: Remove multiple EOL when creating route or middleware - feat: enable asserts when running dart\_frog dev - fix: use custom docker file when building prod server - feat: start daemon implementation - feat: add devserver lifecycle - feat: add dart\_frog daemon command - feat: update brick bundles to dart 3.0.0 - feat: Add dart-vm-port option to dev command - feat: added default port number for --dart-vm-service-port - feat: recommend using --dart-vm-service-port on VM port failure - feat: Update to dart 3.0.0 and vg analysis to 5.0.0 - feat: adding list command - feat: adding dart version flag to build command - feat: add "new middleware" and "new route" commands - fix: filter out error messages from killing the server process ([#592](https://github.com/VeryGoodOpenSource/dart_frog/pull/592)) - refactor: remove duplicate dart:io import ([#556](https://github.com/VeryGoodOpenSource/dart_frog/pull/556)) - chore: bump cli\_completion from 0.2.0 to 0.3.0 in /packages/dart\_frog\_cli ([#553](https://github.com/VeryGoodOpenSource/dart_frog/pull/553)) - feat: support custom init method ([#587](https://github.com/VeryGoodOpenSource/dart_frog/pull/587)) - fix: adjust InternetAddress to anyIPv6 - refactor: update to Dart 2.19 and very\_good\_analysis ^4.0.0 - fix: update silently fails when the sdk is incompatible - feat: dart\_frog create uses Dart 2.19 - feat: upgrade to mason ^0.1.0-dev.38 - perf: significant decrease in hook startup times - feat: upgrade to dart\_frog ^0.3.0 - fix: hook execution after pub cache clean - fix: handle UsageException when invalid option is passed ([@Ascenio](https://github.com/Ascenio)) - fix: nested dynamic route resolution - fix: support cascading middleware - feat: add startup log to production build - deps: upgrade to mason ^0.1.0-dev.34 - BREAKING feat: support for mounting dynamic routes - BREAKING deps: upgrade to Dart ">=2.18.0 <3.0.0" - deps: upgrade to very\_good\_analysis ^3.1.0 - fix: run update check on sigint - feat: package:dart\_frog runtime compatibility check - fix: dart\_frog build Dockerfile dependency resolution - fix: copy static assets into docker image - feat: dart\_frog update command - feat: automatic updates prompt - feat: report external path dependencies - feat: dart\_frog create - upgrade to dart 2.18.0 - upgrade to very\_good\_analysis 3.1.0 - fix: avoid generating an empty .dart\_frog directory - refactor: use packageName internally for consistency - fix: dart\_frog build serve static assets - fix: dart\_frog build failures on windows ### Very Good Ventures goes to Fluttercon 2023 Source: https://verygood.ventures/blog/very-good-ventures-goes-to-fluttercon-2023/ Earlier this month we had the opportunity to attend Fluttercon 2023, colocated with droidcon in Berlin. This three-day conference was packed with entertaining talks, insightful conversations, and of course, quality time with fellow developers. We were a silver sponsor and were lucky to be able to send six speakers. Here are our very good takeaways from the conference. {% video url="https://www.youtube.com/watch?v=25s0jMGtqqI" title="Video" %} ## The Flutter community is one of the best out there! At Fluttercon, we had the opportunity to meet with developers, designers, and Flutter enthusiasts from around the globe. We attended keynotes, watched insightful sessions, and had engaging discussions, setting the stage for a future of innovation and collaboration within the Flutter community. ![VGV team networking with Flutter developers at the Fluttercon 2023 venue](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-0.jpeg) The energy at Fluttercon was truly electric! We may be a bit biased, but the Flutter community stands out as one of the most exceptional and vibrant developer communities in the world. Fluttercon further solidified this: The passion and dedication exhibited by the Flutter community were unparalleled. The community's inclusivity and supportiveness are truly remarkable, as developers from all backgrounds and skill levels come together to celebrate their shared love for Flutter development. From the vibrant online forums and repositories to the countless open source projects, the Flutter community never fails to inspire and empower developers around the globe.  ## Unicorns showcase the magic of Flutter in their talks: from shaders to testing to engineering culture We had unicorns all over Fluttercon! Six of them presented on different topics throughout all three days of the conference across a wide variety of Flutter topics — from shader to testing to the culture of Flutter. We hope these talks inspire developers to push the boundaries of Flutter development and create remarkable experiences for users worldwide. **David DeRemer**, our Founder and CEO delivered a keynote on day three of the conference about the cultural impact of Flutter within organizations. ![David DeRemer delivering a keynote on Flutter culture at Fluttercon 2023](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-1.jpeg) **Tomas Piaggio** facilitated a workshop on Firebase's capabilities, and how to leverage them to create robust and scalable apps using Flutter, alongside Renuka Kelkar. ![Tomas Piaggio leading a Firebase and Flutter workshop at Fluttercon](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-2.png) **Renan Araujo** spoke about the transformative potential of Fragment shaders in UI design.  ![Renan Araujo presenting on Fragment shaders in Flutter UI design](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-3.jpeg) **Óscar Martín** covered the significance of writing functional and scalable code.  ![Oscar Martin speaking about scalable code and testing at Fluttercon](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-4.jpeg) **Jochum van der Ploeg** talked about the use of Fluttium, an end-to-end user testing tool for Flutter.  ![Jochum van der Ploeg talking about Fluttium end-to-end testing](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-5.jpeg) **Erick Zanardo** exhibited the power of the Flame engine for game development by creating an exciting space shooter game using Flutter and Flame, in a workshop alongside Lukas Klingsbo. ![Erick Zanardo running a Flame game development workshop at Fluttercon](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-6.jpeg) ## Impressions from our teammates  “I was really impressed by the quality of the talks at Fluttercon. There were so many great speakers, and I learned a lot from them. I especially enjoyed the talk by Óscar on the road to 100% testing coverage. I also had some great conversations with people from the community. I was surprised by how welcoming and friendly everyone was. I felt like I was part of a big family. The best part for me was the sense of community, and the fact that there were people from all over the world, united by the same feeling. It was so inspiring to see so many people passionate about Flutter. I'm really glad I had the opportunity to attend Fluttercon.” - Tomas “One of the things I was impressed with is that as we were sharing the spot with DroidCon, a lot of Android devs were interested in the future of Flutter, recent projects, etc. Erick Zanardo's workshop was amazing and motivated me a lot to work with Flame. I knew from social media that a lot of people knew VGV, but it is so cool to see that in real life, and hearing all the stories about people using our tools like Very Good CLI.” - Oscar “I was impressed by the number of people who knew us and told us that they used our tools. The willingness of people to answer questions and the good vibes that were generated during the three days was amazing. For me the best part was being able to meet so many people from VGV in person, I think it has generated some very good bonds.” - Pablo “My favorite talk was David's keynote. It was great, even for an insider, to understand better why culture is that important, and why VGV bets on improving it. On the last day in our booth, Alejandro and Erick started talking to Eric Seidel (Flutter co-Founder) about Flame. Then the conversation turned into early decisions made in Flutter,  using Dart, and the future of the framework, and eventually there were like 10-12 people around enjoying that conversation. I loved getting to know everyone (colleagues, and people from Twitter and YouTube) in 3D, talking to them in person, and building more relationships.” - Jaime “The talk that stood out the most for me is Eric Seidel’s keynote about the next ten years of Flutter. It was engaging and very dynamic, plus the content was itself very exciting. I had a lot of great conversations, mainly with developers about VGV’s influence and help to the community. I think the OSS tools and dedicated team are an awesome part of VGV and people know it. Two things that people are most excited about, or at least what I think from the conversations I had, are firstly Dart Frog and then Flame. The best part for me was the communication, the opportunity to share on many subjects, and discovering the new projects in the Flutter community.” - Hugo  “David’s talk was very inspiring. I loved how he showed that technology can help build culture, but it only helps, we also need to take action and make it work. I also loved having the opportunity to discuss with Eric Seidel about a few topics — we discussed possible improvements that we could include in Flame if we try to use more ‘low level’ features from Flutter and he also told me a bit of the history on why of some decisions were made in the first days of Flutter development.” -Erick “I want to express my admiration for the vibrant developer community surrounding Flutter. The energy and enthusiasm I experienced at the conference was truly remarkable. The collective power of this community is pushing Flutter to new heights, and I look forward to the incredible advancements that await us. Working with Flutter right now is incredibly exciting, and I feel really fortunate to be a part of it.” - Felipe ## Flutter continues to gain momentum Todd Volkert, Engineering Director for Flutter, gave an update on some stats for Flutter during his keynote: there are now 6,000,000+ Flutter developers and 1,100,000 Flutter apps on the market. It’s clear that the Flutter community is just getting started, and we’re so proud to be a part of it! ![Flutter stats slide showing 6 million developers and 1.1 million apps](/assets/images/blog/very-good-ventures-goes-to-fluttercon-2023/body-7.jpeg) As the Flutter community continues to grow and thrive, we eagerly look forward to witnessing the next wave of innovative applications, captivating user experiences, and transformative developments that will shape the future of Flutter.  Attending Fluttercon was energizing! We can’t wait to see future collaborations, exciting projects, and the continued exchange of ideas within this vibrant community. Together, we are embarking on a journey fueled by passion, creativity, and a shared vision for building exceptional mobile and web applications using Flutter. The best is yet to come, and the Flutter community stands poised to drive innovation, inspire others, and shape the future of multi-platform app development.  ### [**See how we can help on your Flutter journey >**](https://verygood.ventures/our-services/) ### Very Good Ventures and CreateThrive are now one team Source: https://verygood.ventures/blog/vgv-acquires-createthrive/ Today is an exciting milestone for Very Good Ventures! CreateThrive, a consulting company that focuses on building custom software applications for mobile and web, has officially joined our VGV team of unicorns, bringing together two extraordinary leaders in the Flutter consulting ecosystem.  Since the first days of VGV, we’ve always worked to create a corporate culture and brand that people would love to work for, driven by values we stood by and evolved as we grew together. As one of the leading companies in the world in Flutter development, and with a world-class client portfolio, VGV has the momentum and capability to impact companies – like [Google](https://verygood.ventures/success-stories/flutter-news-toolkit/), [Dow Jones](https://verygood.ventures/success-stories/marketwatch-stock-market-game/), [Nav](https://verygood.ventures/success-stories/nav-fintech-flutter-app/), and [V1 Sports](https://verygood.ventures/success-stories/v1-sports/) – on a large scale. A deep focus on technology delivery, the ability to develop for any platform due to Flutter’s strengths, and top tier design and product capability, means VGV is an important strategic consulting partner for its clients. CreateThrive has followed a similar trajectory to VGV. Just like us, they were early adopters of Flutter in 2018, and have been building with it ever since. We’ve gotten to know them through the Flutter community and through direct partnership, and they’ve proven their impact through a wide range of client projects. ## A Big Moment for Flutter and Expansion of Our Capabilities  Flutter continues to gain momentum, with over 1 million Flutter apps published to date. According to Google’s [Flutter 2023 Strategy document](https://docs.google.com/document/u/1/d/e/2PACX-1vRknZ4Jkc-pWSMsDDyKwMrry7k2BSL_I94JCCQrg8FiHuy4fcypkgIVFbQVKPmzDQHfd20uZf2rFiXP/pub), Flutter is picking up market share from other cross-platform frameworks. And, demand for Flutter continues to accelerate. We’ve been at the forefront of Flutter’s success by working with companies to adopt the new technology and create powerful digital experiences for any screen. This acquisition means we’ll have the scale and capabilities to move faster than before to meet new demands of companies adopting Flutter to build innovative apps for mobile, web, desktop and embedded devices. Working with VGV means clients will be getting access to the world’s largest and most experienced Flutter consulting team. ![VGV team announcing the CreateThrive acquisition](/assets/images/blog/vgv-acquires-createthrive/body-0.jpeg) *Announcing the news to the CreateThrive team.* Adding CreateThrive to VGV represents this commitment, with a focus on building the world’s largest and strongest Flutter team with a rigorous commitment to production-grade quality and the deepest full service capabilities to help companies launch and scale their development projects across all platforms. We’re combining two teams of exceptional Flutter talent with compatible cultures focused on doing the best work. Having partnered together in the past on various client projects and collaborating in the Flutter community, the idea of an acquisition came naturally. With many shared cultural values and a ubiquitous sense of optimism, the integration of our two teams is a natural fit. ![Tomas Piaggio and David DeRemer together after the acquisition](/assets/images/blog/vgv-acquires-createthrive/body-1.jpeg) *Tomas Piaggio and David DeRemer* Together, VGV and CT will have the largest Flutter team in the world as well as the ability to meaningfully scale as a team.  In addition to Flutter, CreateThrive brings an expansion of capabilities to help our clients, including web, backend, and QA engineering expertise as well design and delivery teams to the VGV ecosystem. These new capabilities will help us to better serve a wide variety of our clients needs beyond our core Flutter work. As trusted strategic partners, we’re now capable of designing, development, and managing complex applications for any client situation. While we are excited about this announcement for what it means for our two teams, we also are excited for the momentum this signals for the broader Flutter community. Flutter has truly emerged as the best solution for multi-platform development, and a thriving community and business ecosystem has sprung up around it. We intend to use the combined passion of our team to help Flutter continue to grow long into the future. ## What’s next with this one global team One thing that has always made VGV stand out is the culture of our team. Our people have always been enthusiastic, passionate, supportive, and engaged with the Flutter community. The spirit of the CreateThrive team is closely matched to VGVs, so we anticipate continuing to proliferate our positive culture. We’re building a special place, open and welcoming for all to do the best work of their careers. One of our values at VGV is: **We are one team, both internally and with our clients.** With this acquisition, we’re perhaps most excited about the fact that Very Good Ventures and CreateThrive will now be one team. The truth is, CreateThrive has been a longtime partner of VGV. This acquisition means we’ll keep working together, but now able to tackle bigger and more important challenges for clients and for the community. In our [recent case study](https://verygood.ventures/success-stories/nav-fintech-flutter-app/), Zain Khan, Head of Product at Nav notes “There was a genuine trust between our team and the VGV team. I speak for everybody; we did not think there was a VGV and a NAV team. There was one team.”  We are eager to bring the power of our expanded team to all of our client work. ![The CreateThrive team group photo](/assets/images/blog/vgv-acquires-createthrive/body-2.jpeg) *The CreateThrive team.* ### [**See our press release to read more about Very Good Ventures acquiring CreateThrive.**](https://www.prnewswire.com/news-releases/very-good-ventures-acquisition-of-createthrive-underscores-flutter-momentum-and-enterprise-adoption-301865279.html) ### Very Good Ventures will be at Fluttercon Berlin 2023 Source: https://verygood.ventures/blog/very-good-ventures-will-be-at-fluttercon-berlin-2023/ The highly-anticipated [Fluttercon](https://fluttercon.dev/) is scheduled to take place in just a few weeks. Taking place July 5-7 in the vibrant city of Berlin, the conference promises to be a hub of innovation, collaboration, and learning, with developers gathering to showcase the latest in Flutter. The conference aims to bring together Flutter enthusiasts, developers, and industry experts for an immersive and inspiring event, which is co-located with [droidcon Berlin](https://berlin.droidcon.com/). Attendees will have the opportunity to explore the latest advancements, best practices, and real-world applications of Flutter. The conference will feature keynote speeches, technical sessions, workshops, and networking opportunities. The event kicks off with [keynotes](https://fluttercon.dev/speakers/) by some influential figures from the Flutter community. They will share insights, experiences, and visions for the future of Flutter, providing attendees with valuable information about the framework's growth and upcoming features. We are especially excited for the talks, lightning sessions, and workshops delivered by developers from the community. As a silver sponsor of Fluttercon, we will have a dedicated booth at the event, where attendees can meet our team and learn more about VGV’s contributions to the Flutter ecosystem. Come by and say hi! ![VGV speaker lineup and booth announcement for Fluttercon Berlin 2023](/assets/images/blog/very-good-ventures-will-be-at-fluttercon-berlin-2023/body-0.png) ## VGV talks at Fluttercon We are excited that several of our VGV teammates will be speaking at the conference. David DeRemer, our Founder and CEO will be delivering a keynote on day three of the conference about the cultural impact of Flutter implementation. David says: "I'm excited to witness the breadth and depth of the Flutter community. The sheer number and diverse range of talks is truly remarkable, showcasing the remarkable progress Flutter has made. However, above all, I'm eagerly looking forward to connecting with as many individuals as possible because, in essence, the Flutter community thrives on its amazing people." Tomas Piaggio will be exploring Firebase's capabilities, and how to leverage them to create robust and scalable apps using Flutter. He'll also cover important security considerations to protect user data and provide an optimal user experience. Tomas shares, “I am most excited about the possibility of connecting with hundreds of developers from all around the world, especially my new VGV teammates whom I haven't met in person yet!” Renan Araujo will be speaking about the transformative potential of Fragment shaders in UI design. Renan expressed his anticipation, sharing, "I am excited about the unprecedented significance that this conference holds for the Flutter community. We have never seen so many speakers and attendees at a Flutter conference before. The talks and workshops focused on gamedev will also complement the theme of my talk." Óscar Martín will be covering the significance of writing functional and scalable code. Óscar eagerly shared his excitement for Fluttercon: “Super excited to meet in person for the first time a lot of friends from Flutter community, it is gonna be the biggest Flutter event I have ever been to!” Jochum Van Der Ploeg will be talking about the use of Fluttium, an end-to-end user testing tool for Flutter. Jochum shares, “I am excited to not only show how accessibility and end-to-end testing can work together, but also to finally meet the people that I have interacted with over the years, as well as those I have already met.” Erick Zanardo will be exhibiting the power of the Flame engine for game development by creating an exciting space shooter game using Flutter and Flame. He expressed his anticipation for the conference, saying, "With this being my first international Flutter conference, I have numerous reasons to be excited. However, at the top of my list is the opportunity to finally meet the incredible people within our Flutter community. I can't wait to connect with them and be a part of this amazing event!" ## What's next Fluttercon is set to be an exciting experience for all things Flutter! We can’t wait to see what new developments will be unveiled at the event, and how they will shape the future of Flutter and app development. For those who can’t attend the conference in person, talks will be [recorded](https://fluttercon.dev/faqs/) and uploaded to the droidcon website after the event. ### Erick Zanardo talks about building I/O FLIP on Observable Flutter: Key takeaways Source: https://verygood.ventures/blog/erick-zanardo-talks-about-building-i-o-flip-on-observable-flutter-key-takeaways/ Earlier this month, Erick Zanardo joined Craig Labenz on the Observable Flutter livestream to talk about [I/O FLIP](https://flip.withgoogle.com/#/), the AI-designed take on a classic card game demoed on-stage at Google I/O. As the tech lead on the project, Erick took us behind the scenes on the origins of the game, how we built it in collaboration with Google, and some of the key learnings from the project. Here are some of the things they talked about: ## Origins of I/O FLIP At VGV, we’ve worked with Google on a few demos for Google I/O, such as [I/O Photo Booth in 2021](https://github.com/flutter/photobooth) and [I/O Pinball](https://pinball.flutter.dev/) last year. This year, we teamed up with Google to build for I/O FLIP, a digital card game designed by AI. Erick recounts that the early days of the project were spent on brainstorming. The only requirement the team had was to incorporate AI and machine learning into the game — everything else was up in the air. Some of the early ideas were other games, such as an RPG game with AI-generated quests and assets. Ultimately, the team settled on a card game because it had simple game mechanics and because it wouldn’t take long to play a round, it could encourage players to keep playing. ## Flutter brings UI of the game to life Since this was a Flutter showcase project, Flutter was used for the frontend of I/O FLIP. The Casual Games Toolkit provided a great foundation for both the background music and audio effects within the game via the AudioController class.  Erick took us behind the scenes for the code of the rainbow foil shader that is applied to special cards in I/O FLIP. Craig provided some more context around two key shader widgets: ShaderBuilder, which compiles the shader, and the AnimatedSampler, which renders an element in your app and then applies the shader to that desired element in your app (Craig also teased some shader resources to come later this month 👀).  ## Building a server-authoritative Game: Dart Frog 🤝 Firestore The team decided to use Dart Frog to help build the backend of this project: Erick says that they didn’t want to put all of gaming logic on the client because if they did, players could try to take a look an the servers, try to find ways to exploit the game, and take advantage of their opponent. I/O FLIP is a server-authoritative game, which means it is defined by the backend. All of the logic about which player wins a round, and which player wins the match would be defined by logic on the backend. Erick recalls that the team considered using Firebase functions to handle these server operations, but ultimately decided to use Dart Frog to be able to share code between the server and the client.  ## How long did it take to build I/O FLIP? Erick tells us that I/O FLIP took around three months to go from ideation to final product. There were many phases within where the team explored ideas and created proofs of concept for different approaches. Once they landed on the final idea of the card game, it took about 6 weeks to build. ### Check out the [**full livestream**](https://youtu.be/UMUDLU6sh2s) for even more technical details, including more on the AI integrations and scaling with Firebase. Or, play [**I/O FLIP**](https://flip.withgoogle.com/#/). ### How It’s Made: I/O FLIP Source: https://verygood.ventures/blog/how-its-made-i-o-flip/ [I/O FLIP](http://flip.withgoogle.com/) is an AI-designed card game powered by Google and built in Flutter. It unites a handful of Google products and techniques. This includes [Flutter](https://flutter.dev/), [Firebase](https://firebase.google.com/), generative AI tools, Dreambooth on Muse, [PaLM API](https://developers.googleblog.com/2023/03/announcing-palm-api-and-makersuite.html), and [MakerSuite](https://developers.googleblog.com/2023/03/announcing-palm-api-and-makersuite.html). Players can play multiple matches and share cards from their game to social media. To play the game, visit [http://flip.withgoogle.com](http://flip.withgoogle.com/). To start and to generate your deck, answer a few prompts. To make your hand and join a match, select three cards. You’ll use these cards to play your opponent for three rounds in which the highest card wins the round. The fun doesn’t have to end when the match does. Keep playing matches to increase your win streak and try to make the leaderboard! Let’s dig into the technical details of we used Flutter and Firebase to build I/O FLIP. ## A card game created with Flutter The I/O FLIP user interface, animations, hologram effects, and backend, were all built with Flutter and Dart. To start, we leveraged the [Flutter Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit). We used the out-of-the-box audio functionality, game lifecycle, and app navigation with [go_router](https://pub.dev/packages/go_router) as a foundation. From there, we built out the game logic and UI. I/O FLIP is a [responsive web app](https://docs.flutter.dev/ui/layout/adaptive-responsive). It resizes its UI based on the players’ screen. The app also handles input based on the device the player uses. It receives touch input when accessed on a mobile device or tablet and mouse input on desktop. Playing cards are a key element of I/O FLIP. Thousands of cards could end up in a player’s deck. Each card displays a Google mascot, element, and power value. The elements come into play when they go up against each other. For example, when someone plays a fire card and their opponent plays a water card, the water card receives a penalty of 10 points. We also used records, a [new feature of Dart 3](https://medium.com/dartlang/dart-3-alpha-f1458fb9d232), to render a frame based on the card element. ```dart … (String, SvgPicture) _mapSuitNameToAssets() { switch (suitName) { case 'fire': if (isRare) { return ( Assets.images.cardFrames.holos.cardFire.keyName, Assets.images.suits.card.fire.svg(), ); } return ( Assets.images.cardFrames.cardFire.keyName, Assets.images.suits.card.fire.svg(), ); … ``` ## Cards created with AI-generated images and descriptions ![Example I/O FLIP cards featuring Dash, Sparky, Android, and Dino against various backdrops with props.](/assets/images/blog/how-its-made-i-o-flip/body-0.png) *Example I/O FLIP cards* Each card in I/O FLIP is unique because it contains an AI-generated image and description. At the beginning of the game, players answer two prompts. These prompts help populate a deck of 12 cards that feature images and descriptions that AI models pre-generated. Google team used two technologies to pre-generate the images: [Muse](https://muse-model.github.io/), a Transformer text-to-image model, and [DreamBooth](https://dreambooth.github.io/). DreamBooth enables synthesizing a subject in diverse scenes, poses, views, and lighting conditions. Each card contains one of four Google mascots: [Dash](https://docs.flutter.dev/dash), Sparky, [Android](https://source.android.com/docs/setup/about/brands#robot-android), or Dino, and a location. The mascot also has an item that designates their team. The “team type” prompt at the beginning of the game seed the creation of this item. For example, selecting “wizard” may result in a character with a wizard hat, magic wand, or something else wizardly! The Google team used the [PaLM API](https://developers.googleblog.com/2023/03/announcing-palm-api-and-makersuite.html) to pre-generate card descriptions. The PaLM API accesses Google’s large language models. The prompts at the beginning of the game included the team type and power type for their team. Let’s say you selected team “Wizard” and power “Magnetism”. When your cards generate, one card’s description includes context to the generated image including the character’s special powers. For example, “Dash the Wizard lives in a castle with his pet dragon. He loves to cast spells and make people laugh.” Flutter composes the cards via the **GameCard** widget. This widget receives the card’s data: name, description, image, and power. Once it creates the card, it applies a border to depict the card’s suit element. If a card is a special card, Flutter applies a foil shader effect. To learn more about how the Generative AI aspects of the game were created, check out [this Google Developer blog post](https://www.google.com/url?q=https://developers.googleblog.com/2023/05/how-its-made-io-flip-adds-twist-to.html&sa=D&source=docs&ust=1684366112610573&usg=AOvVaw1fj9CV5BxCPvxUEGfcZF39). ## Shaders add foil effect to special cards ![Holographic foil shader effect animating on a special I/O FLIP card](/assets/images/blog/how-its-made-i-o-flip/body-1.gif) Flutter supports [fragment shaders](https://docs.flutter.dev/ui/advanced/shaders). To generate these per-pixel visual effects, Flutter runs the OpenGL Shading Language (GLSL) on the device’s GPU. Trading card collectors might remember the feeling of opening a pack to find a special edition card with a shiny, holographic foil. I/O FLIP includes special foil cards, too. They are valued at 100 points. Regular cards have a point values ranging from 10 to 99. We used custom shaders to render the foil effect. We implemented the foil shader in the **foil.frag** file. The effect uses the following constants: - **STRENGTH**. This mixes the original pixel color with the color of the foil effect. This ranges from 0.0 for no effect to 1.0 for full effect. - **SATURATION**. This sets the intensity of a color. This ranges from 0.0 for grayscale, or absence of color, to 1.0 for full color or absence of black. - **LIGHTNESS**. This ranges from 0.0 for full black to 1.0 for full white. The shader also receives input through [uniforms](https://docs.flutter.dev/ui/advanced/shaders#uniforms), in this case **resolution** and **offset**. The uniform **sampler2d** called **tSource** represents the card image to which the shader is applied. The final result is a card with a foil effect. ```glsl vec4 rainbowEffect(vec2 uv) { vec4 srcColor = texture(tSource, uv); float hue = uv.x / (1.75 + abs(offset.x)) + offset.x / 3.0; float lightness = LIGHTNESS + 0.25 * (0.5 + offset.y * (0.5 - uv.y)); hue = fract(hue); float c = (1.0 - abs(2.0 * lightness - 1.0)) * SATURATION; float x = c * (1.0 - abs(mod(hue / (1.0 / 6.0), 2.0) - 1.0)); float m = LIGHTNESS - c / 2.0; vec3 rainbowPrime; if (hue < 1.0 / 6.0) { rainbowPrime = vec3(c, x, 0.0); } else if (hue < 1.0 / 3.0) { rainbowPrime = vec3(x, c, 0.0); } else if (hue < 0.5) { rainbowPrime = vec3(0.0, c, x); } else if (hue < 2.0 / 3.0) { rainbowPrime = vec3(0.0, x, c); } else if (hue < 5.0 / 6.0) { rainbowPrime = vec3(x, 0.0, c); } else { rainbowPrime = vec3(c, 0.0, x); } vec3 rainbow = rainbowPrime + m; return mix(srcColor, vec4(rainbow, srcColor.a), STRENGTH); } ``` ## Firebase enables game hosting and sharing to social media All of the gameplay communication happens in real time via [Firebase’s Cloud Firestore](https://firebase.google.com/docs/firestore). Firebase Storage hosts the card assets that generate players’ card decks. We also use Cloud Firestore to track the “Highest Win Streak” on the leaderboard. When a leaderboard adds new leader, the [**firedart**](https://pub.dev/packages/firedart) package adds it to Firestore. ## Dart Frog enables sharing code between the backend and frontend I/O FLIP needed a backend to prevent cheating. This server-authoritative game logic prevents malicious clients from sending fabricated requests. Dart Frog keeps the game logic, such as the winner of each round, on the backend. It also shares this code between the Flutter frontend and the Firestore backend. Sharing code has a few benefits. It allows us to share logic. For example, if one player wins a round, the game can display a win animation without needing to query Firebase every time. Sharing code also sped development, since the team could write backend and frontend code in the same language: Dart. We deployed the I/O FLIP Dart Frog server to [Cloud Run](https://cloud.google.com/run). This means the server code runs in Google Cloud and can autoscale, so the app can handle many players at once. ```dart FutureOr onRequest(RequestContext context) async { if (context.request.method == HttpMethod.post) { final cardsRepository = context.read(); final promptRepository = context.read(); final body = await context.request.json() as Map; final prompt = Prompt.fromJson(body); if (!await promptRepository.isValidPrompt(prompt)) { return Response(statusCode: HttpStatus.badRequest); } final characterClass = prompt.characterClass; if (characterClass == null) { return Response(statusCode: HttpStatus.badRequest); } final characterPower = prompt.power; if (characterPower == null) { return Response(statusCode: HttpStatus.badRequest); } final cards = await cardsRepository.generateCards( characterClass: characterClass, characterPower: characterPower, ); return Response.json( body: {'cards': cards.map((e) => e.toJson()).toList()}, ); } return Response(statusCode: HttpStatus.methodNotAllowed); } ``` Dart Frog also facilitates sharing on social media. When selecting cards, a player can share an individual card to Twitter or Facebook. When you submit your score to the leaderboard, you can share your hand to Twitter or Facebook. Once the user clicks **Share**, Dart Frog generates a pre-populated post. This post contains text and a link to a webpage with your corresponding hand or card and a button for visitors to play I/O FLIP themselves! ## What’s next I/O FLIP showcases how to combine Flutter and Firebase, along with Google generative AI tools and techniques, in a fun game that people around the world can play. [Play a game](http://flip.withgoogle.com/) and show us your hand, or dig into the [open source code](https://github.com/flutter/io_flip)! ### Google I/O 2023: Spotlight on AI, Flutter Moves Forward & I/O FLIP Takes seoTitle: Source: https://verygood.ventures/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/ There were a lot of exciting announcements made by Google at this year’s highly anticipated [Google I/O](https://io.google/2023/). AI was at the forefront of this year’s conference, with Google showcasing new AI advancements for its products, such as the Google Workspace, Google Photos, and Google Cloud, to name a few. On the developer side, there were several key updates to Flutter that we’re excited about. Read on for what caught our eye from Google I/O! ## Flutter development ecosystem gets an upgrade Google’s app development ecosystem saw some big advancements at I/O this year. Notably, [Flutter version 3.10](https://docs.flutter.dev/release/release-notes/release-notes-3.10.0) was highlighted during the Developer Keynote, with new features such as element embedding, shader improvements, and faster load times. Google also announced a focus on new technologies, particularly WebAssembly, which will enable Flutter to render three times faster in the browser.  ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-0.avif) It’s exciting to see the advancements with WebAssembly (Wasm). Wasm now supports Dart, expanding its advantage of providing native support to the web, and enabling Flutter developers to build beautiful, performant apps. Another interesting highlight from I/O was Wear OS. The popularity of Wear OS has grown significantly since the launch of Wear OS 3, with active devices increasing by 5 times. To aid in the effort of developers creating compelling apps for the wrist, Google introduced the new Watch Face Format, an XML format that simplifies the process of bringing your creative ideas to smartwatches. This will make it even easier for developers to create engaging and innovative watch faces that will enhance the user experience on Wear OS. To get started, check out our [guide to building Wear OS apps with Flutter](https://verygood.ventures/blog/building-wear-os-apps-with-flutter-a-very-good-guide/). Additionally, the most recent Flutter release has incorporated a JNI bridge to Jetpack libraries that are written in Kotlin. This allows developers to incorporate a new Jetpack library directly from Dart without the need for an external plugin. The new feature make it easier to use system APIs without having to use platform channels. [Read more about the major Flutter updates to Flutter in this article.](https://verygood.ventures/blog/building-wear-os-apps-with-flutter-a-very-good-guide/) ## Impeller moves forward Flutter has been rebuilding its graphics rendering architecture to empower developers and designers to deliver smoother graphics. The new engine, Impeller, was introduced on iOS and has now been turned on by default for iOS with the release of Flutter 3.10, significantly enhancing in app performance and graphics. Impeller is built on Vulkan, which uses low-level APIs for faster rendering on the graphics hardware. A preview for Impeller for Android is soon to follow, along with more details about the technical details of Impeller. We are looking forward to experimenting with Impeller on future projects, especially those with complex animations and graphics. ## I/O FLIP, an AI-designed card game built with Flutter and Firebase, takes the stage We were especially excited to see [I/O FLIP](https://flip.withgoogle.com/) take the stage at Google I/O! ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-1.avif) VGV collaborated with Google to build the card game [using several Google products and technologies](https://developers.googleblog.com/2023/05/how-its-made-io-flip-adds-twist-to.html). This game is a great example of incorporating Google’s new generative AI technologies into a fun digital experience. The individual cards each contain images and text generated from AI. The custom character images were generated with DreamBooth on Muse, and their descriptions were written using PaLM API. The game was built in Flutter for the web with Dart Frog and Firebase as the backend. View the [open source code here](https://github.com/flutter/io_flip). I/O in-person attendees even received physical I/O FLIP cards in their swag bags! ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-2.avif) ## Betterment mobile app highlighted at Google I/O We were happy to see the Betterment mobile app being showcased during Google I/O. A few years ago, Betterment, a popular investing platform, decided to transition from their existing native code to Flutter. ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-3.avif) In their [developer story](https://www.youtube.com/watch?v=1J3B-xaoXgw&ab_channel=Flutter), the team highlights how Flutter allowed them to achieve a native-like experience across multiple platforms, including iOS and Android, with a single codebase. They emphasized the ability Flutter gave the team to iterate quickly and efficiently during the development process. Additionally, Betterment demonstrates how the extensive set of customizable UI components and its robust performance contributes to a seamless and responsive user interface. Their use of Flutter demonstrates how the framework can enable businesses to create cross-platform apps, with a focus on user experience and efficiency. We at VGV had the privilege of working with their team on their initial migration to the new framework. [Read more about how we worked with Betterment to shift to Flutter](https://verygood.ventures/success-stories/betterment/). ## VGV at Google I/O This was the first year that some of our teammates attended the event in person.  ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-4.avif) The energy and excitement of Google I/O was real! AI was certainly at the forefront of all of the major announcements, which is indicative of where the tech industry is heading as a whole. It was really cool to see so many new product updates and demos on stage. Of course, I/O FLIP being featured on the big screen was a major highlight! One of our favorite parts was watching Craig and Chloe face off by playing the card game between the keynotes. ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-5.avif) ![__wf_reserved_inherit](/assets/images/blog/google-i-o-2023-spotlight-on-ai-flutter-moves-forward-and-i-o-flip-takes-the-stage/body-6.avif) After the I/O festivities, our team also attended the Flutter x Firebase reception, which was a cool opportunity to meet with others who share our same love of Flutter.  ## Looking Ahead Google I/O 2023 showcased Google’s impressive advancing technologies. With advancements for Flutter, Android, Web, and Firebase, developers can expect to see improved performance and improvements in the developer experience. We’ll keep a close eye on all of the advancements, and especially how AI will shape the future of technology. ### Top Flutter & Dart Packages in 2023 Source: https://verygood.ventures/blog/top-flutter-dart-packages/ Open source packages play a huge role in software development, especially when it comes to building Flutter apps. [Pub.dev](http://pub.dev/) has a growing ecosystem of over 33,000 packages to help you add functionality to your Flutter application. This blog contains a curated list of Flutter and Dart packages that are useful to building apps at VGV, as well as those we use in our personal projects. **Table of Contents:** - [**How to decide which Flutter or Dart package to use in your project**](#id-how-to-decide-which-flutter-or-dart-package-to-use-in-your-project) - [**Top open source packages**](#id-top-open-source-packages) - [**Top VGV packages**](#id-top-vgv-packages) ✨ denotes a package added in this update ## How to decide which Flutter or Dart package to use in your project With thousands of packages to choose from in the Flutter ecosystem, how do you choose which package to use in your project? Here at VGV, we take a few characteristics into account when selecting a package to include in our projects.  Packages that have example code, are well-documented, and have a clear README will be much easier to implement in your own code than packages without. On the more technical side, we recommend looking for the package's test coverage, a well documented changelog, lint rules, and if the package is up to date with the latest version of Flutter. The Flutter Ecosystem Committee also takes much of this into account when they give a package the [Flutter Favorite badge](https://docs.flutter.dev/development/packages-and-plugins/favorites). Here is a list of questions we use to assess packages: **Basics** - Does the package have a clearly defined scope/focus? - Does it have an intuitive API? - Does it have a permissive license (MIT, etc.)? - Does it have an easy to read changelog? - Does it have contributing guidelines? - Is it widely used? We like to look at GitHub stars, dependents, and the popularity score on pub to approximate. Bonus points if it’s a Flutter Favorite! **Quality** - Does it have good test coverage? - Does it have a good pana score? - Does it have lint rules configured? - Does it have continuous integration configured? **Maintenance** - Is it kept up-to-date with the latest stable versions of Flutter/Dart? - Is it well-documented?  - Does it contain information on how to use the package (README, examples, docs site, etc.)? - Does it have API documentation (dart doc)? - Are the maintainers active? - Are the maintainers friendly and collaborative? ## Top Open Source Packages ### alchemist  [Alchemist](https://pub.dev/packages/alchemist) is a package designed to enhance golden testing. This package, developed by Betterment x VGV, contains a declarative API and addresses some pain points of golden test support within the Flutter framework — such as fixing continuous integration test flakes and simplifying injecting a custom theme. Here’s a [blog about how to use alchemist](https://verygood.ventures/blog/alchemist-golden-tests-tutorial/) with your golden tests. ### auto\_size\_text ✨ [Auto_size_text](https://pub.dev/packages/auto_size_text) is an enhanced text widget that automatically sizes the text within an area to fit the desired constraints. These constraints include max size, min size, max lines, or even a preset list of sizes. ### cached\_network\_image ✨ [Cached_network_image](https://pub.dev/packages/cached_network_image) is a great way to load any images that are coming from the network. This library will load an image, as well as show placeholders and loading animations. It will also cache this image so you are not loading the same image twice. ### collection  This package from the Dart team is a must-have if you’re using collections in your codebase. [Collection](https://pub.dev/packages/collection) contains functions to help with equality within elements of a collection, as well as between collections, list operations, priority queue implementation, and wrappers. ### coverage We believe in the [importance of 100% code coverage](https://verygood.ventures/blog/very-good-coverage/), which ensures that each line of code is tested and behaves as expected. [Coverage](https://pub.dev/packages/coverage) is a great Dart package that allows users to collect, manipulate, and format coverage data. With coverage, you can collect coverage from the Dart VM and format that coverage data into [LCOV](http://ltp.sourceforge.net/coverage/lcov.php) or pretty-printed format. ### cloud\_firestore [Cloud_firestore](https://pub.dev/packages/cloud_firestore#cloud-firestore-plugin-for-flutter) is a Flutter plugin that gives users access to the Firebase [Cloud Firestore](https://firebase.google.com/products/firestore) database, a NoSQL cloud database for mobile, web, and server development. This allows your application to read, write, and react to real-time changes in the Firestore database. Be sure to check out the documentation on how you can start [using the cloud_firestore package](https://firebase.flutter.dev/docs/firestore/usage/) in your own projects. ### equatable  [Equatable](https://pub.dev/packages/equatable) is a small but helpful package for comparing Dart objects. This package overloads the == operator and allows you to compare objects without code generation. ### firebase\_auth We can’t recommend cloud\_firestore without also mentioning [firebase_auth](https://pub.dev/packages/firebase_auth). firebase\_auth allows users to access the [Firebase Authentication](https://firebase.google.com/products/auth/) API, enabling authentication through passwords, telephone numbers, and service providers like Twitter, Facebook, and Google. Visit the [Authentication Usage documentation](https://firebase.flutter.dev/docs/auth/usage/) to learn more about integrating firebase\_auth into your projects. ### flame [Flame](https://pub.dev/packages/flame) is a must-have for game development in Flutter. It leverages the Flutter framework to provide everything you need to build performant games. With flame, your Flutter game is only a widget away. Get started with their detailed documentation. ### flame\_bloc If you’re using flame to build a game, you can also leverage the bloc library for managing game state. The [flame_bloc](https://pub.dev/packages/flame_bloc) package makes it easy to access and listen to blocs and cubits. For a tutorial on using flame\_bloc, [check out our blog](https://verygood.ventures/blog/flutter-games-with-bloc-and-flame/). ### flutter\_bloc We prefer to use the [flutter_bloc](https://pub.dev/packages/flutter_bloc) package for state management. A [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites), flutter\_bloc is a simple, predictable, and easily testable solution to state management, which fits in perfectly with the development practices here at VGV. We even wrote an article that takes an in depth look at [why we use flutter_bloc](https://verygood.ventures/blog/why-we-use-flutter-bloc/). ### flow\_builder [Flow_builder](https://pub.dev/packages/flow_builder) is a Flutter package that simplifies the use of navigation stacks following the new Navigator 2.0 API. Simply create a flow state and flow\_builder will be able to generate a new navigation stack based on changes to the flow state. ### flutter\_gen In Flutter, using a string to specify a path to an asset isn’t safe. For example, an assertion error can be thrown if you accidentally mistype the asset path. [flutter_gen](https://pub.dev/packages/flutter_gen) is a code generator for assets, fonts, colors, and more, effectively getting rid of string-based APIs. With flutter\_gen, you can use code generation to generate compile-safe asset references instead of using strings for asset paths. For more information on how to install and use flutter\_gen, be sure to check out the [documentation](https://pub.dev/packages/flutter_gen). ### flutter\_secure\_storage ✨ If you are looking to store sensitive data on a device, [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) may come in handy. This library provides a convenient way to access the platform specific encrypted storage: Keychain for iOS and AES encryption or EncryptedSharedPreferences for Android. ### freezed ✨ [Freezed](https://pub.dev/packages/freezed) allows you to define immutable classes in Dart in a clean and easy way. When annotated on a class, freezed will generate all of the required boilerplate to make the class into a data class with fromJson, toJson, and all other necessary methods. Freezed can be a useful tool, but it should be used intentionally, as it can cause long build times when used in larger projects. ### googleapis The [googleapis](https://pub.dev/packages/googleapis) Dart package provides auto-generated Dart libraries to access the APIs found in the [Google API Client Libraries](https://developers.google.com/api-client-library). Installing this one package gives you access to dozens of available Google APIs, including the Admin SDK, Calendar, Dialogflow, Drive, and more. Be sure to read up on how you can use [Google APIs with Flutter](https://flutter.dev/docs/development/data-and-backend/google-apis). ### hive [Hive](https://pub.dev/packages/hive) is a Dart package that provides developers with an easily accessible NoSQL Database. It is simple, powerful, and intuitive — we appreciate how you can easily store primitives and Dart objects inside hive’s map-like database.  ### in\_app\_review User feedback can be an integral part of the app development process, so we’re including the [in_app_review](https://pub.dev/packages/in_app_review) Flutter package on our list. Developers can prompt users for an app store review within the app. ### mason The [mason](https://pub.dev/packages/mason) Dart package is a key tool in increasing efficiency through automation. With mason, you can create and customize reusable code templates in the form of bricks. Here is an article with tutorials on how to use [mason](https://verygood.ventures/blog/code-generation-with-mason/). ### membrane [Membrane](https://github.com/jerel/membrane) is a helpful package for generating code from Rust to Dart. With an emphasis on performance and strict typing, it’s a reliable way to port code over into Dart. ### mocktail Automated testing is a key component of our development workflow to ensure that our applications perform as expected. One go-to package for writing automated tests is [mocktail](https://pub.dev/packages/mocktail). Mocktail, inspired by [mockito](https://pub.dev/packages/mockito), allows users to stub and verify method behavior by creating mocks without relying on code generation in null safe codebases.  Mocktail also works in tandem with our new [mockingjay](https://pub.dev/packages/mockingjay) package, which allows users to mock, test, and verify navigation behavior. Stay tuned for more about this new package! ### modal\_bottom\_sheet Maintained by our teammate Jaime Blasco, [modal_bottom_sheet](https://pub.dev/packages/modal_bottom_sheet) makes it simple to implement a modal bottom sheet that is both performant and looks at home on any native mobile device. It includes support for scrolling, dragging, and other customizations. ### pana [Pana](https://pub.dev/packages/pana) is a handy Dart package that lets you analyze the health and quality of other packages that you’re using in your application. It evaluates the packages across multiple categories: - Follows Dart conventions - Provides documentation - Supports multiple platforms - Passes static analysis - Supports up-to-date dependencies To get started, simply install pana from pub.dev and specify either a package or a local directory to analyze. ### Plus Plugins ✨ The [Plus Plugins](https://github.com/fluttercommunity/plus_plugins) are a collection of plugins that expose added functionality for things like accessing battery state, sensors, and more. Some of our favorite plugins from the bunch are [connectivity_plus](https://pub.dev/packages/connectivity_plus) which tells you when you have internet access and [share_plus](https://pub.dev/packages/share_plus) which lets you share content using the platform's share dialog. ### purchases\_flutter [Purchases_flutter](https://pub.dev/packages/purchases_flutter) is a helpful package by RevenueCat for implementing in-app purchases with StoreKit and Google Play Billing in your Flutter apps. Get purchase tracking, in-app subscriptions, and analytics all in one place. ### recase [Recase](https://pub.dev/packages/recase) is a small but mighty package to easily change the case of input text into snake\_case, dot.case, path/case, PascalCase, and more. ### url\_launcher True to its name, the [url_launcher](https://pub.dev/packages/url_launcher) Flutter package is a convenient way to launch external URLs from your application with many URL schemes supported. Use this package when you want to rely on the underlying platform to handle opening the URL for you. ## Top VGV packages ### cli\_completion ✨ This [package](https://pub.dev/packages/cli_completion) is a small, but mighty utility that comes in handy when building your own command-line interface. Easily add completion functionality to any Dart CLI. ### dart\_frog ✨ [Dart_frog](https://dart-frog.dev/) is a fast, minimalistic backend framework written entirely in Dart. If you love Dart for frontend development and want to use the same language for backend development, you can try dart\_frog. It only takes a few lines to get started, and it supports hot reload. Check out our [full stack tutorial](https://verygood.ventures/blog/dart-frog-full-stack-tutorial/). ### flame\_behaviors ✨ [Flame_behaviors](https://pub.dev/packages/flame_behaviors) allows you to create games using entities and behaviors. It can also make it easier to scale and test your games. Check out [our blog](https://verygood.ventures/blog/build-games-with-flame-behaviors/) to learn how to create a Pong game with flame\_behaviors. ### formz At VGV, we use the [formz](https://pub.dev/packages/formz) package, which heavily simplifies form representation and validation in an easily understandable format. Simply extend FormzInput and provide the input and error type. ### r13n ✨ [R13n](https://pub.dev/packages/r13n) makes it easy to implement regionalization in your app, allowing you to further personalize apps with a global audience. This package can and should be used in conjunction with l10n. ### very\_good\_analysis Linting tools are great for ensuring high-quality, consistent code by checking for stylistic errors. We created the [very_good_analysis](https://pub.dev/packages/very_good_analysis) package to combine the rules from the (now deprecated) [pedantic package](https://pub.dev/packages/pedantic), as well as some additional options that we use at VGV. Read more about [very_good_analysis](https://verygood.ventures/blog/introducing-very-good-analysis/). ### very\_good\_cli [very_good_cli](https://pub.dev/packages/very_good_cli) is a command line tool that can be used to generate a number of opinionated templates, including: Very Good Core, our opinionated Flutter starter application, Flutter package, Dart package, and federated plugin. We created Very Good CLI to make it easier to start new projects — whether a multi-platform application or Flutter package — and ensure our best practices are implemented out of the box. ### very\_good\_workflows GitHub workflows can help you standardize the steps that are run as part of your CI process — we typically use them on each PR before merging. We created [very_good_workflows](https://github.com/VeryGoodOpenSource/very_good_workflows) to open source the workflows we use to check for opinionated best practices at VGV. [Learn more about customizing workflows in this blog](https://verygood.ventures/blog/configuring-workflows-for-your-flutter-projects/). **While this list isn’t exhaustive, we wanted to showcase the top packages that we consistently use in both our VGV work and personal Flutter projects.** *Thanks to those who contributed to this article: Samantha O’Brien, Noel Jacob, Marcos Sevilla, and Daniel Duan.* ### [**See all of our open source packages here →**](https://github.com/VeryGoodOpenSource) ### Building Wear OS apps with Flutter: A very good guide Source: https://verygood.ventures/blog/building-wear-os-apps-with-flutter-a-very-good-guide/ Ever since running my first Flutter app on an Android device and an iPhone, I can't help but wonder whenever I see a device with a display: *Can this run Flutter?* It is all speculation until one goes deep into a device's specifications to see whether the hardware can handle it. Of all the devices out there, those powered by Android-based operating systems will most likely run a Flutter application without too much hassle. That is precisely the case for Wear OS. After spending some time on experimentation, and finally [shipping a real Wear OS game](https://twitter.com/reNotANumber/status/1614990869685497857), I learned how to make Flutter run on anyone's wrist, which raised a few questions that I had to answer. Questions such as:  - How should a Wear OS application work?  - What about battery Life?  - Do screen sizes and screen shapes matter?  - How should my UI behave?  - And finally, how to ship the app? That is what we are talking about today. Let us begin! ## Understanding Wear OS Just because an app can be executed on different types of screens doesn’t mean that the application should look the same on all of them. This is mainly because, unlike smartphones, physical constraints from a device such as a wearable, can change the app's usability. For example, a watch may want to reflect a certain theme during the day versus during the night. Porting a mobile phone UI directly to a smartwatch without changes will certainly result in a bad user experience. From the usability front, one should consider the following when building apps for smartwatches: ### ☝️One-finger UI: Since a smartwatch is expected to be strapped to the user's arm most of the time, assume that all the input that a screen will receive will be from a single finger. As a result, Wear OS applications should avoid multi-touch gestures unless completely necessary. ### 🤏 Displays can be tiny: As expected, smartwatches have to be fashionable. As a result, screens may vary, but always under a few inches. The Pixel watch is 1.24 inches, while the Galaxy Watch 5 pro is 1.4 inches. This means that every pixel on the screen is even more precious than on smartphones. Some tips when building UI for tiny screens: - Try to make each view self-containing. Use scrollable views only when necessary and expected - Be extra careful with the clickable areas of buttons ### 🏠 Displays can be weirdly shaped: ![Smartwatch with a round display showing varied screen shapes on Wear OS](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-0.jpeg) *Photo by Triyansh Gill on Unsplash* The world of Android smartwatches is full of bespoke screens. In the case of Wear OS, one has to be prepared for either square or circular screens. It is essential to notice that devices like the Pixel Watch have seamless dark bezels on OLED screens. This makes dark backgrounds or [vignettes](https://en.wikipedia.org/wiki/Vignetting) good design options. ### ⚙️Inputs may vary: Wear OS watches have three main types of hardware input:  - Touch screen - Physical buttons - Rotary input  Apart from the touch screen, these inputs are not uniform among the different models of watches and can even be absent in some cases. The rotary input is used from interactions such as scroll. On watches like the Samsung Galaxy Watch 4 Classic, it takes the form of a rotating bezel. On Watches like the [Pixel Watch](https://store.google.com/product/google_pixel_watch_specs), it takes the form of a side crown that works as a sole hardware button. ![Three Wear OS watches with different input types: rotating bezel, crown, and buttons](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-1.png) *Example of different watches with different hardware inputs. The one to the left has hardware buttons and a rotating bezel; the one in the middle has a rotating crown and no buttons; the one on the right has no physical rotary input but two physical buttons. Photos by Me, Triyansh Gill and Shawn Rain on Unsplash.* ### 🔋Spare the battery Wear OS devices are quite powerful for their size, but with great power comes severe battery consumption. Nobody likes seeing their watch dying quickly because a specific application drains all of its energy. Be mindful of how much CPU power your app requires. Go easy on animations and take advantage of dark UIs on OLED-based devices. ### ⌚️Plan if your app will be standalone Wear OS apps can run on smartwatch devices without needing a companion smartphone app. Those are called **standalone apps,** and this is the type of app we will be building in this tutorial. [Another consideration is whether you will support Wear OS 2 and earlier devices; this documentation](https://www.androidauthority.com/wear-os-3-vs-wear-os-2-2747606/) helps you plan if your Wear OS app will or won't be a standalone app. ### Keep on reading The best place to know about Wear OS is its documentation; here are some useful links on the aspect of usability and anatomy of Wear OS applications: - [Principles of Wear OS development](https://developer.android.com/training/wearables/principles) - [Wear OS versus Mobile Development](https://developer.android.com/training/wearables/wear-v-mobile) ## The state of Wear OS support on Flutter There are a few things to expect when building a Wear OS app. Even though people have written and deployed Wear OS apps made with Flutter, when targeting smartwatches: **You should expect the following:** - Most (if not all) efforts on this front are community-led: plugins, examples, and articles — like this very one. - Most features that are available for Android should work on Wear OS, including [flavors](https://docs.flutter.dev/deployment/flavors) and most [plugins](https://pub.dev/packages?q=platform%3Aandroid+plugin&sort=popularity). - Native APIs are reachable via method calls or FFIs, like [Health Services](https://developer.android.com/training/wearables/health-services). **You should not expect the following:** - First-party efforts such as  official smartwatch support from Flutter, is [listed as a non-goal](https://github.com/flutter/flutter/wiki/Roadmap#non-goals). - Wear OS-specific features to be baked into the framework, for example, **Platform.isWear**. From the Framework point of view, a Wear OS app **is** an Android app. - An app to have different runners for Wear OS and Android. Wear OS code will go under the **android** directory; there is no support for something in a **wearos** directory with a specific platform code. **💡 What about building apps for the Apple Watch? More on that at the end of this article.** ## Building a Wear OS app with Flutter ![Wear OS smartwatch running a Flutter counter app demo](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-2.jpeg) Now that we know what we should consider when building any app attached to people's wrists, let's learn how to use Flutter to do it. **💡 Before starting, you may want to prepare a device to debug your application. You can read about** [**how to create a watch emulator**](https://developer.android.com/training/wearables/get-started/creating) **or** [**how to debug it on a physical device**](https://developer.android.com/training/wearables/get-started/debugging#enable-dev-options)**.** It is important to notice that any Wear OS app is essentially just an Android app. So, to run Flutter on a Wear OS device (or emulator), we need to adjust some files under the **android** directory. Besides that, on the Flutter side of things, we must adapt the UI for the small screens and get some useful information from the hardware. Now that the objectives are clear, let us build something!  In this tutorial, we will build a counter app (just like [very_good_core](https://github.com/VeryGoodOpenSource/very_good_core)) that: - Has a basic dark UI (to save some energy on OLED devices) - Adapts its colors for when the watch goes on ambient mode - The user can use the rotary input to interact with the app's functionality **💡 We have created an** [**example app**](https://github.com/VeryGoodOpenSource/very_good_wear_app) **with all these bullet points addressed, so you can jump ahead to see how it works. We also plan to make a template out of this and add it to the** [**Very Good CLI**](https://cli.vgv.dev/)**, so stay tuned. 👀** Okay then, so let's get started by creating the app using [very_good_cli](https://cli.vgv.dev/): ```shell very_good create flutter_app my_appcd my_app ``` ### Enabling watch support After that, the order of business is to remove the unnecessary files. Since we are making a standalone Wear OS-only app, we will only need runners for Android. Remove the **macos**, **ios**, **web**, **linux** and **windows** folders. After that, we should add some useful plugins that we will use further down in this tutorial. Those are the **wear** and the **wearable\_rotary** packages: ```shell flutter pub add wear wearable_rotary ``` Now, to make the android runner able to run on Android devices, add the following line to the **AndroidManifest.xml** file: ```html package="com.example.veryGoodCore">+ ... ``` Also, consider adding this metadata under the **application** tag to mark the app as standalone (this will be important when deploying in the store): ```html android:name="com.google.android.wearable.standalone" android:value="true" /> ``` Another file that should be adjusted is the **MainActivity.kt file**. This first change tells the OS to render the app without a background. This makes the app appears round on devices with rounded screens: ```kotlin import android.os.Bundle import androidx.annotation.NonNull import android.view.MotionEvent ... class MainActivity: FlutterActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { intent.putExtra("background_mode", "transparent") super.onCreate(savedInstanceState) } } ``` Later on, we will be using the **wearable\_rotary** plugin. This means we have to add the following callback to the main activity: ```kotlin import com.samsung.wearable_rotary.WearableRotaryPlugin import android.view.MotionEvent ... class MainActivity: FlutterActivity() { ... override fun onGenericMotionEvent(event: MotionEvent?): Boolean { return when { WearableRotaryPlugin.onGenericMotionEvent(event) -> true else -> super.onGenericMotionEvent(event) } } } ``` Last but not least, let us update the Android SDK version constraints to match the added plugins, which, at the time of writing this article, was on minimum API level 25. Regardless of that, I strongly recommend setting the minimum SDK version to 30, which includes Wear OS 3 and above only. See [why avoid Wear OS 2 and below.](https://www.androidauthority.com/wear-os-3-vs-wear-os-2-2747606/) To change the Android SDK constraints, change the **build.gradle** file: ```groovy ... defaultConfig { minSdkVersion 30 targetSdkVersion 32... ``` After this, you can run the app on an emulator or device. ![Flutter counter app with squished UI on a round Wear OS watch emulator](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-3.jpeg) You will notice one thing about this: the UI looks squished. That is not very good! This happens because the current UI for the counter app was meant for smartphone screens, not watches. So let's adapt that. ### Adapting to watch support We can use a few API resources to make Material apps fit those tiny screens, but it mostly comes down to good practices and being mindful of what should be on the screen at a time. From the Material front, it could be a good start to set the visual density to compact: ```dart ... @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( visualDensity: VisualDensity.compact, ... ``` This automatically sets up widgets like buttons and menus to have fewer pixels for padding and general spacing. I recommend using Material 3 since it is more visually compatible with most of the Wear OS 3’s system UI. Also, I advise using dark backgrounds. Since most watches have dark bezels on OLED screens, this will help save some battery and will blend nicely on devices like the Pixel Watch. ```dart ... @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( visualDensity: VisualDensity.compact, useMaterial3: true, // use material 3 colorScheme: const ColorScheme.dark( // dark colorscheme primary: Colors.white24, onBackground: Colors.white10, onSurface: Colors.white10, ), ... ``` In our example, we also need to make some adjustments in the **CounterPage** widget, which we can change from this: ```dart @override Widget build(BuildContext context) { final l10n = context.l10n; return Scaffold( appBar: AppBar(title: Text(l10n.counterAppBarTitle)), body: const Center(child: CounterText()), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read().increment(), child: const Icon(Icons.add), ), const SizedBox(height: 8), FloatingActionButton( onPressed: () => context.read().decrement(), child: const Icon(Icons.remove), ), ], ), ); } ``` To something like this: ```dart @override Widget build(BuildContext context) { final l10n = context.l10n; return Scaffold( body: SizedBox.expand( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () => context.read().increment(), child: const Icon(Icons.add), ), const SizedBox(height: 10), Text(l10n.counterAppBarTitle), const CounterText(), const SizedBox(height: 10), ElevatedButton( onPressed: () => context.read().decrement(), child: const Icon(Icons.remove), ), ], ), ), ); } ``` We swapped the floating action buttons for elevated buttons and centered everything in a column. Now the app should look like this: ![Adapted Flutter counter app with centered layout on a Wear OS watch](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-4.jpeg) ### Taking advantage of watch support It is looking good so far, but some cool hardware resources can further enrich the user experience. Despite all the sensors one can access through some plugins, we will focus today on two awesome plugins made by the community. One is the [**wear**](https://pub.dev/packages/wear) plugin, maintained by the Flutter Community. This will give us access to the state of the ambient mode so that we can adapt the app appearance on low power mode. The other is [**wearable_rotary**](https://pub.dev/packages/wearable_rotary), created by Tizen, which includes Wear OS support. This allows our Flutter code to receive events from the rotary inputs, such as the rotating bezel on Samsung Watches and the rotating crown on the Pixel Watch. #### Adapting to ambient mode When the always-on display is activated either in the watch’s settings or via the **WAKE\_LOCK** android [permission](https://developer.android.com/training/wearables/views/always-on#ConfigureProject), the system may go into a “low power” mode while the application runs. This is called ambient mode. We can use **wear**'s widget **AmbientMode** to listen for when this mode is toggled on or off, and then adapt the Material colors accordingly. The following code makes the app go monochromatic when in ambient mode: ```dart import 'package:wear/wear.dart'; class App extends StatelessWidget { ... @override Widget build(BuildContext context) { // Add ambient mode support return AmbientMode( child: const CounterPage(), builder: (context, mode, child) { return MaterialApp( home: child, theme: ThemeData( useMaterial3: true, visualDensity: VisualDensity.compact, colorScheme: mode == WearMode.active ? const ColorScheme.dark( primary: Color(0xFF00B5FF), ) : const ColorScheme.dark( primary: Colors.white24, onBackground: Colors.white10, onSurface: Colors.white10, ), ... ``` Example of the app going into ambient mode: ![Flutter counter app switching to monochromatic ambient mode on Wear OS](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-5.jpeg) Additionally, this plugin allows us to get meta-information about the ambient mode via the [**AmbientDetails**](https://pub.dev/documentation/wear/latest/wear/AmbientDetails-class.html) class. [It is also possible to detect the shape of the screen.](https://pub.dev/documentation/wear/latest/wear/WatchShape-class.html) #### Listening to rotary input A nice feature for our little counter app would be to allow the user to increase and decrease the counting via the rotating bezel/crown. For that, we can listen to the **rotaryEvents** provided by the plugin and set the counter state from there: ```dart ... class _CounterViewState extends State { late final StreamSubscription rotarySubscription; @override void initState() { super.initState(); rotarySubscription = widget.rotaryEvents.listen(handleRotaryEvent); } @override void dispose() { rotarySubscription.cancel(); super.dispose(); } void handleRotaryEvent(RotaryEvent event) { final cubit = context.read(); if (event.direction == RotaryDirection.clockwise) { cubit.increment(); } else { cubit.decrement(); } } ... ``` Result:  ### Deploying to the app store So far, so good, so easy. We have a Flutter app that looks and feels like it is made for smartwatches. But how do we release this so the world can download it directly onto their watches? As expected, Wear OS apps are distributed via the Google Play Store (Samsung devices may use the Galaxy Store too). There are some specific parts that need special attention to avoid a version rejection. Those are: - Standalone apps must mention Wear OS on their pages, in the description, and in the title. - There must be screenshots for multiple screen sizes, resolutions, and shapes. It is encouraged to produce them on emulators. - Wear OS screenshots are far more restricted than apps for smartphones . Screenshots should contain the app interface and the app interface only. Don't include photos of devices. The first published version of the app will go through two approval phases. The first one should be just like any Android app. See the official [docs on deploying a Flutter app in the Play Store.](https://docs.flutter.dev/deployment/android) After that version is approved, the app should be visible in the store, but people still won't be able to install them on Wear OS devices. Wear OS apps require a special format so they can be installed on wearable devices. For that: - Open the page of your app in the [Play Store console](https://play.google.com/console/about/releasesoverview/). - Go to Advanced Settings in the sidebar. - Navigate to the Form factors tab and add Wear OS. That will create a step-by-step form so you can provide additional Wear OS-specific information that appears on your store's page. ![Google Play Console form factors tab showing Wear OS setup](/assets/images/blog/building-wear-os-apps-with-flutter-a-very-good-guide/body-6.png) After finishing that form, another review process will start to see if the given app is fit to run on people's watches. Make sure that the aforementioned requirements are met. After the second approval step, the application should appear in the store. Congratulations, now you can brag around that you are a smartwatch developer 🥳 {% embed url="https://media.giphy.com/media/DhstvI3zZ598Nb1rFf/giphy-downsized-large.gif" title="Video" %} But hold on! Time for the not so good news because we need to talk about the… ## Apple Watch support Unlike Wear OS, watchOS (Apple’s watch operating system; confusing names, I know) is not just a smartphone OS with a different logo and a *few* bespoke APIs (ok, it is *almost* that). [WatchKit](https://developer.apple.com/documentation/watchkit/) changes the game more than its Android counterpart. [In this Flutter issue](https://github.com/flutter/flutter/issues/28901), [Jenn Magder](https://github.com/jmagman) lists a set of procedures that must be done in the framework and engine for sound support for this type of device. As far as we know, running Flutter and Apple Watches is not as straightforward as it is for Wear OS. We decided not to cover the Apple Watch in this tutorial since this is still unknown territory. But if there is one thing about Flutter that time has proved again and again, is that: **if it has a screen, it will run Flutter, eventually.** ### [**See the Very Good Wear App source code on GitHub →**](https://github.com/VeryGoodOpenSource/very_good_wear_app) ### VGV approach to application architecture [resource] Source: https://verygood.ventures/blog/vgv-approach-to-application-architecture/ ![VGV layered application architecture whitepaper cover](/assets/images/blog/vgv-approach-to-application-architecture/body-0.png) ## [**Download our whitepaper**](https://verygood.ventures/whitepaper/building-enterprise-apps-with-flutter/) to learn more about the VGV approach to development. ### Very Good App Development Can Boost Digital Customer Experience: Here's How Source: https://verygood.ventures/blog/very-good-app-development-boosts-digital-customer-experience-heres-how/ Businesses of all kinds are now technology companies. From services companies to manufacturers and everything in between, brands are getting into mobile app development to deliver better digital connections with their customers.  Any brand that really wants to compete today must ensure it's putting its best foot forward in encouraging digital customer engagement and creating experiences that are rewarding, enjoyable, and encourage brand loyalty, keeping consumers coming back for more. ## The Importance of Great Digital Customer Experience In a world where digital CX and overall CX are practically synonymous, providing great digital interactions impacts the bottom line. Consumers are reaching for their smartphones as their first choice when it's time to make a purchase, look up information, or interact with a brand. The only question is: **Will they like this process or be disappointed by a brand's offerings?** McKinsey & Company reported that since numerous companies in the services sector don't focus on digital CX, the ones that stand out from the pack with good digital offerings stand to [increase overall customer satisfaction by 15-20%](https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/service-industries-can-fuel-growth-by-making-digital-customer-experiences-a-priority). Banking is a prime example of a field where digital CX matters acutely. McKinsey market research revealed that 40% of consumers resolve almost all their banking service queries through online means, whether that means a mobile app, a web app, email, or chat. Forbes contributor Gary Drenik [added some context](https://www.forbes.com/sites/garydrenik/2022/08/16/upgrading-the-digital-customer-experience-with-a-modern-software-team-heres-how-to-do-it-well/?sh=673a7f455fd9) to the overwhelming use of smartphones for banking, citing recent Prosper Insights & Analytics market research which found consumers across age ranges are doing their banking on mobile devices. Even among baby boomers, who didn't grow up with any sort of advanced computers, 42% of consumers bank on their phones. A bank’s role in safeguarding customers' money means it can't afford to provide a negative customer journey or experience. A bank whose digital portals are frustrating or confusing to use, or one that doesn't provide a sense of security, can cause major customer dissatisfaction. This need for secure, reliable digital CX even goes beyond banking to touch industries of all kinds. These digital experiences need to include mobile devices, and not as secondary considerations. HubSpot explained the specific importance of mobile digital CX: Since it's easy for consumers to [try out companies' mobile app or website offerings](https://blog.hubspot.com/service/digital-customer-experience), they can compare various brands before ever buying their products. Dissatisfaction with a mobile app may lead a customer to abandon a company and gravitate to a competitor that offers better digital CX. ## Digital Customer Experience: State of the Industry When it comes to actively creating better digital customer experiences, companies' progress, maturity, and results will naturally vary. Some organizations have come out of the gate with business models centered around mobile apps, while others have been slower and more circumspect in their efforts to complete digital transformation and deliver useful digital touchpoints. It should be noted that the division between newer companies and legacy businesses is not one-dimensional, with young firms being more digitally enabled and older firms falling behind. For instance, a firm's mobile app could be held back from hitting its efficiency goals by a split development team working natively on separate iOS and Android versions of an app. Focused Labs CEO Austin Vance [described companies' path to better digital CX](https://www.forbes.com/sites/garydrenik/2022/08/16/upgrading-the-digital-customer-experience-with-a-modern-software-team-heres-how-to-do-it-well/?sh=673a7f455fd9) while speaking to Drenik for Forbes. Whether the companies are legacy businesses or start-ups, they have to make sure they are prioritizing digital design, progress, and innovation. Building internal teams and processes with tech excellence in mind is the way to join the ranks of companies that create positive digital CX. Drenik noted that digital maturity varies from one industry to another. While fields such as finance have fully committed to the idea of creating mobile experiences for their customers, that level of participation isn't universal. No matter what type of products or services a company provides, however, a positive mobile experience should be a priority. Organizations can self-reflect about their place in the digital customer journey hierarchy. If there's no easy way for consumers to interact with the brand via mobile devices, creating one should be a high priority. If internal mobile development does exist, leaders can take a granular look at those teams and departments, to make sure they're meeting their potential. ## How Apps Create the Digital Customer Experience With smartphones so popular with customers of all ages, app development is now a central component of digital customer experiences and the digital customer journey. It's important to remember that apps don't simply have a single role or purpose — they can accomplish a number of functions for companies, depending on need. A business's app can be a: - Primary e-commerce channel for a large or small retailer - Portal for customer interaction with service and care departments - Key component of a rewards program, allowing customers to carry omnichannel customer loyalty points between in-store and online purchases - Second-screen experience meant to [enrich an in-person show or event](https://verygood.ventures/success-stories/hamilton/) - Game experience designed to entertain and please customers These are just a few of the use cases for applications, which can deliver smooth, user-friendly experiences on today's powerful iOS and Android devices. Companies that take full advantage of app development can use these software tools as extensions of their overall branding and experience. Apps are part of an omnichannel digital customer experience strategy, one that spans all digital interaction and communication channels, as well as any in-person interactions. HubSpot specifies that true [omnichannel operations involve synchronicity](https://blog.hubspot.com/service/digital-customer-experience): A mobile app user can also interact with a brand through other digital channels and platforms and pick up where they left off. Companies should integrate their app development practices deep into their business operations, making sure the messaging on the app is consistent with their presence on social media, their websites, their ads and every other digital channel. This is yet another reason to have a unified development team instead of splitting development between Android or iOS: There's less chance of an inconsistent or fragmented customer experience across platforms. ## App Development Practices that Encourage Positive Customer Experience [Developing apps using best practices](https://verygood.ventures/solution/build-an-app/) can provide the kind of boost that companies need to compete in the demanding world of digital CX. Rather than just engaging in app development as an afterthought, businesses can truly focus on creating software that will lead to an optimal user experience and help the brand keep pace with industry norms while meeting customer needs. Practices closely associated with this kind of development include: ### Engaging in Multi-Platform Development Using Flutter Running multiple development teams can be a major drag on organizations' efforts to create strong and consistent digital experiences.  The emergence of effective multi-platform development solutions such as Flutter, however, has changed the calculus for companies. Now it's possible to create apps with impressive customer experiences while working with a single code base. This allows organizations to concentrate all their developers on a single project, saving resources and focusing on creating polished user experiences. Omnichannel consistency is one of the marks of an effective digital CX, and engaging in multi-platform development via Flutter is a one-step way to create such an experience. When coupled with the efficiency and quality assurance benefits of using a single code base, it's clear that organizations should consider shifting to multi-platform development, if they aren't already using it. ### Embracing Continuous Deployment & Automation Continuous deployment has become a major priority among software developers of all specializations. Corporate, customer-facing apps are no different. Continuous deployment methodologies mean pushing more updates more quickly, making small, low-impact changes that cumulatively keep apps up to date, rather than waiting for major scheduled releases. Continuous deployment makes heavy use of automation to ensure that developers can keep up the faster cadence of releases. When working as intended, the methodology removes some of the risks associated with slower update methods, including the danger that a large set of changes will cause unintended consequences, or that a feature will spend a long time in development, but not be relevant to users by the time it goes live. The digital CX of an app benefits in several ways when engineers use continuous deployment. The methodology allows developers to quickly address errors or inconveniences, while also enabling them to roll out new features that suit customer needs. Companies can continue tweaking their apps in subtle ways that won't cause them to break or confuse users with major shifts in functionality. ### Relying On Standardization & Simplification While app development is essential to the creation of a positive digital customer experience, this doesn't mean the process has to be complicated. In fact, companies tend to fare best when they use practices based around simplicity and standardization, such as relying on well-known Git processes and keeping pull requests small and limited in scope. By using practices such as a layered architecture, where each part of the app's architecture has a standard role, development teams can make their software easy to understand. This allows the fast onboarding of new team members, who will be familiar with what they're looking at. The developers should also make sure they're using a reliable and well-understood state management tool. [The Flutter framework](https://verygood.ventures/blog/top-companies-using-flutter/) also provides numerous ways to take a more simple, straightforward approach to development. A team can use the build flavors functionality to separate the development version of the app from the stable build that's in production, allowing teams to work without stepping on each other's toes. The Flutter ecosystem also contains thousands of open source tools for developers to take advantage of. ## Keeping Digital Customer Experience Strong Over Time One defining aspect of digital CX is the fact that success is a moving target. Organizations across industries are continuously developing and updating apps and other digital experiences to suit customers' evolving needs and tastes. Keeping up with this churn is essential. An organization's mobile app should be supported by strong backend tools that will allow developers to analyze and assess customer data around performance and usage. Through reports and dashboards, engineers can collect customer feedback and see how users are actually interacting with their apps, determining where improvements are needed, and measuring the performance of the application against customer expectations, as well as goals and projections. [Observability options for Flutter](https://verygood.ventures/blog/building-a-flutter-sdk-for-embrace/) continue to improve, reflecting the framework’s rapid maturation. One of the most significant roles an app can fulfill for a company is a customer data collection and actionable insight generation tool, gathering real-time information about a company's audience. The business can collect that information, analyze it, and use it as a tool, both for long-term efforts such as drawing up a development roadmap for the app, or short-term efforts such as sending in-app messages as part of a marketing campaign. App development can't be a set-and-forget process. Every aspect of modern customer experience management demands constant attention and frequent action by development teams. Companies that not only embrace these priorities but also give themselves the tools and systems for success are well positioned to create an impactful digital customer experience management strategy. ## Working with Experts on App Development App development is essential to digital CX. In turn, digital CX can lead to companies' success. All the processes involved are too important for organizations to take for granted. Working with industry experts is a way to get a leg-up in the digital CX space by collaborating with experienced development professionals to infuse development processes with best practices. Depending on an organization's needs, partners can help with the following: - Moving development to the Flutter ecosystem - Implementing development best practices - Engaging in digital experience design - Running analytics and reporting programs - Deploying and maintaining apps in app stores Working with the development experts at Very Good Ventures, who bring highly refined processes and years of experience to every project, is a way to create solutions that deliver the digital CX users crave, leading to customer satisfaction, and business success. ### [**Download our whitepaper**](https://verygood.ventures/whitepaper/building-enterprise-apps-with-flutter/) to learn more about the Very Good approach to development, or [**contact us**](https://verygood.ventures/contact-us/) directly to get started. ### Flutter News Toolkit: An advantage for publishers, powered by VGV Source: https://verygood.ventures/blog/flutter-news-toolkit-an-advantage-for-publishers-powered-by-vgv/ VGV is gratified to see the industry rallying around the Flutter News Toolkit - a Google-led project to help publishers get into mobile with speed, quality, and maintainability.  Some of the successes we’ve seen… - The amazing reaction from the community - A number of blog posts digging into its value - Companies already working on new news apps based on our template - Other agencies seeing the value and embracing the approaches VGV has pioneered and embodied in the template ![Flutter News Toolkit app screens showing pre-built features for publishers](/assets/images/blog/flutter-news-toolkit-an-advantage-for-publishers-powered-by-vgv/body-0.png) *The Flutter News Toolkit comes with a number of pre-built features.* [VGV and Google set out](https://youtu.be/dukRAS-OUMM) to help publishers succeed in the mobile era. To see the enthusiasm and efforts already underway to take advantage of this template has validated these efforts. News organizations have lots of choices when picking an agency to help them succeed with mobile.  ## Here's why we suggest you consider us at VGV ### VGV developed the Flutter News Toolkit for Google [We partnered with Google](https://verygood.ventures/success-stories/flutter-news-toolkit/) on the design and development of the Flutter News Toolkit. Our expertise is your advantage in speed to market with no compromises on brand or features.  > “The Very Good Ventures team is just so organized. I just have a lot of confidence in them. When they take on a project, I know we will get good results.” Zoey Fan, Senior Product Manager at Google.  ### VGV developed and maintains the technology stack that powers the toolkit With tools like Mason, Bloc, and Dart Frog, VGV puts you at the forefront of efficiency and maintainability. > “Very Good Ventures is laser focused on process and making life easier for the developers. They find economies of scale everywhere, always suggesting technologies that improve efficiency, like setting us up so that developers only need to learn Flutter and Dart to work on both front and backend development.” Timothy Hoang, Technical Solutions Product Manager at Google ### VGV knows that your app needs great code and great design to win readers. That's why we contributed the [Flutter News Toolkit Figma community file](https://www.figma.com/community/file/1200564667658247533) to make it even easier to customize your app’s design. > “Very Good Ventures puts a lot of design guidance into the toolkit as well. The template is not just a block of code. It offers a beautiful UI to start with. The user can easily customize, but they start from a great looking, performant app.” Zoey Fan, Google. ## Before choosing an agency partner, here are some key questions to ask: ### How familiar are they with the Flutter News Toolkit? Google hired VGV to partner with the Flutter team and the Google News Initiative to design and build the Flutter News Toolkit. [Read all about it](https://verygood.ventures/success-stories/flutter-news-toolkit/). ### How many enterprise flutter open source packages do they maintain? VGV develops and maintains [21 open source packages](https://verygood.ventures/resources/open-source-packages/) used by thousands of developers around the world. Many of these packages power the efficiency and maintainability in the Flutter News Toolkit.  ### How familiar are they with Flutter? VGV was the first agency fully dedicated to Flutter and is the industry leader in scalable Flutter standards. [Our approach](https://verygood.ventures/solution/build-an-app/) is a team-based model, coupled with our tools and best practices, to create Flutter codebases of exceptional architectural quality that will support feature and team growth long into the future. > “Thanks to Flutter and VGV, we delivered on our goal of building a fun, engaging, and performant app for all screens and mobile devices in significantly less time and cost than if we built it natively. The team and thousands of players are very happy with the results.” Clarence Kwei, SVP Consumer Technology, Dow Jones ### How will they ensure knowledge transfer so the publisher is self-sufficient? VGV is committed to making our clients self-sufficient through peer programming and training. Don’t be fooled by low-cost agencies who will leave you holding the bag with a lot of code your engineers may not be able to maintain. Most of your app’s cost comes ***after launch*** when you need to maintain it and add features. VGV works with you to ensure your [long-term success](https://verygood.ventures/success-stories/).  ## Build a very good news app We are proud that the Flutter News Toolkit is easy to use and superbly documented so that publishers and their agencies can launch with it.  For publishers with exacting requirements, perhaps with existing native apps they wish to migrate, VGV is the obvious choice. [At a fixed fee](https://verygood.ventures/solution/very-good-news/), publishers can partner with VGV and get a beautiful, custom, full-featured mobile news app for iOS and Android in 8 weeks for a fraction of the cost to build natively.  ### Partner with us to bring your news app to mobile with the Flutter News Toolkit. [**Contact us to get started →**](https://verygood.ventures/contact-us/) ### Enterprise app development with Flutter: What you need to know Source: https://verygood.ventures/blog/enterprise-app-development-what-you-need-to-know/ Companies today have one leading way to communicate with their customers, employees and partners: They do it digitally. And a very good way to make that kind of digital connection is through applications. Complicating this view is the fact that apps aren’t limited to a single platform. Web apps and desktop apps are both valid ways to reach PC audiences, while the mobile app landscape is split between Android and iOS devices. This means companies don't just need an app development strategy — they need one that will allow them to engage all of these segments. Organizations that haven't yet figured out their approach to enterprise app development, or those stuck with an inefficient, labor-intensive approach need to think about their next steps, and do so quickly. In the race to create a positive digital customer experience, enterprise application development services are a critical consideration. First, it pays to take a step back and think about what an app can accomplish for a company. Then, it's time to take action and implement best practices that will turn any company into a software development powerhouse. ## Enterprise apps: How companies connect with audiences Bain & Company research confirms that people reach for digital channels first when communicating with brands. The consultancy analyzed the data and found that both banks and consumer product companies that prioritize tech are [leading their competitors](https://www.bain.com/insights/six-trends-shaping-the-future-of-enterprise-technology/) in terms of compound annual growth rate and shareholder return. These market leaders have decided to make technology a key part of their value proposition, embracing digital transformation to turn themselves into enterprise software development companies alongside their core business. While in decades past it may have seemed absurd for a bank to have a fully functioning team of coders on staff, today that's simply the cost of doing business — fast-moving companies need enterprise software, and someone has to develop it. Once an organization is functioning as an enterprise app development company, it gains exciting new ways to interact with customers and internal users alike. Real-world examples of enterprise apps in action include: - [**Toyota**](https://flutter.dev/showcase/toyota): Advanced vehicle control features on easy-to-use touchscreen displays. - [**Betterment**](https://verygood.ventures/success-stories/betterment/): Reliable on-the-go investing through a multi-platform mobile application. - [**Alibaba**](https://flutter.dev/showcase/alibaba-group): A feature-rich e-commerce marketplace application with massive scalability. - [**BMW**](https://flutter.dev/showcase/bmw): Simplified vehicle tech system access through a mobile application. ## The importance of multi-platform app development The rise of Flutter is a fast-growing phenomenon: [Over 1,000 new apps](https://medium.com/flutter/announcing-flutter-3-3-at-flutter-vikings-6f213e068793) built with the framework are released every day. Organizations have seen the value of multi-platform development and are shifting rapidly to take advantage of it. [Flutter development](https://verygood.ventures/blog/top-companies-using-flutter/) is a revelation for companies because it combines the best of both worlds — teams can create powerful apps that make effective use of devices' processing power while uniting around a single codebase. Without the headaches of managing multiple development streams, companies can focus on creating better apps, rather than simply trying to keep up between teams. They can push for tweaks to improve the digital user experience, or work on fine-tuning performance. A good application will keep evolving over time, driven by both a long-term road map and short-term updates in response to usage patterns and feedback. A business developing its enterprise solution in Flutter can work on these priorities, rather than sacrificing time and effort to managing multiple code bases. Choosing Flutter as an enterprise software development framework isn't just a good choice because of its multi-platform nature, either. The Flutter environment contains [over 29,000 packages](https://pub.dev/packages?q=sdk%3Aflutter), allowing developers to quickly and painlessly build out their apps' functionality. Once a business has committed to multi-platform development and shifted its codebase over to Flutter, it's time for that organization to make sure its developers are adhering to best practices and getting the most out of their efforts. ## App development best practices Rather than implementing quick-fix solutions to sharpen their app development practices, companies have to fully rethink the way they work. This doesn't have to act as a barrier stopping companies from developing enterprise software. Many of the best practices powering modern enterprise app development are common-sense ideas that pay off quickly. At Very Good Ventures, we've given a lot of thought to these best practices and ideal workflows, as we've refined our Very Good approach to enterprise app development. The following are the five main areas we focus on in our method, and which can easily become part of any company's strategy. ### 1. Architecture One of the most impactful decisions enterprise mobile app development teams will make is the way they structure the architecture of their software. If they choose a consistent, simple method, it becomes easier for each individual app developer to understand how the app works. Breaking the functions of the app down into discrete layers is a key practice. This allows the team to build out code that is "boring" and easy to grasp, even for a new team member who has just joined the project. The net result is easier bug fixes and less time briefing new developers on how the app works. State management should be another priority in addition to layered architecture. By choosing a reliable state management solution, a development team can set itself up with a simplified testing process and comprehensive documentation, making its own job easier. ### 2. Testing Testing today should be a mixture of multiple methodologies. Manual quality assurance processes are still useful, no matter how advanced automation becomes. With that said, the increasing use of automation helps organizations attain very high levels of coverage to release code with fewer errors. Attaining high levels of automated test coverage is a valuable practice for both short-term reasons, such as eliminating bugs before they reach production, and long-term purposes, including creating code that will keep functioning effectively through years and refactors. Setting a high threshold of test coverage and then raising that standard over time is a valuable practice when organizations are determining how to handle this aspect of development. Working in this way, developers can approach 100% code coverage. ### 3. Configuration Enterprise application configuration should be driven by the same best practices that have caught on in the mobile app development world. Perhaps most prominently, this means continuous integration. Small, rapid changes are a low-risk and highly effective way to roll out new features and bug fixes. In addition to a good CI framework, organizations should have standardized processes that they rely on. A lack of esoteric practices has long-term benefits for developers, as any non-standard actions can lead to overly complicated code down the line. Working in Flutter, developers have access to another simplifying feature that can help them do their jobs: build flavors. This functionality allows users to set up different builds that make sense for their team. For example,  setting up a development build can allow teams to move quickly and see changes in real time, while a separate stable build runs in production. ### 4. Automation Adding automation to the development process has numerous benefits that make it worth the effort. Perhaps most prominently, removing manual work gives developers time back in their days. They can spend those hours saved on more experimental efforts, working on new items on the application's development roadmap. Working with templates is one way to put automation in place. Developers can start a new app from a template, or use one to add a feature or a package to an existing application. The use of open source templates is a streamlining option for development workflows, one that encourages the continued use of best practices. Repeatable and standardized GitHub Actions are another automation developers can use when building their enterprise apps. As with templates, many of these workflows are publicly available and open source, meaning they're accessible and community-tested. ### 5. Ecosystem The Flutter ecosystem has extra advantages for developers. The thousands of packages available can plug into companies' workflows and help them reach their individual enterprise solution development goals. Flutter Community members, prominently including the Very Good Ventures team, are constantly adding new useful open source packages. One way to measure the quality of a package is its popularity among developers. Other criteria to consider is how actively maintained a package is and if it contains adequate documentation. No matter what their particular enterprise app use case may be, organizations can settle into the Flutter ecosystem, using the available tools to develop software that can adapt and grow in the years to come. Working with Flutter means becoming part of an active community. ## The results of deploying an effective enterprise app While best practices for enterprise app development are important, they're simply means to an end. The proof that these methods and workflows are worth embracing comes from the results companies achieve. ### A reliable, future-proof mobile experience for 500,000 users Businesses in the financial space have to deliver a truly exceptional app user experience today. Customers want quick, friction-free access to their account information and assets. Being an organization that manages clients' money is a high-pressure situation, one with no margin for error. Apps must be completely secure and reliable. [Betterment](https://verygood.ventures/success-stories/betterment/), an investment service with 500,000 customers and $26 million assets under management, needed a better way to manage its legacy code bases, which were becoming unwieldy and slowing down new feature development. The organization worked with Very Good Ventures to retrain its 20 developers in Flutter and move the app, piece by piece, into the multi-platform architecture. With the app successfully transferred to Flutter, Betterment can keep providing a handy mobile experience for its large user base for years to come. Now that development has become significantly simpler, the company can iterate and add features, so the app will remain relevant in the future. ### A tax tool that works for users and organizations alike Rather than porting an existing app to Flutter, the tax administrator [Berkheimer Tax Innovations](https://verygood.ventures/success-stories/h-a-berkheimer/) decided to build a new application in the Flutter ecosystem. This would be an ambitious project, one that would use devices' cameras to capture document images and interact directly with the Pennsylvania state government's processing platform. In addition to allowing developers to work with a single code base, Flutter offers a powerful tool set, so the Very Good Ventures team was able to execute on Berkheimer's design specifications. The resulting app provides a simple and elegant tax filing solution without compromising the security of user data. The end result of releasing a reliable and powerful app was to double user growth year over year. The app runs on scalable cloud resources to ensure that it would be able to handle the traffic without suffering performance drop-offs. All those improvements came with a streamlined, cost-effective development process. ## The process of working with an app development partner Every company has its own use cases for mobile, desktop and web applications. By that same token, each organization will also have its own ideal development process. To achieve high levels of performance while pursuing their unique business goals, companies can form partnerships with third-party enterprise app development services, and these collaborations can take a variety of forms. When reaching out to external teams for assistance working with their enterprise app development functions, an organization can control both the type of project and the depth of the engagement. Some of the options include: - [**Code assessment**](https://verygood.ventures/solution/flutter-code-assessment/): An organization that already has an application, as well as the internal development talent to continue working on that app, may still want an expert set of eyes to assess its code. The third-party team can offer insights and recommendations about how to ensure the app is scalable and conforms with best practices. - [**App developer training**](https://verygood.ventures/solution/flutter-training/): In cases when a development team needs an infusion of new skills, for example when a company is using Flutter for the first time, engineers can benefit from training sessions led by external experts. - [**Engineering a shift to Flutter**](https://verygood.ventures/solution/flutter-transformation/): Shifting app features from native app developed legacy code to Flutter is a high-priority process for organizations hoping to move forward with a multi-platform app. To ensure success, these businesses can work with experienced partners. - [**Full application development**](https://verygood.ventures/solution/build-an-app/): Not every organization has a full in-house enterprise application development team. To stay competitive in today's digital-focused corporate climate, these businesses can enlist enterprise application development services from experts. - [**Development acceleration**](https://verygood.ventures/solution/very-good-start/): Outside developers don't have to take complete control of an app development process to drive its success. Kickstarting development with reliable frameworks and best practices can accelerate a development timeline. At Very Good Ventures, we can help you add any of these capabilities to your organization's enterprise app development efforts. With a hand in numerous industry-leading Flutter apps, including the first commercial app built on Flutter, there is no more experienced digital transformation partner for your business. ### [**Read our whitepaper**](https://verygood.ventures/whitepaper/building-enterprise-apps-with-flutter/) to learn more about the Very Good approach to enterprise app development, or [**contact us today**](https://verygood.ventures/contact-us/). ### VGV at Flutter Forward Source: https://verygood.ventures/blog/vgv-at-flutter-forward/ 2023 started off with a bang with [Flutter Forward](https://flutter.dev/events/flutter-forward). At VGV, we always look forward to these global Flutter events because we know that there will be many updates, demos, and teasers for what’s around the corner. Flutter Forward did not disappoint! We were excited to see the [Flutter 3.7](https://medium.com/flutter/whats-new-in-flutter-3-7-38cbea71133c) and [Dart 3 alpha](https://medium.com/dartlang/dart-3-alpha-f1458fb9d232) announcements, the progress made on the [Impeller](https://github.com/flutter/flutter/wiki/Impeller) runtime, [element embedding on the web](https://flutter-forward-demos.web.app/#/), and many more promising previews of what's to come. We won’t get into all of the details here (instead, see this [comprehensive article by Tim Sneath about what’s next for Flutter](https://medium.com/flutter/whats-next-for-flutter-b94ce089f49c)), but we did want to share some of the ways VGV was involved in Flutter Forward: ## VGV in Nairobi, Kenya Our teammates Felix, Kaicey, Óscar, and Jaime traveled to Nairobi, Kenya to attend Flutter Forward in person! Here are some pictures from the event: ![VGV teammates at the Flutter Forward event in Nairobi with life-size Dash](/assets/images/blog/vgv-at-flutter-forward/body-0.jpeg) *The event featured a life-size Dash and a real pinball machine where users could play I/O Pinball* ## Flutter Forward Holobooth We teamed up with Google to take [I/O Photo Booth](https://github.com/flutter/photobooth) to the next level with Flutter Forward [Holobooth](https://holobooth.flutter.dev/#/). Holobooth is a Flutter web app that incorporates Flutter, Firebase, Machine Learning (through MediaPipe and TensorFlow.js), and Rive. It was launched the day before the event as excitement grew with all of the impending Flutter Forward announcements. This demo was a very cool opportunity that allowed us to explore some uncharted territory with Flutter, such as incorporating TensorFlow. We had a lot of fun with it! You can learn more about [how we built it](https://medium.com/flutter/how-its-made-holobooth-6473f3d018dd) or visit the [Holobooth](https://holobooth.flutter.dev/#/) to take your own dynamic photo. ![Holobooth Flutter web app with TensorFlow-powered dynamic photo experience](/assets/images/blog/vgv-at-flutter-forward/body-1.png) ## Flutter News Toolkit  Google also open sourced the [Flutter News Toolkit](https://medium.com/flutter/announcing-the-flutter-news-toolkit-180a0d32c012), a template built to help streamline the process of building news apps (or really any article-based app). We teamed up with Google to build the toolkit, which comes with a bunch of pre-built features, such as content feeds, monetization through ads, search, and more. A lot of thought went into this project — from implementing a scalable architecture to creating an easily customizable design for the app. It uses some helpful VGV tools such as Dart Frog to make it easy to connect to your server. To start building your app, head over to the docs site for detailed step-by-step instructions. ![Flutter News Toolkit documentation site homepage](/assets/images/blog/vgv-at-flutter-forward/body-2.png) *Flutter News Toolkit documentation site* Or, follow along with this video: {% video url="https://www.youtube.com/watch?v=dukRAS-OUMM" title="Video" %} We even created a [Figma community template](https://www.figma.com/community/file/1200564667658247533) for the Flutter News Toolkit to make it even easier to customize your app’s theme, assets and components: ![Figma community template for customizing the Flutter News Toolkit theme](/assets/images/blog/vgv-at-flutter-forward/body-3.png) More information about the Flutter News Toolkit can be found on the [Flutter site](https://flutter.dev/news). ## Flutter community talks Our teammates also participated in the Flutter community talks, now available on YouTube. ### Jaime Blasco provides an essential overview of navigation with GoRouter {% video url="https://www.youtube.com/watch?v=ZH3tlL60fTI" title="Video" %} ### Felix Angelov teams up with Dominik Roszkowski to build real-time games with Dart {% video url="https://www.youtube.com/watch?v=TGKipiJhpXo" title="Video" %} ## Bonus: Shaders featured in the keynote We were excited to see shader work from our teammates Renan and Jochum highlighted during the Flutter Forward keynote, right before the preview of [Impeller](https://github.com/flutter/flutter/wiki/Impeller) — the next version of Flutter’s rendering engine, which prioritizes performance for graphics and animations. ![VGV shader demos highlighted during the Flutter Forward keynote](/assets/images/blog/vgv-at-flutter-forward/body-4.png) ## What’s next We are very excited about where [Flutter is headed](https://medium.com/flutter/whats-next-for-flutter-b94ce089f49c). We look forward to implementing some of the amazing updates announced at the event, like web stuff and 3D rendering with Impeller, in our projects with clients. And, we’ll keep an eye on the [Flutter Roadmap](https://github.com/flutter/flutter/wiki/Roadmap) to see if we can come up with new open source tooling ideas. ### Dart Frog full stack tutorial Source: https://verygood.ventures/blog/dart-frog-full-stack-tutorial/ In this blog we're going to build, yup you guessed it, a Flutter counter — but we're not going to build just any Flutter counter. We're going to build a Flutter counter which connects to a Dart Frog server using WebSockets and increment the value of the counter in real time across all platforms! When we're finished, we should have something that looks like this: We'll learn how to establish a WebSocket connection, react to changes in the connection state, and send and receive messages from the server. In this blog, we will focus on the Flutter application. Before we dive in, check out the [Dart Frog WebSocket Counter tutorial](https://dart-frog.dev/tutorials/web-socket-counter/) in the Dart Frog docs to understand how to build the server used in this example. ## Project structure First, let's take a look at a how the app will work: ![Diagram of project structure with steps 1. Send increment message 2. Compute new count 3. Broadcast new count. Diagram has three devices displaying a Flutter app. Above is a cloud with the Dart Frog logo to represent the Dart Frog server. An increment event arrow extends from one device to the Dart Frog server. Arrows point from the Dart Frog server to the three devices to represent the updated count on all devices.](/assets/images/blog/dart-frog-full-stack-tutorial/body-0.png) The Flutter application we'll build in this blog will establish a WebSocket connection with a Dart Frog server and listen for changes in count. When a user taps the increment or decrement buttons, the client will send an increment or decrement message to the Dart Frog server. The Dart Frog server will handle computing the new count and broadcast that count to all connected clients. When we're all done, our project structure should look something like this: ```txt ├── lib │ ├── app │ │ └── view │ ├── counter │ │ ├── bloc │ │ └── view │ └── l10n ├── packages │ └── counter_repository ├── test ``` Note that other directories like **ios**, **android**, etc. are omitted for simplicity. ## Creating the Flutter app Let's use [**very_good_cli**](https://pub.dev/packages/very_good_cli) to generate a new Flutter application. If you haven't used **very\_good\_cli** already, you can install it from pub.dev: ```shell dart pub global activate very_good_cli ``` Then, let's use the create command to generate a new Flutter app: ```shell very_good create web_socket_counter_flutter --desc "A Flutter real-time counter which integrates with Dart Frog and WebSockets." ``` At this point we have a Flutter starter application with VGV best practices baked in. To learn more about Very Good CLI, check out the [official documentation site](https://cli.vgv.dev/). ### Counter Repository Before we make changes to the Flutter application, we'll start by creating a **counter\_repository** package which will manage the counter domain. Creating the **counter\_repository** allows us to establish a layer of abstraction between the application and the data giving us flexibility. For example, in the future if we decide to move away from WebSockets, we will not need to modify the Flutter application. To learn more about a layered architecture, check out our [blog post](https://verygood.ventures/blog/very-good-flutter-architecture/). We can use **very\_good\_cli** to create the **counter\_repository** package: ```shell very_good create counter_repository -t dart_pkg --desc "A Dart package which manages the counter domain." ``` Next, we'll need to add two dependencies to the **counter\_repository pubspec.yaml**: - [**web_socket_client**](https://pub.dev/packages/web_socket_client): A Dart WebSocket Client which will allow us to communicate with the Dart Frog server. - [**web_socket_counter**](https://github.com/VeryGoodOpenSource/dart_frog/tree/main/examples/web_socket_counter): The Dart Frog WebSocket counter package which exports models which we can reuse. The **pubspec.yaml** should look like: ```yaml name: counter_repository description: A Dart package which manages the counter domain. version: 0.1.0+1 publish_to: none environment: sdk: ">=2.18.0 <3.0.0" dependencies: web_socket_client: ^0.1.0-dev.1 web_socket_counter: git: url: https://github.com/VeryGoodOpenSource/dart_frog path: examples/web_socket_counter dev_dependencies: mocktail: ^0.3.0 test: ^1.19.2 very_good_analysis: ^3.1.0 ``` Then, install the dependencies by running **dart pub get**. The **CounterRepository** class will expose public methods that abstract the implementation details and provide domain models for the application to consume. ```dart import 'package:web_socket_client/web_socket_client.dart'; import 'package:web_socket_counter/counter/counter.dart'; /// A Dart package which manages the counter domain. class CounterRepository { CounterRepository({WebSocket? socket}) : _ws = socket ?? WebSocket(Uri.parse('ws://localhost:8080/ws')); final WebSocket _ws; /// Send an increment message to the server. void increment() => _ws.send(Message.increment.value); /// Send a decrement message to the server. void decrement() => _ws.send(Message.decrement.value); /// Return a stream of real-time count updates from the server. Stream get count => _ws.messages.cast().map(int.parse); /// Return a stream of connection updates from the server. Stream get connection => _ws.connection; /// Close the connection. void close() => _ws.close(); } ``` We are exposing increment and decrement methods which send messages to the Dart Frog server. Note that the **Message** enum allows us to use strongly typed objects, and it is used and exported by the Dart Frog server. Being able to share the **Message** enum is an example of code sharing, which can help you be more efficient when building full stack Dart applications and ensure the frontend client code is compatible with the server. The **CounterRepository** also exposes a stream of counts which the application can subscribe to in order to receive updates. A stream of **ConnectionState** is exposed in order to allow the application to react to changes in the **WebSocket** connection. Lastly, the **CounterRepository** exposes a close method which closes the underlying connection and can be used to free up any resources. That's it for the **counter\_repository** implementation! Next, add the **counter\_repository** to the **pubspec.yaml** of the Flutter application. The updated **pubspec.yaml** should look like: ```yaml name: web_socket_counter_flutter description: A Flutter real-time counter which integrates with Dart Frog and WebSockets. version: 1.0.0+1 publish_to: none environment: sdk: ">=2.18.0 <3.0.0" flutter: 3.3.10 dependencies: bloc: ^8.1.0 counter_repository: path: packages/counter_repository equatable: ^2.0.5 flutter: sdk: flutter flutter_bloc: ^8.1.1 flutter_localizations: sdk: flutter intl: ^0.17.0 dev_dependencies: bloc_test: ^9.1.0 flutter_test: sdk: flutter mocktail: ^0.3.0 very_good_analysis: ^3.1.0 flutter: uses-material-design: true generate: true ``` Make sure to install all dependencies by running **flutter packages get**. Now time to switch gears and focus on the Flutter app! ### Counter Bloc The application generated by Very Good CLI already comes with a counter feature which you can find in the lib directory. The first thing we'll do is remove the cubit directory and create a **CounterBloc** instead. Since we'll be working with streams, [using a bloc provides some advantages over a cubit](https://bloclibrary.dev/#/coreconcepts?id=cubit-vs-bloc). We can create a new bloc either by using the [bloc VS Code extension](https://marketplace.visualstudio.com/items?itemName=FelixAngelov.bloc) or using the [bloc brick](https://brickhub.dev/bricks/bloc/0.2.0). **CounterState** Once we have generated a new **CounterBloc**, let's work on modeling the state. In this example, we'll model the state as a single object which contains the value of the count as well as a status to indicate whether the client is connected to the server: ```dart part of 'counter_bloc.dart'; enum CounterStatus { connected, disconnected } class CounterState extends Equatable { const CounterState({ this.count = 0, this.status = CounterStatus.disconnected, }); final int count; final CounterStatus status; @override List get props => [count, status]; CounterState copyWith({int? count, CounterStatus? status}) { return CounterState( count: count ?? this.count, status: status ?? this.status, ); } } ``` **CounterEvent** Next, let's define the events the bloc will be reacting to. The **CounterBloc** in this example will react to five different events but only three of those events will be external. - **CounterStarted**: notifies the bloc that the counter feature has started and prompts the bloc to subscribe to changes from the backend - **CounterIncrementPressed**: notifies the bloc that the user has tapped on the increment button - **CounterDecrementPressed**: notifies the bloc that the user has tapped on the decrement button - **\_CounterCountChanged**: notifies the bloc that the count has changed on the backend (internal) - **\_CounterConnectionStateChanged**: notifies the bloc that the connection state has changed (internal) ```dart part of 'counter_bloc.dart'; abstract class CounterEvent { const CounterEvent(); } class CounterStarted extends CounterEvent { const CounterStarted(); } class CounterIncrementPressed extends CounterEvent { const CounterIncrementPressed(); } class CounterDecrementPressed extends CounterEvent { const CounterDecrementPressed(); } class _CounterCountChanged extends CounterEvent { const _CounterCountChanged(this.count); final int count; } class _CounterConnectionStateChanged extends CounterEvent { const _CounterConnectionStateChanged(this.state); final ConnectionState state; } ``` **CounterBloc** Now that we have the state and events defined, we can implement the bloc. ```dart class CounterBloc extends Bloc { CounterBloc({required CounterRepository counterRepository}) : _counterRepository = counterRepository, super(const CounterState()) {...} final CounterRepository _counterRepository; } ``` First, the bloc will depend on the **CounterRepository** we created earlier, so we'll add it as a required parameter to the bloc constructor. Next, we'll create an event handler for each event. Tip: you can use the **onevent** and **\_onevent** snippets from the Bloc VS Code extension. Let's start with **CounterStarted**. When the bloc receives a **CounterStarted** event, it will subscribe to changes in the count and connection and add internal events for whenever either changes. As a result, we need to define two **StreamSubscription** instances and override close to cancel those subscriptions when the bloc is closed. ```dart class CounterBloc extends Bloc { CounterBloc({required CounterRepository counterRepository}) : _counterRepository = counterRepository, super(const CounterState()) { on(_onCounterStarted); } StreamSubscription? _countSubscription; StreamSubscription? _connectionSubscription; void _onCounterStarted( CounterStarted event, Emitter emit, ) { _countSubscription = _counterRepository.count.listen( (count) => add(_CounterCountChanged(count)), ); _connectionSubscription = _counterRepository.connection.listen((state) { add(_CounterConnectionStateChanged(state)); }); } @override Future close() { _connectionSubscription?.cancel(); _countSubscription?.cancel(); return super.close(); } } ``` Next, let's implement the **\_CounterConnectionStateChanged** handler. When the connection state changes, the bloc will emit a new state with an updated status based on whether we are connected or disconnected from the backend. We use a private extension on ConnectionState to convert the **ConnectionState** to a **CounterStatus**. ```dart class CounterBloc extends Bloc { CounterBloc({required CounterRepository counterRepository}) : _counterRepository = counterRepository, super(const CounterState()) { ... on<_CounterConnectionStateChanged>(_onCounterConnectionStateChanged); } ... void _onCounterConnectionStateChanged( _CounterConnectionStateChanged event, Emitter emit, ) { emit(state.copyWith(status: event.state.toStatus())); } } extension on ConnectionState { CounterStatus toStatus() { return this is Connected || this is Reconnected ? CounterStatus.connected : CounterStatus.disconnected; } } ``` The **\_CounterCountChanged** event handler will be very similar so let's implement that next. When the bloc receives a **\_CounterCountChanged** event it will emit a new state with the updated count and in this case always set the status to connected since the bloc can only ever receive count updates from the server if it is connected. ```dart class CounterBloc extends Bloc { CounterBloc({required CounterRepository counterRepository}) : _counterRepository = counterRepository, super(const CounterState()) { on(_onCounterStarted); on<_CounterConnectionStateChanged>(_onCounterConnectionStateChanged); on<_CounterCountChanged>(_onCounterCountChanged); } ... void _onCounterCountChanged( _CounterCountChanged event, Emitter emit, ) { emit(state.copyWith(count: event.count, status: CounterStatus.connected)); } } ``` The **CounterIncrementPressed** and **CounterDecrementPressed** event handlers are all that's left and when a bloc receives either event it will invoke increment or decrement on the **CounterRepository** respectively. The finished **CounterBloc** implementation should look like: ```dart import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:counter_repository/counter_repository.dart'; import 'package:equatable/equatable.dart'; part 'counter_event.dart'; part 'counter_state.dart'; class CounterBloc extends Bloc { CounterBloc({required CounterRepository counterRepository}) : _counterRepository = counterRepository, super(const CounterState()) { on(_onCounterStarted); on<_CounterConnectionStateChanged>(_onCounterConnectionStateChanged); on<_CounterCountChanged>(_onCounterCountChanged); on(_onCounterIncrementPressed); on(_onCounterDecrementPressed); } final CounterRepository _counterRepository; StreamSubscription? _countSubscription; StreamSubscription? _connectionSubscription; void _onCounterStarted( CounterStarted event, Emitter emit, ) { _countSubscription = _counterRepository.count.listen( (count) => add(_CounterCountChanged(count)), ); _connectionSubscription = _counterRepository.connection.listen((state) { add(_CounterConnectionStateChanged(state)); }); } void _onCounterIncrementPressed( CounterIncrementPressed event, Emitter emit, ) { _counterRepository.increment(); } void _onCounterDecrementPressed( CounterDecrementPressed event, Emitter emit, ) { _counterRepository.decrement(); } void _onCounterConnectionStateChanged( _CounterConnectionStateChanged event, Emitter emit, ) { emit(state.copyWith(status: event.state.toStatus())); } void _onCounterCountChanged( _CounterCountChanged event, Emitter emit, ) { emit(state.copyWith(count: event.count, status: CounterStatus.connected)); } @override Future close() { _connectionSubscription?.cancel(); _countSubscription?.cancel(); return super.close(); } } extension on ConnectionState { CounterStatus toStatus() { return this is Connected || this is Reconnected ? CounterStatus.connected : CounterStatus.disconnected; } } ``` ### Counter Feature Now that the **CounterBloc** is implemented, let's update the UI. First, let's provide an instance of the **CounterRepository** to the widget tree in **app.dart** ```dart import 'package:counter_repository/counter_repository.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:web_socket_counter_flutter/counter/counter.dart'; import 'package:web_socket_counter_flutter/l10n/l10n.dart'; class App extends StatefulWidget { const App({super.key}); @override State createState() => _AppState(); } class _AppState extends State { late final CounterRepository _counterRepository; @override void initState() { super.initState(); _counterRepository = CounterRepository(); } @override void dispose() { _counterRepository.close(); super.dispose(); } @override Widget build(BuildContext context) { return RepositoryProvider.value( value: _counterRepository, child: const AppView(), ); } } class AppView extends StatelessWidget { const AppView({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: CounterPage(), ); } } ``` Note that we're using a **StatefulWidget** so that we can manage the **CounterRepository** instance and close it when the widget is disposed. Next, let's update the **CounterPage** widget to provide an instance of the **CounterBloc**. ```dart class CounterPage extends StatelessWidget { const CounterPage({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => CounterBloc( counterRepository: context.read(), )..add(const CounterStarted()), child: const CounterView(), ); } } ``` We immediately add a **CounterStarted** event to the bloc in order to start listening for updates to the count as soon the widget is mounted. Next, let's update the **CounterView** to display the counter text, connection text, and floating action buttons to increment and decrement the counter: ```dart class CounterView extends StatelessWidget { const CounterView({super.key}); @override Widget build(BuildContext context) { final l10n = context.l10n; return Scaffold( appBar: AppBar(title: Text(l10n.counterAppBarTitle)), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: const [ CounterText(), ConnectionText(), ], ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: const [ IncrementButton(), SizedBox(height: 8), DecrementButton(), ], ), ); } } ``` The **CounterText** widget will use the **context.select** extension to rebuild whenever the count changes in the bloc state: ```dart class CounterText extends StatelessWidget { const CounterText({super.key}); @override Widget build(BuildContext context) { final theme = Theme.of(context); final count = context.select((CounterBloc bloc) => bloc.state.count); return Text('$count', style: theme.textTheme.headline1); } } ``` Similarly, the **ConnectionText** widget uses **context.select** to rebuild whenever the status changes in the bloc state: ```dart class ConnectionText extends StatelessWidget { const ConnectionText({super.key}); @override Widget build(BuildContext context) { final theme = Theme.of(context); final l10n = context.l10n; final connection = context.select((CounterBloc bloc) => bloc.state.status); switch (connection) { case CounterStatus.connected: return Text( l10n.counterConnectedText, style: theme.textTheme.caption?.copyWith( color: theme.colorScheme.primary, ), ); case CounterStatus.disconnected: return Text( l10n.counterDisconnectedText, style: theme.textTheme.caption?.copyWith( color: theme.colorScheme.error, ), ); } } ``` Lastly, the increment and decrement buttons are only enabled if the status is connected. When tapped, they add the correct event to the bloc: ```dart class IncrementButton extends StatelessWidget { const IncrementButton({super.key}); @override Widget build(BuildContext context) { final isConnected = context.select( (CounterBloc bloc) => bloc.state.status == CounterStatus.connected, ); return FloatingActionButton( backgroundColor: isConnected ? null : Colors.grey, onPressed: isConnected ? () => context.read().add(const CounterIncrementPressed()); : null, child: const Icon(Icons.add), ); } } class DecrementButton extends StatelessWidget { const DecrementButton({super.key}); @override Widget build(BuildContext context) { final isConnected = context.select( (CounterBloc bloc) => bloc.state.status == CounterStatus.connected, ); return FloatingActionButton( backgroundColor: isConnected ? null : Colors.grey, onPressed: isConnected ? () => context.read().add(const CounterDecrementPressed()); : null, child: const Icon(Icons.remove), ); } } ``` ### Running the App We're finally ready to run the app now! We recommend running the app in debug mode directly from either VS Code or Android Studio but you can also run it from the command line: ```shell flutter run -t lib/main_development.dart --flavor development ``` Once the app is running you should see the client is disconnected and the floating action buttons are disabled. Now all that's left to do is run the Dart Frog server. To see how we built the server, refer to the [tutorial](https://dart-frog.dev/tutorials/web-socket-counter/) in the Dart Frog docs. ```shell # Install Dart Frog CLI. dart pub global activate dart_frog_cli # Clone the GitHub Repository. git clone https://github.com/VeryGoodOpenSource/dart_frog # Change into the web_socket_counter example directory. cd dart_frog/examples/web_socket_counter # Install the dependencies. dart pub get # Run the development server. dart_frog dev ``` Once the server is running you should see the client establish a connection. You can run the Flutter app on a different device to establish multiple connections to the server and watch the counter change in real-time. ## Summary That's it! You can now build a full stack Dart application with real-time bidirectional communication using WebSockets. The [complete source code](https://github.com/VGVentures/web_socket_counter_flutter) is available on GitHub (with 100% test coverage, of course). ### [**Learn more about Dart Frog at dart-frog.dev →**](https://dart-frog.dev/) ### 2023 Open Source & Tooling Roadmap Source: https://verygood.ventures/blog/open-source-tooling-roadmap-2023/ 2022 was a pivotal year for open source at Very Good Ventures. We announced the addition of Jochum and Renan to our [tooling & open source team](https://verygood.ventures/blog/tooling-and-open-source-team/), made thousands of commits to GitHub, and released ten brand new open source tools, including [**Dart Frog**](https://dart-frog.dev/), [**Very Good Ranch**](https://ranch.vgv.dev/), and multiple new [**Very Good CLI**](https://cli.vgv.dev/) templates. We’re proud to have created a suite of open source tools that work really well together or separately. For example, you could use **Very Good CLI** to generate a Flutter app foundation using **Very Good Core**, which comes with **Very Good Analysis** and **Very Good Workflows**; then, as you’re developing you could use **Very Good Infinite List** to implement a lazy-loading photo album and use **r13n** to add region-specific contact information in your app. Or, maybe you’re working on a client app and just want to use our strict lint rules with **Very Good Analysis**. Both use cases are totally valid. We want to provide tools that are useful and help increase developer efficiency — in whatever combination makes the most sense for you! In 2023, we’re going to focus on all of the things that come with maintaining open source tools: ongoing upkeep, fixing bugs, triaging issues, and developing high-quality documentation and examples — all with the goal of improving the developer experience within VGV and in the broader Flutter community. We’re also going to explore new tools and packages that we can add to the growing ecosystem. Before we dive into more detail, let’s take a look back at what we accomplished last year. ## 2022 by the numbers ![Stats from the Very Good Open Source GitHub organization. 10 new open source tools, 1400+ stars on open source GitHub repos, 4000+ contributions to Very Good Open Source.](/assets/images/blog/open-source-tooling-roadmap-2023/body-0.png) ## 2023 OSS & tooling roadmap ### Maintaining core tooling Our team builds tools that fall into three phases of development: **create**, **build**, and **scale**. In 2022, we open sourced ten new tools, from templates like the Flame Game template, to our backend framework Dart Frog, to our fun exploration into game development [**Very Good Ranch**](https://ranch.vgv.dev/). This year, we will keep maintaining all of our open source tools, and look for opportunities for new tooling to help developers create, build, and scale their projects with VGV best practices. Here is a list of open source tools that we actively maintain: #### CREATE - [**Very Good CLI**](https://github.com/VeryGoodOpenSource/very_good_cli): A Command-Line Interface to help you generate scalable templates and execute helpful commands. Supported templates include: - [**Very Good Core**](https://github.com/VeryGoodOpenSource/very_good_core): A Flutter app template with VGV best practices, including flavors, 100% test coverage, and scalable architecture. - [**Very Good Flame Game**](https://github.com/VeryGoodOpenSource/very_good_flame_game): A template for a game that uses the Flame game engine. - [**Dart CLI**](https://github.com/VeryGoodOpenSource/very_good_dart_cli): A template for a Dart Command-Line Interface. - [**Dart Package**](https://github.com/VeryGoodOpenSource/very_good_dart_package): A template for a Dart package. - [**Flutter Package**](https://github.com/VeryGoodOpenSource/very_good_flutter_package): A template for a Flutter package. - [**Docs Site**](https://github.com/VeryGoodOpenSource/very_good_docs_site): A template for a documentation site. - [**Flutter Federated Plugin**](https://github.com/VeryGoodOpenSource/very_good_flutter_plugin): A template for a plugin that supports the federated plugin architecture. #### BUILD - [**Dart Frog**](https://github.com/verygoodopensource/dart_frog): A fast, minimalistic backend framework for Dart. - [**Dart Frog Web Socket**](https://github.com/VeryGoodOpenSource/dart_frog/tree/main/packages/dart_frog_web_socket): WebSocket support for Dart Frog.  - [**CLI Completion**](https://github.com/VeryGoodOpenSource/cli_completion): Completion functionality for Dart Command-Line Interfaces built using CommandRunner. - [**Flame Behaviors**](https://github.com/VeryGoodOpenSource/flame_behaviors): Helps apply the separation of concerns to game logic in the form of entities and behaviors. - [**Formz**](https://github.com/VeryGoodOpenSource/formz): A unified form representation in Dart. - [**r13n**](https://github.com/VeryGoodOpenSource/r13n): Implement regionalization within your app. - [**Very Good Infinite List**](https://github.com/VeryGoodOpenSource/very_good_infinite_list): An infinite list widget for activity feeds, news feeds, and more. #### SCALE - [**Very Good Analysis**](https://github.com/verygoodopensource/very_good_analysis): Lint rules for Dart and Flutter used internally at VGV. - [**Very Good Coverage**](https://github.com/verygoodopensource/very_good_coverage): A GitHub Action to help enforce a specified code coverage threshold. - [**Very Good Workflows**](https://github.com/VeryGoodOpenSource/very_good_workflows): A collection of helpful, reusable GitHub workflows. One of our most popular tools is **Very Good CLI** and the templates it supports. All templates supported by our CLI provide a scalable, VGV-approved foundation for your project, whether it’s a Flutter app (core template), Dart CLI, Flutter package, federated plugin, or something else.   We’re always on the lookout for new templates; often, we’ll be working on something and ask ourselves the tried and true question: could this be abstracted to a template (that’s how the [Flame Game template got started](https://verygood.ventures/blog/generate-a-game-with-our-new-template/))? As use cases come up, we are open to adding additional templates to **Very Good CLI** — and useful commands, like [**very_good test**](https://cli.vgv.dev/docs/commands/test) (which helped the [SNCF Connect & Tech team](https://nsalleron.medium.com/flutter-pipeline-optimization-with-very-good-test-96952f671557) cut the time to run golden tests in half, and unit tests ran 6x faster). In 2023, we hope to explore template extensions. If we can figure out how to extend a template using another template, we’ll be able to better customize the developer experience and keep templates as lightweight as possible. We’ll also keep tools like **Very Good Analysis** and **Very Good Workflows** at the forefront of our thoughts. We’ll be ready to upgrade **Very Good Analysis** with any new lint rules that we want to incorporate into our projects internally at VGV. We’ll look for opportunities to add additional workflows that help automate the development process. ### Full stack Dart with Dart Frog Last May, we released [**Dart Frog**](https://dart-frog.dev/), which was started as an experimental framework for Dart on the server that leverages existing capabilities of the shelf package. A few months later, we brought [**Dart Frog** to stable with v0.1.0](https://verygood.ventures/blog/dart-frog-stable/), along with a handful of bug fixes and new features. We’re currently [working toward v1.0.0](https://github.com/dart-frog-dev/dart_frog/milestone/2), which we expect to release this year 👀 We’ve been blown away by the excitement and support for **Dart Frog** so far and plan to ramp up development on this project into 2023! To follow along as we add more features to **Dart Frog**, check out our [roadmap](https://dart-frog.dev/roadmap/).  Another area we plan to explore with **Dart Frog** is independent **Dart Frog** packages that can be added to your projects as needed. For an example of this, see our package [**Dart Frog Web Sockets**](https://pub.dev/packages/dart_frog_web_socket). Developing separate packages that work well with Dart Frog projects will allow us to keep the core **Dart Frog** package simple and lightweight, and empower you to add in what you need when you need it. In fact, this mix-and-match approach is what underlies a lot of our open source tools — we want you to use what you need when you need it, without being burdened by features that you’ll remove right away. At VGV, we’re dedicated to pursuing full stack Dart in our projects. Our goal with **Dart Frog** is to create tooling that makes it easy for developers already familiar with Dart to write backend code. We want developers to be able to focus on building apps in an efficient way using the same tooling, linting, best practices that Dart enables. With **Dart Frog**, you don’t have to context switch from Dart to Typescript or another language. You can simply tap into the features that Dart provides, as well as existing Dart packages, to write both frontend and backend code.  ### Strengthening the internal feedback loop The benefit of working at a consulting company is that we are always working on multiple projects with customers across different industries. We can use our teammates’ feedback and suggestions to drive some of what we explore and develop as an OSS and tooling team. Are there ways we can streamline development? Can we automate repeatable tasks? These are some of the questions we ask that can help inspire new tools to increase our team’s efficiency. This is how some of our existing tools originated — both **Very Good Workflows** and **r13n** are examples of recent tools that came from a need on the client services side. With **Very Good Workflows**, we were able to standardize a handful of use cases to run certain checks within CI/CD on a project in a flexible way. **R13n** came from the need for a scalable way to add region-specific text to an application. We’ll continue to look for opportunities to solve common pain points of our teammates, and if it’s a solution that would be useful to developers beyond VGV, we’ll open source it. Last year, we started hosting internal focus groups at VGV so that we can gather feedback on certain types of tools to see if there are things we can do to help improve the developer experience for those on our team (and beyond). We host weekly office hours for anyone on our team who wants to chat about tools or any other Flutter topics of interest. This year, we’ll continue these initiatives so that we can see which of our open source tools are being used and how, generate new ideas, and gather feedback in real time.  ### Other areas to explore #### Accessibility  Accessibility is one area we want to explore in 2023. The Flutter team provides [helpful documentation](https://docs.flutter.dev/development/accessibility-and-localization/accessibility) and [resources](https://youtu.be/bWbBgbmAdQs) for implementing accessibility within an application to ensure that there is sufficient text contrast, target size, semantic labels, and other best practices. We’ve been excited by recent momentum in the accessibility tooling space from Rebel App Studio with their [**accessibility_tools**](https://pub.dev/packages/accessibility_tools) package, released last month. At VGV, we want to see if we can help automate some accessibility checks; just like you can run widget tests to ensure your widgets are behaving as intended, we want to explore tooling that will make it easy to check for things like text contrast. #### Games Because two of the three full time team members are active maintainers of the Flame game engine, we will likely continue to develop tooling that makes it easier to build games with Flutter. Last year we took a deep dive into building games with Flutter, first through our work with Google on [**I/O Pinball**](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d). We also produced our **Very Good Ranch** game and shortly after, a Flame game template that can be generated from **Very Good CLI**. Stay tuned for additional explorations in this area. #### Other areas 👀 Our work will continue to follow the evolution of Flutter and Dart. As 2023 unfolds, expect us to come up with some very good ideas that even we can’t anticipate right now, with the ultimate goal of improving the developer experience. ### Keep up with the latest updates from the open source & tooling team on [**Twitter**](https://twitter.com/VGVentures) and watch for new releases on [**GitHub**](https://github.com/VeryGoodOpenSource). ### Where Flutter & VGV are Headed in 2023 Source: https://verygood.ventures/blog/where-flutter-and-vgv-are-headed-in-2023/ Last year was a huge year for Flutter. Some notable milestones include: The [release of Flutter 3.0](https://medium.com/flutter/announcing-flutter-3-3-at-flutter-vikings-6f213e068793), the arrival of the [Flutter Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit) and Flutter Vikings, which allowed the Flutter community to gather in-person for the first time in years. We also saw many companies like [ByteDance](https://youtu.be/8RmsstcNE1Y), [Tonal](https://flutter.dev/showcase/tonal), and [Superlist](https://flutter.dev/showcase/superlist) share how their teams have found success with Flutter. We're more optimistic than ever for Flutter’s upward trajectory in 2023. We imagine a future where organizations can efficiently manage an entire ecosystem of applications with the same tools, skills, and teams. Flutter and Dart enable a future where apps are not only multi-platform, but are complementary and build on shared foundations of common code and processes.   Taking a cue from the [annual Flutter strategy and roadmap](https://medium.com/flutter/flutter-in-2022-strategy-and-roadmap-8c5eaf7c4275), we wanted to provide a look back at what we accomplished in 2022, where we think the Flutter industry is heading in 2023, and how VGV will be a part of it. ## 2022 Retrospective First, a look back at some of the work we accomplished in 2022.  ## 2023 Roadmap ### Experiment #### Pushing the limits of Flutter We’ve been on the cutting edge of what Flutter can do since Day 1. VGV started out by building the [first commercial app outside of Google](https://verygood.ventures/case-studies/hamilton/), then expanded to even more projects to show the world what Flutter can do, such as creating the [first demo of Flutter on macOS](https://verygood.ventures/success-stories/flutter-slides/), [demoing the first large-scale web app with New York Times’ KENKEN](https://verygood.ventures/success-stories/new-york-times/), and more recently, building a [2D game in Flutter at Google I/O 2022](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d). Last year, we worked on over 50 Flutter projects! In 2023, we’ll continue to be on the forefront of pushing Flutter to new heights, new use cases, and new screens. We predict we’ll see more proof-of-concepts but also real applications of Flutter far beyond mobile and “traditional screens.” ### Form #### Design & Development  We’re keeping a close eye on the efforts the community is making to improve the designer and developer feedback loop. We’ve seen firsthand that using Flutter is a huge opportunity for companies to bring their brands to any and all screens in a beautiful and performant way. Companies like [Parabeac](https://parabeac.com/), [Widgetbook](https://www.widgetbook.io/), and Adobe (via the [Adobe XD plugin](https://pub.dev/packages/adobe_xd)) are actively developing solutions to make it even easier for developers and designers to collaborate, so that teams can ship Flutter apps that look beautiful and don’t compromise on design. Last year, we brought on [Josh Musick to help us expand our design expertise](https://verygood.ventures/blog/behind-the-unicorn-magic-get-to-know-the-vgv-team-josh-musick/). In 2023, we’ll continue to grow our design team, help teams design and deploy high-quality multi-platform experiences. #### Powerful Templates Templates can be a useful tool to jumpstart app development by providing useful out-of-the-box features but also establishing best practices and architectural patterns for a new codebase. The [Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit) and [Flutter News Toolkit](https://medium.com/flutter/introducing-the-flutter-news-toolkit-now-accepting-applications-9e926db55f6d) are two recent examples of high-quality Flutter templates. The former provides everything you need to get started with casual app-like games including pre-built features like a main menu screen, music, ads, leader boards, and [Firebase Crashlytics](https://firebase.google.com/products/crashlytics). The Flutter News Toolkit is currently being used by publishers to jumpstart building mobile news apps by “reducing the app development time up to 80%.” In 2023, we hope that we see even more templates hit the market with helpful out-of-the-box functionality, so that teams can focus on building unique features, instead of the basics. At VGV, we have developed a few templates that are widely used by developers around the world. Our open source tool [Very Good CLI](https://cli.vgv.dev/) can be used to generate a Flutter Starter app — all with our built-in best practices, 100% test coverage, and scalable architecture. For an enterprise-grade Flutter app foundation, check out our [Very Good Start](https://verygood.ventures/solution/very-good-start/) solution. ### Build #### Full Stack Dart Full stack Dart is becoming a real option for teams. We’re seeing an influx of pure Dart SDKs, such as the latest [Google Cloud SDK](https://pub.dev/packages/gcloud) release, [AWS Amplify](https://aws.amazon.com/about-aws/whats-new/2022/11/aws-amplify-flutter-web-desktop-developer-preview-api-analytics-storage/), and other tools that will help Flutter developers add useful functionality to their apps. We think that 2023 will be the year of Dart — and we don’t just mean in the context of Flutter. There's also been an explosion of tools for Dart on the server, such as [Serverpod](https://serverpod.dev/), [Alfred](https://pub.dev/packages/alfred), and our own tool: [Dart Frog](https://verygood.ventures/blog/dart-frog-stable/). We expect that this trend will continue, as being able to utilize Dart on both the front and backend can lead to more efficient workflows for developers and less context switching (similar to the effect the rise of server-side development had on the JavaScript ecosystem). #### Flutter on All Screens  There are [plenty of examples](https://flutter.dev/showcase) of beautiful, high-quality mobile apps built with Flutter. The most recent Flutter [user survey](https://medium.com/flutter/what-we-learned-from-the-flutter-q3-2022-survey-9b78803accd2) found that 76% of developers were using Flutter for two or more platforms, and 32% were developing for three or more platforms. In 2023, we expect to see Flutter on even more screens, including web apps, embedded devices like [Toyota’s in-vehicle infotainment system](https://flutter.dev/showcase/toyota), devices running [Fuchsia](https://fuchsia.dev/), and more! This is great news for companies that want to build beautiful digital experiences for any screen. It’s even better news for developers because being able to develop for any device (not just mobile) can open up a lot of career opportunities — you’re no longer limited by the popularity of a given device or screen. Using a tool like Flutter can unlock the super power to build for *any screen*.  ### Scale #### App Support & Growth In 2022, we started to see more solutions and maturity when it comes to supporting and growing Flutter apps that are in production. With companies like [Amplitude](https://www.docs.developers.amplitude.com/data/sdks/flutter/), [Braze](https://www.braze.com/docs/developer_guide/sdk_integration?sdktab=flutter&redirected=3), and [Sentry](https://docs.sentry.io/platforms/flutter/) developing Flutter SDKs, it’s clear that analytics performance tools are gaining ground within the Flutter community. We are proud to have contributed to this ecosystem by helping [Embrace build a Flutter SDK for their observability tools](https://verygood.ventures/blog/building-a-flutter-sdk-for-embrace/) using our best practices. We’ve also seen tools such as [LaunchDarkly](https://docs.launchdarkly.com/sdk/client-side/flutter) for feature flagging, and [Wiredash](https://wiredash.io/) for in-app feedback, gain in popularity within the Flutter ecosystem. These tools are a signal to us that the Flutter ecosystem is maturing. We expect The Flutter ecosystem to continue to grow with tools that support apps in production, making it even easier for teams to deliver top-notch experiences to users over time.  ### Transform **Organizational impact of Flutter** While Flutter is still a fairly new technology, companies that have embraced it a few years ago are commenting on how it can powerfully unify and improve teams. In the words of Will Lockwood, a Senior Software Engineer at Betterment: “Our mobile velocity has left our backend resources struggling to keep up in supporting feature development.” Check out the full [Twitter thread](https://twitter.com/deremer/status/1592207184414519296) for more on the [organizational benefits](https://verygood.ventures/success-stories/betterment/) of Flutter. At VGV we’ve observed that companies that develop with Flutter see efficiency impacts on all teams related to the product, not just the engineering team. This year, we are excited to help clients harness the power of Flutter within all of their teams — from development to design to product to project management, and beyond! Flutter is more than just a UI toolkit, it is a tool with huge transformational power.  **At Very Good Ventures, we partner with clients to build enterprise-grade experiences for any screen using Flutter.** [**Want to unlock the transformational power of Flutter for your team? Contact us →**](https://verygood.ventures/contact-us/) ### Build a Flutter app with Very Good CLI and AWS Amplify Source: https://verygood.ventures/blog/flutter-app-very-good-cli-aws-amplify/ [AWS Amplify](https://aws.amazon.com/amplify/) is a great solution to easily build and host full-stack applications for both web and mobile. It has SDKs for iOS, Android, web, React Native, and Flutter. AWS Amplify allows you to do a lot of helpful tasks when it comes to app development. For example, you can use AWS Amplify to: - **Create and connect to an app backend:** Create a backend hosted on AWS and connect it using the cross-platform Amplify Flutter library, which gives you the ability to enable real-time data interactions with offline support. - **Host a web app:** Easily deploy web app or website to the fast, secure and reliable AWS content delivery network with [AWS Amplify Hosting](https://aws.amazon.com/amplify/hosting/). - **Use pre-built authentication UI:** The AWS Authenticator gives you a ready-made UI component for your app. In this tutorial we're going to be focusing on the backend functionality by adding AWS authentication to our Flutter project using email and password. Let's get started! 🙌 ## 💡Overview The main goal of this tutorial is to create a new project with AWS Amplify that uses Very Good Ventures tools and best practices. We will create an app, enable the authentication service, and implement it in the app. This example will have a layered architecture (check out this [article](https://verygood.ventures/blog/very-good-flutter-architecture/) by my teammate Marcos Sevilla) with 100% test coverage, and our starter app will be generated by [Very Good CLI](https://cli.vgv.dev/) ✨ You're going to see how easily the Very Good Ventures tools and best practices can be implemented with AWS Amplify. ### Installation Install the AWS Amplify CLI to be able to configure the project. ```shell npm install -g @aws-amplify/cli ``` ### AWS Amplify configuration The next step is to configure AWS Amplify and create an IAM User. This user enables you to manage users and user permissions in AWS. For more detail on creating an IAM User, refer to [these docs](https://docs.amplify.aws/lib/project-setup/prereq/q/platform/flutter/#sign-up-for-an-aws-account). 1\. Run the **amplify configure** command. ```shell amplify configure ``` This command will ask you to sign into the AWS Console (you need to copy the URL that appears in the terminal). After signing in, the CLI will ask you to create an IAM user. ![Terminal showing amplify configure prompting to sign into AWS Console](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-0.png) 2\. After filling out the IAM user information you need to continue in the web browser to manage the policy **AdministratorAccess-Amplify** in your account. Note that we're providing administrative access to the account in this example for the sake of simplicity, but you should follow best practices when it comes to admin access. - Set the username (in this case I added **vgv\_amplify\_user** but you can use your own username). ![AWS IAM user creation form with username vgv_amplify_user](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-1.png) - Select **AdministratorAccess-Amplify** for this user. ![AWS IAM permission policy selection with AdministratorAccess-Amplify](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-2.png) - Check the review page and create the user. As you can see, you have important information here, like the console URL, the access key ID, and the secret access key. Don't close this page, because we need to use these keys to complete the configuration 3\. Go back to the terminal and set the keys that we saw in the last section. Set the Profile name as default. ![Terminal prompting for AWS access key ID, secret key, and profile name](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-3.png) ### AWS Amplify initialization 1\. Run the command **amplify init** locally to create your Amplify project. Then, run **amplify push**. Here we're going to fill out a few questions to set up the project. ![Terminal running amplify init with project setup questions](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-4.png) After finish the form, you should receive this success message. This means that your project is successfully initialized in the cloud. ![Terminal showing successful Amplify project initialization message](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-5.png) You can check that everything is setup correctly if you sign into your AWS account and search for **AWS Amplify**. There you are going to see your project and the creation date. ![AWS Amplify console showing the newly created project](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-6.png) 2\. The next step is to enable the [Amplify Studio](https://aws.amazon.com/amplify/studio/). With this, we're going to have an Admin UI that allows us to control the project in the cloud very easily. ![AWS Amplify Studio enable toggle in the console](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-7.png) ![Amplify Studio admin UI dashboard for the project](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-8.png) 3\. We now have a new folder in the current directory which contains all the Amplify configurations. ![Project directory showing new amplify configuration folder](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-9.png) Also, we will have a new file in the lib folder called **amplify\_configuration.json** (note that we renamed the file from the generated name: **amplifyconfiguration.json**). This file will contain the configurations of the services that we want to use (in this example, authentication). But for now, it shouldn't contain many lines, since we haven't configured authentication yet. ![amplify_configuration.json file contents in the lib folder](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-10.png) ### Add authentication to AWS Amplify We are going to use the AWS Amplify CLI to add the authentication configuration. We need to run **amplify add auth command** to start. Then we need to answer a few questions: ```shell ? Do you want to use the default authentication and security configuration? `Default configuration` ? How do you want users to be able to sign in? `Username` ? Do you want to configure advanced settings? `No, I am done.` ``` After completing these questions, it is important to run the **amplify push** command because this is going to push all the changes that we made to the cloud. After a few minutes, you should see this message in the terminal: All resources are updated in the cloud. You can check that the authentication service is deployed in Amplify Studio. ![Amplify Studio showing authentication service successfully deployed](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-11.png) If you check your **amplify\_configuration.dart** file (note that we renamed the file from the generated name: **amplifyconfiguration.dart**). You can now see that there are new lines within regarding authentication. Now, we have the authentication service deployed in the cloud, but we need to implement it in our Flutter application. ### Authentication settings in Amplify Studio Now we're going to configure the authentication settings in Amplify Studio. Here we'll be able to see password protection settings and change the body of the email that is sent to the user when the user signs up. 1. Go to Amplify Studio and select the project that you created. 2. Go to password protection settings and change the password policy. 3. Go to verification message settings and change the body of the email that is sent to the user when the user is signing up. 4. Deploy the changes. ![Amplify Studio authentication settings for password policy and verification email](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-12.png) After deploying the changes, we need to pull them. To do that, go to the terminal and run the following command at the root of your Flutter application. ```shell amplify pull --appId [YOUR_APP_ID] --envName dev ``` ![Terminal running amplify pull command with app ID and environment](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-13.png) Then you should see the following message in the terminal. ![Terminal showing successful amplify pull confirmation message](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-14.png) ### Create a new Flutter app (using the default [**Very Good Core template**](https://cli.vgv.dev/docs/templates/core)). With your AWS Amplify project now setup, we can now create the Flutter application using [Very Good CLI](https://cli.vgv.dev/). 1\. Activate Very Good CLI. ```shell dart pub global activate very_good_cli ``` 2\. Create a new Flutter project. ```shell very_good create flutter_amplify_login —desc "An example Flutter app using Very Good CLI and AWS Amplify Authentication" ``` ### Create the auth client package Now is time to create a new package called **auth\_client** in the packages folder (you should create the packages folder in the root). This package is going to contain all the code related to the authentication service. ```shell very_good create auth_client -t flutter_pkg --desc "AWS Amplify auth client" ``` We're going to use the **amplify\_auth\_cognito** package to implement the authentication service in our Flutter application. Also, we're going to use the **amplify\_flutter** package to connect our Flutter application with the AWS Amplify project. We need to add these packages to our **pubspec.yaml** file into the **auth\_client** package. Note that we're using the latest stable versions of these packages, but you can check out the developer preview versions, which [include cross-platform support for web and desktop](https://aws.amazon.com/about-aws/whats-new/2022/11/aws-amplify-flutter-web-desktop-developer-preview-api-analytics-storage/). 1\. Add the **amplify\_auth\_cognito** package to the **pubspec.yaml** file. ```yaml amplify_auth_cognito: ^0.6.9 ``` 2\. Add the **amplify\_flutter** package to the **pubspec.yaml** file. ```yaml amplify_flutter: ^0.6.9 ``` Once we have all the dependencies added, we can start with the implementation. This is the structure of the **auth\_client** package. ![File structure of the auth_client package with src and test directories](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-15.png) In the **auth\_client** file, we're going to create a class called **AuthClient** that will be the entrypoint to the authentication service. This class will have all the methods that we need to implement the authentication service: - **signUp** will sign up a new user - **confirmSignUp** will confirm the sign up of a new user - **signIn** will sign in a user - **signOut** will sign out a user - **onHubEvent** will listen to the events that are happening in the authentication service. ```dart /// Enum indicating the Authentication status enum AuthStatus { /// Authenticated session authenticated, /// Unauthenticated session unauthenticated, } /// {@template auth_client} /// AWS Amplify auth client. /// {@endtemplate} class AuthClient { /// {@macro auth_client} AuthClient({ required AuthCategory auth, required AmplifyHub hub, }) : _auth = auth, _hub = hub { _hub.listen([HubChannel.Auth], _onHubEvent); } final AuthCategory _auth; final AmplifyHub _hub; final _controller = StreamController(); /// Stream current [AuthStatus] Stream get authStatus => _controller.stream; void _onHubEvent(dynamic hubEvent) { if (hubEvent is HubEvent) { switch (hubEvent.eventName) { case 'SIGNED_IN': _controller.add(AuthStatus.authenticated); break; case 'SIGNED_OUT': case 'SESSION_EXPIRED': _controller.add(AuthStatus.unauthenticated); break; } } } /// Returns whether the user is authenticated or not /// /// Throws a [FetchAuthenticatedUserFailure] if an exception occurs. Future isUserAuthenticated() async { try { final currentSession = await _auth.fetchAuthSession(); return currentSession.isSignedIn; } catch (error, stackTrace) { Error.throwWithStackTrace( FetchAuthenticatedUserFailure(error), stackTrace, ); } } /// Creates a new user with the [email] and [password] variables. /// /// Throws a [SignUpFailure] if an exception occurs. /// Throws a [UserAlreadyExistException] if email already exists. Future signUp( String email, String password, ) async { try { final userAttributes = { CognitoUserAttributeKey.email: email, }; final options = CognitoSignUpOptions(userAttributes: userAttributes); await _auth.signUp( username: email, password: password, options: options, ); } on UsernameExistsException catch (error, stackTrace) { Error.throwWithStackTrace(UserAlreadyExistException(error), stackTrace); } catch (error, stackTrace) { Error.throwWithStackTrace(SignUpFailure(error), stackTrace); } } /// Confirm the sign up with a confirmation code. Future confirmSignUp( String email, String confirmationCode, ) async { try { await _auth.confirmSignUp( username: email, confirmationCode: confirmationCode, ); } catch (error, stackTrace) { Error.throwWithStackTrace( ConfirmationCodeSignUpFailure(error), stackTrace, ); } } /// Sign in with the provided [email] and [password]. /// /// Throws a [UserDoesNotExistException] if [UserNotFoundException] occurs /// (provided email is not correct). /// Throws a [UserDoesNotExistException] if [NotAuthorizedException] occurs. /// (provided email is correct, but the password isn't, or vice versa). /// Throws a [SignInFailure] if an exception occurs. Future signIn(String email, String password) async { try { await _auth.signIn( username: email, password: password, ); } on UserNotFoundException catch (error, stackTrace) { Error.throwWithStackTrace(UserDoesNotExistException(error), stackTrace); } on NotAuthorizedException catch (error, stackTrace) { Error.throwWithStackTrace(UserNotAuthorizedException(error), stackTrace); } catch (error, stackTrace) { Error.throwWithStackTrace(SignInFailure(error), stackTrace); } } /// Sign out the current user. /// /// Throws a [SignOutFailure] if an exception occurs. Future signOut() async { try { await _auth.signOut(); } catch (error, stackTrace) { Error.throwWithStackTrace(SignOutFailure(error), stackTrace); } } } ``` As you can see there are [custom exceptions that you can find here](https://github.com/VGVentures/flutter_amplify_login/blob/main/packages/auth_client/lib/src/exceptions.dart). ### Create the user repository package The user repository will interact with the **auth\_client** package. We're creating this package to abstract the implementations details of the Amplify **auth\_client** from the rest of our application, according to our [layered architecture](https://verygood.ventures/blog/very-good-flutter-architecture/). The structure is the following: ![File structure of the user_repository package](/assets/images/blog/flutter-app-very-good-cli-aws-amplify/body-16.png) 1\. Create a new package called **user\_repository** and add the **auth\_client** package as a dependency to the **pubspec.yaml** file: ```shell very_good create user_repository -t flutter_pkg --desc "A package which manages the user domain" ``` ```yaml dependencies: auth_client: path: ../auth_client ``` Once we have the package created, we can start working on the **user\_repository.dart** file. 2\. Create a new file called **user\_repository.dart** and add the following code: ```dart /// {@template user_repository} /// A package which manages the user domain /// {@endtemplate} class UserRepository { /// {@macro user_repository} const UserRepository({ required AuthClient authClient, }) : _authClient = authClient; final AuthClient _authClient; /// Stream of [AuthStatus] which will emit the current user status when /// the authentication state changes. /// /// Emits [AuthStatus.unauthenticated] if the user is not authenticated. Stream get authStatus => _authClient.authStatus; /// Starts the Sign Up flow. /// /// Throws a [SignUpFailure] if [AuthenticationException] occurs. /// Throws a [UserAlreadyExistException] if [UserAlreadyExistException] /// occurs. Future signUp({ required String email, required String password, }) async { try { await _authClient.signUp(email, password); } on UserAlreadyExistException catch (error, stackTrace) { Error.throwWithStackTrace(UserAlreadyExistException(error), stackTrace); } on AuthenticationException catch (error, stackTrace) { Error.throwWithStackTrace(SignUpFailure(error), stackTrace); } } /// Starts the Sign In flow. /// /// Throws a [SignInFailure] if [AuthenticationException] occurs. /// Throws a [UserDoesNotExistException] /// if [UserDoesNotExistException] occurs. Future signIn({ required String email, required String password, }) async { try { await _authClient.signIn(email, password); } on UserDoesNotExistException catch (error, stackTrace) { Error.throwWithStackTrace(UserDoesNotExistException(error), stackTrace); } on AuthenticationException catch (error, stackTrace) { Error.throwWithStackTrace(SignInFailure(error), stackTrace); } } /// Starts the Sign Out flow. /// /// Throws a [SignOutFailure] if an [AuthenticationException] occurs. Future signOut() async { try { await _authClient.signOut(); } on AuthenticationException catch (error, stackTrace) { Error.throwWithStackTrace( SignOutFailure(error), stackTrace, ); } } /// Starts the Confirmation Sign Up flow. /// /// Throws a [ConfirmationCodeSignUpFailure] if an [AuthenticationException] /// occurs. Future confirmSignUp({ required String email, required String confirmationCode, }) async { try { await _authClient.confirmSignUp(email, confirmationCode); } on AuthenticationException catch (error, stackTrace) { Error.throwWithStackTrace( ConfirmationCodeSignUpFailure(error), stackTrace, ); } } /// Returns whether the user is authenticated or not. /// /// Returns `false` if an exception occurs. Future isUserAuthenticated() async { try { return _authClient.isUserAuthenticated(); } catch (_) { return false; } } } ``` Now we have the user\_repository package ready to be used in the blocs. #### Update pubspec.yaml in the root folder We need to add the **user\_repository** package and **auth\_client** as a dependencies to the **pubspec.yaml** file in the root folder: ```yaml dependencies: auth_client: path: packages/auth_client user_repository: path: packages/user_repository ``` ### Create the needed features We're going to create the following features in the lib folder: - App - Sign in - Sign up - Sign out - Confirmation code - Home #### Create the needed blocs We're going to create different blocs for each feature. You can create a feature quickly using [**flutter_bloc_feature** brick](https://brickhub.dev/bricks/flutter_bloc_feature/0.2.0). This brick is very useful to create all the structures for our feature. To use it you need to activate mason, if you don't know what mason is, you can [find more information about it](https://docs.brickhub.dev/). Let's get started! 🙌 - **app\_bloc.dart**: This bloc will be responsible for the authentication flow. It will listen to the authStatus stream and emit the correct state. Additionally, it will handle the sign-out method. ```dart class AppBloc extends Bloc { AppBloc({ required UserRepository userRepository, required bool isAuthenticated, }) : _userRepository = userRepository, super( isAuthenticated ? const AppState.authenticated() : const AppState.unauthenticated(), ) { on(_onAuthStatusChanged); on(_onSignOutRequested); _authStatusSubscription = _userRepository.authStatus.listen( _authStatusChanged, ); } final UserRepository _userRepository; late StreamSubscription _authStatusSubscription; void _authStatusChanged(AuthStatus authStatus) => add(AppAuthStatusChanged(authStatus)); void _onAuthStatusChanged( AppAuthStatusChanged event, Emitter emit, ) { switch (event.authStatus) { case AuthStatus.authenticated: emit(const AppState.authenticated()); break; case AuthStatus.unauthenticated: emit(const AppState.unauthenticated()); break; case AuthStatus.sessionExpired: emit(const AppState.sessionExpired()); break; } } void _onSignOutRequested(AppSignOutRequested event, Emitter emit) { unawaited(_userRepository.signOut()); } @override Future close() { _authStatusSubscription.cancel(); return super.close(); } } ``` You can check the **app\_event** and the **app\_state** [files here](https://github.com/VGVentures/flutter_amplify_login/tree/main/lib/app/bloc). - **sign\_in\_bloc.dart**: This bloc will be responsible for the sign-in flow. It will listen to the **SignInEvent** event and emit the correct state. Additionally, it will have events to update the state of the form. As you can see in here, we are using **Formz** to handle the form state. **Formz** is a package that provides a set of classes that can be used to handle the state of a form. There is a package called **form\_inputs** into packages folder. This package is a wrapper of **Formz** that provides some custom inputs that can be used in the forms. [You can find it here](https://github.com/VGVentures/flutter_amplify_login/tree/main/packages/form_inputs). ```dart class SignInBloc extends Bloc { SignInBloc({ required UserRepository userRepository, }) : _userRepository = userRepository, super(const SignInState()) { on(_onEmailChanged); on(_onPasswordChanged); on(_onSignInSubmitted); on(_onSignInPasswordVisibilityToggled); } final UserRepository _userRepository; void _onEmailChanged( SignInEmailChanged event, Emitter emit, ) { final email = Email.dirty(event.email); emit( state.copyWith( status: SignInStatus.edit, email: email, isValid: Formz.validate([email, state.password]), ), ); } void _onPasswordChanged( SignInPasswordChanged event, Emitter emit, ) { final password = Password.dirty(event.password); emit( state.copyWith( status: SignInStatus.edit, password: password, isValid: Formz.validate([password, state.email]), ), ); } Future _onSignInSubmitted( SignInSubmitted event, Emitter emit, ) async { if (!state.isValid) return; emit(state.copyWith(status: SignInStatus.loading)); try { await _userRepository.signIn( email: event.email, password: event.password, ); emit(state.copyWith(status: SignInStatus.success)); } catch (error, stackTrace) { emit(state.copyWith(status: SignInStatus.failure)); addError(error, stackTrace); } } Future _onSignInPasswordVisibilityToggled( SignInPasswordVisibilityToggled event, Emitter emit, ) async { emit(state.copyWith(isObscure: !state.isObscure)); } } ``` You can check the **sign\_in\_event** and the **sign\_in\_state** [files here](https://github.com/VGVentures/flutter_amplify_login/tree/main/lib/sign_in/bloc). - **sign\_up\_bloc.dart**: This bloc will be responsible for the sign-up flow. It will listen to the **SignUpEvent** event and emit the correct state. Additionally, it will have events to update the state of the form. ```dart class SignUpBloc extends Bloc { SignUpBloc({ required UserRepository userRepository, }) : _userRepository = userRepository, super(const SignUpState()) { on(_onEmailChanged); on(_onPasswordChanged); on(_onSignUpSubmitted); on(_onSignUpPasswordVisibilityToggled); } final UserRepository _userRepository; void _onEmailChanged( SignUpEmailChanged event, Emitter emit, ) { final email = Email.dirty(event.email); emit( state.copyWith( email: email, isValid: Formz.validate([email, state.password]), ), ); } void _onPasswordChanged( SignUpPasswordChanged event, Emitter emit, ) { final password = Password.dirty(event.password); emit( state.copyWith( password: password, isValid: Formz.validate([password, state.email]), ), ); } Future _onSignUpSubmitted( SignUpSubmitted event, Emitter emit, ) async { if (!state.isValid) return; emit(state.copyWith(status: SignUpStatus.loading)); try { await _userRepository.signUp( email: event.email, password: event.password, ); emit(state.copyWith(status: SignUpStatus.success)); } catch (error, stackTrace) { emit(state.copyWith(status: SignUpStatus.failure)); addError(error, stackTrace); } } Future _onSignUpPasswordVisibilityToggled( SignUpPasswordVisibilityToggled event, Emitter emit, ) async { emit(state.copyWith(isObscure: !state.isObscure)); } } ``` You can check the **sign\_up\_event** and the **sign\_up\_state** [files here](https://github.com/VGVentures/flutter_amplify_login/tree/main/lib/sign_up/bloc). - **confirm\_sign\_up\_bloc.dart**: This bloc will be responsible for the sign-up flow confirmation. It will listen to the **ConfirmationCodeEvent** event and emit the correct state. Additionally, it will have events to update the state of the form. ```dart class ConfirmationCodeBloc extends Bloc { ConfirmationCodeBloc({ required UserRepository userRepository, }) : _userRepository = userRepository, super(const ConfirmationCodeState()) { on(_onConfirmationCodeChanged); on(_onConfirmationCodeSubmitted); } final UserRepository _userRepository; void _onConfirmationCodeChanged( ConfirmationCodeChanged event, Emitter emit, ) { final confirmationCode = ConfirmationCode.dirty(event.confirmationCode); emit( state.copyWith( confirmationCode: confirmationCode, isValid: Formz.validate([confirmationCode]), ), ); } Future _onConfirmationCodeSubmitted( ConfirmationCodeSubmitted event, Emitter emit, ) async { if (!state.isValid) return; emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); try { await _userRepository.confirmSignUp( email: event.email, confirmationCode: event.confirmationCode, ); emit(state.copyWith(status: FormzSubmissionStatus.success)); } catch (error, stackTrace) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error, stackTrace); } } } ``` You can check the **confirmation\_code\_event** and the **confirmation\_code\_state** [files here](https://github.com/VGVentures/flutter_amplify_login/tree/main/lib/confirmation_code/bloc). #### Create the UI in each feature Now that we have the blocs ready, we can start creating the UI for each feature. ##### Sign-in - **sign\_in\_page.dart**: This is the page for the sign-in feature. It will be responsible for creating the bloc and the **SignInView** widget, as well as initializing the **SignInBloc**. ```dart class SignInPage extends StatelessWidget { const SignInPage({super.key}); static Page page() => const MaterialPage(child: SignInPage()); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => SignInBloc( userRepository: context.read(), ), child: const SignInView(), ); } } ``` - **sign\_in\_view.dart**: This is the view of for the sign-in feature. It will be responsible for creating the UI and interacting with the bloc to sign in and check the status of the form. ```dart class SignInView extends StatelessWidget { const SignInView({super.key}); @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { if (state.status == SignInStatus.failure) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Something went wrong. Try it later or sign up.'), ), ); } }, child: Scaffold( appBar: AppBar( title: const Text('Sign In'), ), body: const Padding( padding: EdgeInsets.symmetric( horizontal: AppSpacing.xlg, ), child: ScrollableColumn( children: [ SizedBox(height: AppSpacing.xxlg), _AWSLogo(), SizedBox(height: AppSpacing.xxlg), _TitleAndSubtitleSignIn(), SizedBox(height: AppSpacing.xxlg), _EmailTextFieldSignIn(), SizedBox(height: AppSpacing.lg), _PasswordFieldSignIn(), SizedBox(height: AppSpacing.lg), _SignInButton(), Spacer(), _NotAccountSignIn(), SizedBox(height: AppSpacing.xxlg), ], ), ), ), ); } } class _AWSLogo extends StatelessWidget { const _AWSLogo(); @override Widget build(BuildContext context) { return Assets.images.awsLogo.image( key: const Key('signIn_awsLogo'), width: 200, ); } } class _TitleAndSubtitleSignIn extends StatelessWidget { const _TitleAndSubtitleSignIn(); @override Widget build(BuildContext context) { final theme = Theme.of(context).textTheme; return Column( children: [ Text( key: const Key('signIn_title'), 'Very Good Amplify Login', style: theme.headline5, textAlign: TextAlign.center, ), Text( key: const Key('signIn_subtitle'), 'By Very Good Ventures', style: theme.caption, textAlign: TextAlign.center, ), ], ); } } class _EmailTextFieldSignIn extends StatelessWidget { const _EmailTextFieldSignIn(); @override Widget build(BuildContext context) { return AppTextField( key: const Key('signIn_emailTextField'), hintText: 'Email', autoFillHints: const [AutofillHints.email], keyboardType: TextInputType.emailAddress, prefix: const Icon(Icons.email), onChanged: (email) => context.read().add( SignInEmailChanged(email), ), ); } } class _PasswordFieldSignIn extends StatelessWidget { const _PasswordFieldSignIn(); @override Widget build(BuildContext context) { final isObscure = context.select((SignInBloc bloc) => bloc.state.isObscure); return AppTextField( key: const Key('signIn_passwordTextField'), hintText: 'Password', autoFillHints: const [AutofillHints.password], obscureText: isObscure, prefix: const Icon(Icons.lock), suffix: IconButton( icon: Icon(isObscure ? Icons.visibility : Icons.visibility_off), onPressed: () => context.read().add( const SignInPasswordVisibilityToggled(), ), ), onChanged: (password) => context.read().add( SignInPasswordChanged(password), ), ); } } class _NotAccountSignIn extends StatelessWidget { const _NotAccountSignIn(); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Padding( padding: const EdgeInsets.only(top: AppSpacing.sm), child: RichText( key: const Key('signIn_notAccountButton'), text: TextSpan( children: [ TextSpan( text: "Don't have an account? ", style: theme.textTheme.bodyText1, ), TextSpan( text: 'Sign Up!', style: theme.textTheme.bodyText1?.apply( color: AppColors.primary, ), recognizer: TapGestureRecognizer() ..onTap = () => Navigator.of(context).push( SignUpPage.route(), ), ), ], ), ), ); } } class _SignInButton extends StatelessWidget { const _SignInButton(); @override Widget build(BuildContext context) { final state = context.watch().state; return AppButton( key: const Key('signIn_signInButton'), onPressed: state.isValid ? () => context.read().add( SignInSubmitted( state.email.value, state.password.value, ), ) : null, child: state.status == SignInStatus.loading ? const CircularProgressIndicator() : const Text('Sign In'), ); } } ``` ##### Sign-up - **sign\_up\_page.dart**: This is the page that will be displayed when the user clicks on the Sign Up button. It will be responsible for displaying the **SignUpView** widget and providing the **SignUpBloc** to it. ```dart class SignUpPage extends StatelessWidget { const SignUpPage({super.key}); static Route route() { return MaterialPageRoute( builder: (_) => const SignUpPage(), ); } @override Widget build(BuildContext context) { return BlocProvider( create: (context) => SignUpBloc(userRepository: context.read()), child: const SignUpView(), ); } } ``` - **sign\_up\_view.dart**: This view will be responsible for displaying the sign-up form and handling the user interactions with it. Also, it is going to be in charge of the **SignUpBloc** which handles the sign-up process. ```dart class SignUpView extends StatelessWidget { const SignUpView({super.key}); @override Widget build(BuildContext context) { return BlocListener( listenWhen: (previous, current) => previous.status != current.status, listener: (context, state) async { if (state.status == SignUpStatus.failure) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Something went wrong. Try it later.'), ), ); } else if (state.status == SignUpStatus.success) { final navigator = Navigator.of(context); await showMaterialModalBottomSheet( context: context, builder: (context) => ConfirmationCodePage( email: state.email.value, ), ); // Navigate back when the confirmation code is successful to return to // the sign-in page. navigator.pop(); } }, child: Scaffold( appBar: AppBar( title: const Text('Sign Up'), ), body: const Padding( padding: EdgeInsets.symmetric( horizontal: AppSpacing.xlg, ), child: ScrollableColumn( children: [ SizedBox(height: AppSpacing.xxxlg), _SignUpAWSLogo(), SizedBox(height: AppSpacing.xxlg), _SignUpTitleAndVGVLogo(), SizedBox(height: AppSpacing.xxlg), _EmailTextFieldSignUp(), _PasswordFieldSignUp(), Spacer(), _SignUpButton(), SizedBox(height: AppSpacing.xxlg), ], ), ), ), ); } } class _SignUpAWSLogo extends StatelessWidget { const _SignUpAWSLogo(); @override Widget build(BuildContext context) { return Assets.images.awsLogo.image( key: const Key('signUp_awsLogo'), width: 200, ); } } class _SignUpTitleAndVGVLogo extends StatelessWidget { const _SignUpTitleAndVGVLogo(); @override Widget build(BuildContext context) { final theme = Theme.of(context).textTheme; return Column( children: [ Assets.images.vgvLogo.image( key: const Key('signUp_vgvLogo'), width: 150, ), const SizedBox(height: AppSpacing.xxlg), Text( key: const Key('signUp_title'), 'Fill out the form to Sign Up', style: theme.headline5, textAlign: TextAlign.center, ), ], ); } } class _EmailTextFieldSignUp extends StatelessWidget { const _EmailTextFieldSignUp(); @override Widget build(BuildContext context) { return AppTextField( key: const Key('signUp_emailTextField'), hintText: 'Email', autoFillHints: const [AutofillHints.email], keyboardType: TextInputType.emailAddress, prefix: const Icon(Icons.email), onChanged: (email) => context.read().add( SignUpEmailChanged(email), ), ); } } class _PasswordFieldSignUp extends StatelessWidget { const _PasswordFieldSignUp(); @override Widget build(BuildContext context) { final isObscure = context.select((SignUpBloc bloc) => bloc.state.isObscure); return AppTextField( key: const Key('signUp_passwordTextField'), hintText: 'Password', autoFillHints: const [AutofillHints.password], obscureText: isObscure, prefix: const Icon(Icons.lock), suffix: IconButton( icon: Icon(isObscure ? Icons.visibility : Icons.visibility_off), onPressed: () => context.read().add( const SignUpPasswordVisibilityToggled(), ), ), onChanged: (password) => context.read().add( SignUpPasswordChanged(password), ), ); } } class _SignUpButton extends StatelessWidget { const _SignUpButton(); @override Widget build(BuildContext context) { final state = context.watch().state; return AppButton( key: const Key('signUp_signUpButton'), onPressed: state.isValid ? () => context.read().add( SignUpSubmitted( state.email.value, state.password.value, ), ) : null, child: state.status == SignUpStatus.loading ? const CircularProgressIndicator() : const Text('Sign Up'), ); } } ``` You can see that we're using a **showMaterialModalBottomSheet** to show the confirmation code page. This is possible thanks to the **modal\_bottom\_sheet** package, created by our teammate Jaime Blasco, you can find more types of modal bottom sheets in the [package](https://pub.dev/packages/modal_bottom_sheet) documentation. ##### Confirmation Code - **confirmation\_code\_page.dart:** This page will be responsible for displaying the confirmation code form and handling the user interactions with it. ```dart class ConfirmationCodePage extends StatelessWidget { const ConfirmationCodePage({super.key, required this.email}); final String email; @override Widget build(BuildContext context) { return BlocProvider( create: (context) => ConfirmationCodeBloc( userRepository: context.read(), ), child: ConfirmationCodeForm(email: email), ); } } ``` - **confirmation\_code\_form.dart**: This widget will be responsible for displaying the confirmation code form modal and handling the user interactions with it. It will also be in charge of the **ConfirmationCodeBloc** which handles the confirmation code process. ```dart class ConfirmationCodeForm extends StatelessWidget { const ConfirmationCodeForm({ super.key, required this.email, }); final String email; @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { if (state.status == FormzSubmissionStatus.success) { Navigator.of(context).pop(); } }, child: Padding( key: const Key('confirmationCodeForm_confirmationCodeModal'), padding: MediaQuery.of(context).viewInsets.copyWith( left: AppSpacing.lg, right: AppSpacing.lg, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: AppSpacing.xxlg), _ConfirmationCodeTitle(email: email), const SizedBox(height: AppSpacing.xxlg), const _ConfirmationCodeTextFieldSignUp(), const SizedBox(height: AppSpacing.xxlg), _ConfirmationCodeButton(email: email), const SizedBox(height: AppSpacing.xxlg), ], ), ), ); } } class _ConfirmationCodeTitle extends StatelessWidget { const _ConfirmationCodeTitle({ required this.email, }); final String email; @override Widget build(BuildContext context) { final theme = Theme.of(context).textTheme; return Text( key: const Key('confirmationCodeForm_title'), 'Add the confirmation code \nreceived on $email', style: theme.bodyMedium, ); } } class _ConfirmationCodeTextFieldSignUp extends StatelessWidget { const _ConfirmationCodeTextFieldSignUp(); @override Widget build(BuildContext context) { final state = context.watch().state; return AppTextField( key: const Key('confirmationCode_codeTextField'), hintText: 'Confirmation code', keyboardType: TextInputType.number, prefix: const Icon(Icons.numbers), errorText: state.status.isFailure ? 'Invalid code' : null, onChanged: (code) => context.read().add( ConfirmationCodeChanged(code), ), ); } } class _ConfirmationCodeButton extends StatelessWidget { const _ConfirmationCodeButton({ required this.email, }); final String email; @override Widget build(BuildContext context) { final state = context.watch().state; return AppButton( key: const Key('confirmationCodeForm_confirmationCodeButton'), onPressed: state.isValid ? () => context.read().add( ConfirmationCodeSubmitted( email, state.confirmationCode.value, ), ) : null, child: const Text('Confirm code'), ); } } ``` ##### App - **app.dart**: This page will initialize the **AppBloc** and the **UserRepository**. We will use a boolean to check if the user is authenticated or not. This widget will also be responsible for displaying the **app\_view** widget. ```dart class App extends StatelessWidget { const App({ super.key, required UserRepository userRepository, required bool isAuthenticated, }) : _userRepository = userRepository, _isAuthenticated = isAuthenticated; final UserRepository _userRepository; final bool _isAuthenticated; @override Widget build(BuildContext context) { return RepositoryProvider.value( value: _userRepository, child: BlocProvider( create: (context) => AppBloc( userRepository: _userRepository, isAuthenticated: _isAuthenticated, ), child: const AppView(), ), ); } } ``` - **app\_view.dart**: This widget will be responsible for displaying the correct view depending on the **auth\_state**. To control which view should be displayed, we're going to use [**FlowBuilder**](https://pub.dev/packages/flow_builder) and the status store in the **AppBloc**. ```dart class AppView extends StatelessWidget { const AppView({super.key}); @override Widget build(BuildContext context) { return MaterialApp( themeMode: ThemeMode.light, theme: const AppTheme().themeData, darkTheme: const AppDarkTheme().themeData, localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, ], supportedLocales: AppLocalizations.supportedLocales, home: FlowBuilder( state: context.select((AppBloc bloc) => bloc.state.status), onGeneratePages: onGenerateAppViewPages, ), ); } } ``` We can have three possible states to display the needed view: - **AppStatus.authenticated**: This state will be displayed when the user is authenticated. In this case we're going to display the **HomePage** widget. - **AppStatus.unauthenticated**: This state will be displayed when the user is not authenticated. In this case we're going to display the **SignInPage** widget. - **AppStatus.sessionExpired**: This state will be displayed when the user session has expired. In this case we're going to display the **SignInPage** widget. ```dart List> onGenerateAppViewPages( AppStatus state, Listdynamic>> pages, ) { switch (state) { case AppStatus.unauthenticated: return [SignInPage.page()]; case AppStatus.authenticated: return [HomePage.page()]; case AppStatus.sessionExpired: return [SignInPage.page()]; } } ``` ##### HomePage - **home\_page.dart**: This page will be displayed when the user is authenticated. In this page we're going to display the **home\_view** widget. ```dart class HomePage extends StatelessWidget { const HomePage({super.key}); static Page page() => const MaterialPage(child: HomePage()); @override Widget build(BuildContext context) { return const HomeView(); } } ``` - **home\_view.dart**: This view is the last view that we're going to display. If the user can see this view it means that the user is authenticated. Here, we're have the possibility to sign out the user. ```dart class HomeView extends StatelessWidget { const HomeView({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Home'), ), body: const Padding( padding: EdgeInsets.symmetric( horizontal: AppSpacing.lg, ), child: ScrollableColumn( children: [ SizedBox(height: AppSpacing.xxxlg), _AppLogos(), SizedBox(height: AppSpacing.xxxlg), _TitleAndSubtitle(), Spacer(), _SignOutButton(), SizedBox(height: AppSpacing.xxlg), ], ), ), ); } } class _AppLogos extends StatelessWidget { const _AppLogos(); @override Widget build(BuildContext context) { return Row( key: const Key('homeView_appLogos'), mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Assets.images.vgvLogo.image(width: 200), Assets.images.awsLogo.image(width: 100), ], ); } } class _TitleAndSubtitle extends StatelessWidget { const _TitleAndSubtitle(); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( key: const Key('homeView_titleAndSubtitle'), crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Welcome to Very Good Amplify Login App', style: theme.textTheme.headline4, ), const SizedBox(height: AppSpacing.lg), Text( 'Made with 💙 by Very Good Ventures 🦄 and AWS Amplify.', style: theme.textTheme.headline6, ), const SizedBox(height: AppSpacing.xxlg), Text( 'Congratulations, you followed the tutorial and ' 'successfully logged in! Now you can try out the sign-out.', style: theme.textTheme.bodyText1, ), ], ); } } class _SignOutButton extends StatelessWidget { const _SignOutButton(); @override Widget build(BuildContext context) { return AppButton( key: const Key('homeView_signOutButton'), child: const Text('Sign Out'), onPressed: () => context.read().add(const AppSignOutRequested()), ); } } ``` #### Add configure Amplify method to the bootstrap file Now we're going to add the **configureAmplify** method to the **bootstrap.dart** file. This method will be responsible for configuring the Amplify plugins and the **AmplifyAuthCognito** plugin. ```dart Future configureAmplify() async { await Amplify.addPlugins([AmplifyAuthCognito()]); await Amplify.configure(amplifyConfiguration); } ``` Call it into bootstrap method. ```dart Future bootstrap(FutureOr Function() builder) async { WidgetsFlutterBinding.ensureInitialized(); await configureAmplify(); // <--- Add this line FlutterError.onError = (details) { log(details.exceptionAsString(), stackTrace: details.stack); }; Bloc.observer = AppBlocObserver(); await runZonedGuarded( () async => runApp(await builder()), (error, stackTrace) => log(error.toString(), stackTrace: stackTrace), ); } ``` ## Conclusion We've seen how easy is to use AWS Amplify and implement a sign-in and sign-up flow in a Flutter application using the **AmplifyAuthCognito** plugin and the Very Good Ventures tools and best practices. One thing we appreciate about using AWS Amplify is that it's very well-documented! In addition to authentication, AWS Amplify provides you with other utilities such as: - Data - Storage - Functions - GraphQL API - REST API - Analytics - Predictions - UI Library If you are thinking to build an application that requires authentication, data modeling, storage and more. AWS Amplify is a great option. To start working with AWS Amplify I highly recommend you to read the [AWS Amplify documentation](https://docs.amplify.aws/console/) and the [AWS Amplify Flutter documentation](https://docs.amplify.aws/start/q/integration/flutter/). For any issues, see the [AWS Amplify GitHub repository](https://github.com/aws-amplify/amplify-flutter). Hope you enjoyed it! If you are interested in testing this application, check out our [GitHub repository](https://github.com/VGVentures/flutter_amplify_login) with this example. Happy coding!💻 ### [**View the full source code on GitHub →**](https://github.com/VGVentures/flutter_amplify_login) ### Building a Flutter SDK for Embrace Source: https://verygood.ventures/blog/building-a-flutter-sdk-for-embrace/ Enterprises building mobile apps at scale want to know how people are using their app: Is it crashing? Where are users spending the most time? What requests is the app making to the backend? Observability tools can help you answer these answers.  One example is [Embrace](https://embrace.io/), a leading enterprise mobile observability platform and a VGV partner. With existing SDKs for Unity, React Native, iOS, and Android, today we are excited to announce the [beta release of the Embrace Flutter SDK](https://blog.embrace.io/embrace-flutter-sdk/)! ## Embrace expands into Flutter The Embrace product and engineering teams have no shortage of mobile, observability, and SDK expertise. As their customers, including mutual VGV and Embrace customer Slickdeals, increasingly asked for Flutter support, Embrace knew it was time to turn their attention to developing an SDK for Flutter. But they were looking for a partner with Flutter expertise to help build a solid SDK, so, they turned to our team at Very Good Ventures. ### Building a beta in three weeks with our best practices Embrace came to VGV with a Proof of Concept (POC) for the Flutter SDK that proved feasibility but, not surprisingly for a POC, lacked many of the best practices and standards that VGV recommends. Initially, we worked with the existing POC structure to make some initial improvements. Once we reached a stable point, we regenerated the plugin using [**very_good_cli**](https://cli.vgv.dev/) and migrated to the new project structure. As we’ve [written about before](https://verygood.ventures/blog/generate-command-line-application-cli/), **very\_good\_cli** speeds up what would normally be the tedious project setup phase. We used the **very\_good\_cli** create command with the **flutter\_plugin** template option to generate a solid foundation for this SDK. ```shell very_good create my_plugin -t flutter_plugin ``` The original Embrace POC had around 5% test coverage, as they were not initially focused on tests and were simply trying to demonstrate technical feasibility. Our [**very_good_coverage**](https://verygood.ventures/blog/very-good-coverage/) tool can be helpful when it comes to enforcing code coverage metrics over time. By default, the minimum coverage threshold is 100%, but you can customize that based on your team's needs. Once the team overcame the initial concerns and gained confidence that high test coverage was feasible, we switched gears and made sure that the codebase was well-tested. Now, the SDK has 100% test coverage — meaning that every single line of code is exercised at least once. While this doesn't guarantee the codebase is bug-free, it is one measure of quality and increases confidence in the functionality of the SDK. Overall, we were able to release a beta in about three weeks due to: - **Embedded peer programming:** Our teammates worked with engineers at Embrace on code reviews and architecture discussions to ensure that everyone was on the same page throughout the project. These pair programming sessions are a great way to get quick feedback and ensure everyone is aligned. - **Our open source tools:** Using **very\_good\_cli** allowed us to generate a solid federated plugin with a single command, which greatly sped up the project timeline, compared to if we had started from scratch. - **Embrace's deep observability expertise:** Because the Embrace team has existing SDKs in other ecosystems, they were able to bring their mobile observability knowledge to the project and ensure that the Flutter SDK we were creating would meet their users' needs. ### Making the Embrace SDK \_Fluttery \_with familiar APIs An important part of this project was ensuring that the SDK would feel familiar to Flutter developers. One of these elements involved structuring APIs to be familiar to developers who are working with plugins. For example, do you set up a singleton with a bunch of static APIs? — e.g. EmbracePlatform.instance.logError — or instead do you implement top level functions such as logError? This is the type of question that we discussed early on in order to ensure the final result aligned with what Flutter developers expect. For this SDK, we aligned with approaches used in other Flutter plugins and went with the singleton and static API approach.  For decisions that impact developer experience, we referenced similar SDKs in the ecosystem to make sure that when a developer integrates this SDK into a Flutter codebase, it feels natural. The ultimate goal is for a developer to look at the Embrace SDK and say: "Oh yeah, I've done this before. I've seen or used other observability tools and the API makes sense.” Generally, when you're developing across multiple frameworks and languages, it is important to avoid tunnel vision: “We have this existing implementation and it works well, so let’s do the exact same thing in this other ecosystem." In our experience, this rarely produces the best results. There are many things that make sense to do in iOS, Android, or other contexts, but don't translate well when using Flutter due to conceptual differences. One example in which we focused on designing APIs specifically for Flutter was tracking page views and moments. Knowing how many times a user saw or interacted with a page is important for many developers and product professionals trying to optimize their app's performance. While there are a variety of different mechanisms to accomplish these things across iOS, Android, and Flutter, the obvious choice for Flutter was using the NavigatorObserver API to track page view events by overriding didPush, didPop, and didReplace. ```dart @override void didPush(Route route, Route? previousRoute) { super.didPush(route, previousRoute); _updateView(route, previousRoute); } @override void didReplace({Route? newRoute, Route? oldRoute}) { super.didReplace(newRoute: newRoute, oldRoute: oldRoute); _updateView(newRoute, oldRoute); } @override void didPop(Route route, Route? previousRoute) { super.didPop(route, previousRoute); _updateView(previousRoute, route); } ``` ### Federated plugin architecture With Embrace, we implemented a [federated plugin architecture](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#federated-plugins). The benefits of this approach are numerous: flexibility, ease of adoption, ease of maintenance, facilitating community contribution, and an abstraction layer between the logic and the platform-specific implementations. This allows authors to define an interface, implement support for whatever platforms they want, and the community can gradually contribute support for new platforms without requiring changes to the plugin interface. Currently, the Embrace SDK only supports Android and iOS. But with the federated plugin architecture, it will be easy to add support for other platforms in the future, without overhauling any of the existing plugin code. ### Using integration tests with federated plugins The federated plugin architecture also allows you to test, instrument, and set up automations for each of the platforms independently of the others. This helps with maintenance and the general CI/CD setup because everything can be tested and worked on independently. This can be particularly beneficial for larger companies that have different teams working on different platform implementations without stepping on each other's toes. The [**very_good_cli** plugin template](https://verygood.ventures/blog/generate-flutter-plugins-with-very-good-cli/) also includes integration tests, which can come in handy when testing a plugin. These workflows make sure we're testing the platform-specific implementations on the respective platforms. For example, if a change is made to the iOS implementation of the plugin, these workflows would spin up an iOS environment and run integration tests to verify the plugin functions as expected on iOS. This gives us an extra boost of confidence that the SDK is performing as intended without the need to constantly perform manual tests. ## Using the Embrace SDK for comprehensive observability metrics When it comes to observability and crash reporting with Flutter apps, there historically have been only a couple of solutions. There are existing observability tools, but most tend to focus on one specific area, like crash reporting, logging, or user tracking. Flutter makes observability extra challenging because applications built with Flutter can be deployed to many platforms. As the ecosystem stands right now, you would need to research and integrate several different tools to get at the full spectrum of app performance the way Embrace does. In addition to crash reporting, the Embrace Flutter SDK currently offers network monitoring, logging, and custom user flows (moments). With the addition of the Embrace SDK to the Flutter ecosystem, developers now have another comprehensive observability solution. ### [**Get started with the Embrace Flutter SDK here →**](https://pub.dev/packages/embrace) ### Generate a game foundation with our new template Source: https://verygood.ventures/blog/generate-a-game-with-our-new-template/ Our team is all about streamlining processes so that we can spend time on the fun parts of development. We love [“boring” code](https://verygood.ventures/blog/boring-code-part-1/), which means we try to standardize our projects and processes where possible. To help us in this endeavor, we have created a few open source templates accessible via our [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli) to give us a scalable base that we can immediately build upon, instead of wasting time on project setup. Very Good CLI currently supports templates for a Flutter Starter App (Core), Flutter package, Dart package, Dart CLI, and federated plugin. Today we announce the addition of a game starter template! ![Very Good CLI terminal output generating a Flame game project](/assets/images/blog/generate-a-game-with-our-new-template/body-0.png) ## Why we built the Very Good Game template We started building the Flame game template a few months ago, and then took a detour to work on [Very Good Ranch](https://verygood.ventures/blog/very-good-ranch-game/), a game we built with Flutter and Flame that allows you to collect unicorns at your own ranch. In building a real, playable game, we were able to standardize how we might build a game in a way that mirrors our “boring” approach to building apps. We then took what we learned from Very Good Ranch and used it to inform the design of our game template. We built the Very Good Game template with one question in mind: “Would you actually start with this if you were building a game?” We kept it simple and the feature set pared down because we wanted the template to be a useful foundation without you needing to rip a bunch of stuff out. Most of the code in the template is project setup, and the code responsible for the actual gameplay is less than 100 lines in total. As with our other templates, once you generate your project, everything is entirely customizable. Feel free to remove things, or update them as you see fit. The template is simply a starting place for how we at VGV would structure a game. ## What’s included The game template includes a simple demo game with the basics you'll need for game development and VGV-opinionated best practices. It has the following features: ✅ **Components** - Think of them as game objects, or anything that can render in a game. ✅ **Entity and Behaviors** - Entities are what manage the game objects and the behaviors handle the game logic for those objects.  ✅ **Sprite Sheets** - Easily access and render sprites on the screen. ✅ **Audio** - Background music and sound effects within the game. ✅ **VGV Project Architecture** - This project contains a similar architecture to other VGV projects (see our [core starter app](https://github.com/VeryGoodOpenSource/very_good_core/tree/main)). ✅ **100% Test Coverage** — Each line is executed at least once by a test. ## Demo game The template includes a simple game with the above features. After launching the app, you’ll first see a loading screen as all game assets are loading in the background. ![Game template loading screen with progress bar](/assets/images/blog/generate-a-game-with-our-new-template/body-1.png) *template game loading screen* Then you’ll reach the title page, which includes the game name and the start button. Clicking the start button will start the game: tap the unicorn to display an animation and update the counter. Background music will play in the background, and on each tap, a sound effect plays. To mute it, press the button in the top right.  Note that there is currently no “win” state, as some games may not be able to be “won” by design. Again, we wanted to keep the template simple and provide the basics for game development, without adding too much that you would need to remove to implement your own game.  ![Unicorn tap game with score counter and mute button](/assets/images/blog/generate-a-game-with-our-new-template/body-2.png) *demo game* ## How to use the Very Good Game template First, make sure you have Very Good CLI installed. Then, use the **very\_good create** command to generate your Flame game: ```shell very_good create my_game -t flame_game --desc "My new Flame game" ``` ### A closer look at the template code #### Sprite sheets and animations The **Unicorn** entity has a sprite animation, which displays when the unicorn gets tapped and displays the sunglasses falling down over its eyes. Shoutout to our teammate Erick Zanardo for creating this animated unicorn! ![Unicorn sprite sheet with sunglasses animation frames](/assets/images/blog/generate-a-game-with-our-new-template/body-3.png) *unicorn sprite sheet* You can see how the spite sheets are implemented within the game template here: ```dart … SpriteAnimation? _animation; @visibleForTesting SpriteAnimation get animation => _animation!; @override Future onLoad() async { _animation = await gameRef.loadSpriteAnimation( Assets.images.unicornAnimation.path, SpriteAnimationData.sequenced( amount: 16, stepTime: 0.1, textureSize: Vector2.all(32), loop: false, ), ); resetAnimation(); animation.onComplete = resetAnimation; await add(SpriteAnimationComponent(animation: _animation, size: size)); } /// Set the animation to the first frame by tricking the animation /// into thinking it finished the last frame. void resetAnimation() { animation ..currentIndex = _animation!.frames.length - 1 ..update(0.1) ..currentIndex = 0; } /// Plays the animation. void playAnimation() => animation.reset(); /// Returns whether the animation is playing or not. bool isAnimationPlaying() => !animation.isFirstFrame; ``` #### Using flame\_behaviors We are using **flame\_behaviors** to define our game objects and their behaviors. For this template, we have a single **Unicorn** entity that has a **TappingBehavior**: ```dart class Unicorn extends Entity with HasGameRef { Unicorn({ required super.position, }) : super( anchor: Anchor.center, size: Vector2.all(32), behaviors: [ TappingBehavior(), ], ); @visibleForTesting Unicorn.test({ required super.position, super.behaviors, }) : super(size: Vector2.all(32)); … ``` Check out [our blog to learn more](https://verygood.ventures/blog/build-games-with-flame-behaviors/) about how to use **flame\_behaviors**. #### Using Bloc to manage state We are using **bloc** to manage the state of the volume level of all the audio being played. In this template, a single **cubit** holds both the audio players and manages their volume: ```dart class AudioCubit extends Cubit { AudioCubit({required AudioCache audioCache}) : effectPlayer = AudioPlayer()..audioCache = audioCache, bgm = Bgm(audioCache: audioCache), super(const AudioState()); AudioCubit.test({ required this.effectPlayer, required this.bgm, }) : super(const AudioState()); final AudioPlayer effectPlayer; final Bgm bgm; void changeVolume(double volume) { effectPlayer.setVolume(volume); bgm.audioPlayer.setVolume(volume); if (!isClosed) { emit(state.copyWith(volume: volume)); } } void mute() => changeVolume(0); void unmute() => changeVolume(1); @override Future close() { effectPlayer.dispose(); bgm.dispose(); return super.close(); } } class AudioState extends Equatable { const AudioState({this.volume = 1}); final double volume; AudioState copyWith({double? volume}) { return AudioState(volume: volume ?? this.volume); } @override List get props => [volume]; } ``` Fun fact: the audio files are a Wolfen original creation. 😎 #### Audio effects We are using the [**audioplayers**](https://pub.dev/packages/audioplayers) and [**flame_audio**](https://pub.dev/packages/flame_audio) packages to play both background music and sound effects in the game. The background music uses the **Bgm** class (Bgm = background music) from the **flame\_audio** package. This class automatically handles looping background music without any gaps in between the segments. ```dart class _GameViewState extends State { … late final Bgm bgm; @override void initState() { super.initState(); bgm = context.read().bgm; bgm.play(Assets.audio.background); } @override void dispose() { bgm.pause(); super.dispose(); } … } ``` ## What’s next For the rest of the year, our team (Open Source & Tooling) is going to focus on making improvements to [our existing tools](https://github.com/VeryGoodOpenSource), such as Very Good CLI and Dart Frog. We’re also working on updating documentation for Very Good CLI and the multiple templates it supports (now including this game template!). We hope that our Very Good Game template is a helpful foundation for your next game! If you're looking for some inspiration, you can learn more about building complex games in Flutter – with a focus on architecture, testing, and automation – in our blog about [how we built Very Good Ranch](https://verygood.ventures/blog/very-good-ranch-game/). ### [**Check out the Very Good Game template code →**](https://github.com/VeryGoodOpenSource/very_good_flame_game) ### Very Good Ranch: A Very Good Game Made with Flutter Source: https://verygood.ventures/blog/very-good-ranch-game/ The Flutter community has been experimenting with game development for a while, resulting in many incredible packages, experiments, and games. We saw interest in game development reach a new level when Google announced the [Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit) at Google I/O 2022. At the same time, Google released [I/O Pinball](https://pinball.flutter.dev/), a showcase game that pushed the limits of Flutter and demonstrated that the framework can in fact be used to build a robust 2D game. I/O Pinball was also our first experience [building a game with Flutter](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d) as a team at VGV. With interest in game development growing in the Flutter community, we wanted to push our skills further and figure out how we at VGV might standardize and approach game development in a way that mirrors our [“boring” approach to app development](https://verygood.ventures/blog/boring-code-part-1/). Today we release our demo: [Very Good Ranch](https://ranch.vgv.dev/), a game of our own, built with Flutter and [Flame](https://flame-engine.org/). ## About Very Good Ranch ### How to Play It Make friends by keeping your unicorns happy — watch them grow with the right food and enough love. How many unicorns can you befriend? Gameplay goes like this: - From time to time, a baby unicorn will arrive at your ranch. - Each unicorn has two bars: the top indicates their happiness and the bottom bar indicates their fullness level. - If you keep your unicorns’ levels high, they will evolve. If you don’t, they will fly away. - At times, different types of food will spawn at your ranch. - As time passes, unicorns get hungry and bored. - Clicking a unicorn pets them and increases their enjoyment (the top bar increases). - Dragging and dropping food on the unicorn increases their fullness (the bottom bar increases). - If the food fed to the unicorn is its favorite, their enjoyment will also increase (the top bar increases). - If you keep a unicorn happy, it will evolve into the next unicorn stage. ### Concepts & Ideas Game ideation is a big challenge on its own. When creating a game, you have to account for game design, playability, game mechanics, and rules, and on top of all that, making the game fun. We had quite the challenge ahead of us! While we are not a game studio, we knew that we could create something cool and learn a lot in the process. So we agreed on only two requirements: the game should be simple and it should feature unicorns 🦄 We brainstormed for some time and landed on a [Tamagotchi-like game](https://en.wikipedia.org/wiki/Tamagotchi), where you have to take care of multiple unicorns that arrive at your ranch. Like all games, we started focusing on its core mechanics, as outlined in [this file](https://github.com/VeryGoodOpenSource/very_good_ranch/blob/712d13effe8928dd112f078f123cb8a2f95e0562/GAME_DESIGN.md) (note that a few principles have been updated since the original gameplay design, for example, there are only four types of food, not five). Here are some early explorations into designs and game mechanics: Once the basics of the gameplay were in place, we started polishing the game, adding sprite sheets, animations, and lastly, making the game look good, thanks to the magical assets provided by [HOPR](https://www.hopr.tv/)! Then, came time to build the game — a huge shoutout to our teammate [Renan Araújo](https://github.com/renancaraujo), who led the development of this project. Let’s take a closer look at the code! ## Bringing Our Scalable Standards to Game Development When we started planning the development of the game, we wanted to take what we learned on I/O Pinball and expand it. It was important for us to be able to apply our standards for app development to make this a scalable project that we could iterate upon, while maintaining a high-quality game. Read on for a closer look at some of the elements that embody our approach to [scalable app development](https://verygood.ventures/blog/scalable-best-practices/). ### Separation of Concerns At Very Good Ventures, we stick to a [layered architecture](https://verygood.ventures/blog/very-good-flutter-architecture/) when building apps. This allows us to decouple our business logic from the presentation of the application so that we can easily test, iterate, and reuse components. We applied a similar approach to this game, with the help of components and entities. ![Four unicorn evolution stages: baby, child, teen, and adult](/assets/images/blog/very-good-ranch-game/body-0.png) *Four stages of unicorn evolution* #### Components: How an Object Looks in a Game In the context of Very Good Ranch, we used **flame\_behaviors** to help us decouple the components from the [entities and their behaviors](https://verygood.ventures/blog/build-games-with-flame-behaviors/). For example, we have a unicorn component which is focused on rendering the unicorn assets. Each unicorn component uses a sprite sheet because the assets are always animated, whether they’re walking around the ranch or eating food. ![Baby unicorn eating animation sprite sheet with frame sequence](/assets/images/blog/very-good-ranch-game/body-1.png) *Baby unicorn eating sprite sheet* ```dart class BabyUnicornComponent extends UnicornComponent { BabyUnicornComponent({UnicornState? initialState}) : super( spritePadding: const EdgeInsets.only( top: 136, left: 45, right: 55, bottom: 33, ), spriteComponent: UnicornSpriteComponent( initialState: initialState, eatAnimationData: UnicornAnimationData( columnsAmount: 10, frameAmount: 90, filePath: Assets.animations.babyEat.keyName, ), idleAnimationData: UnicornAnimationData( columnsAmount: 10, frameAmount: 50, filePath: Assets.animations.babyIdle.keyName, ), pettedAnimationData: UnicornAnimationData( columnsAmount: 10, frameAmount: 90, filePath: Assets.animations.babyPetted.keyName, ), walkAnimationData: UnicornAnimationData( columnsAmount: 7, frameAmount: 34, filePath: Assets.animations.babyWalkCycle.keyName, ), ), ); } ``` The above component is just a representation of how the unicorn looks. It does not have any logic or behaviors for interacting with anything else in the ranch. Components live in the **ranch\_components** subpackage and come with their own sandbox so that you can play around and tweak them in isolation before adding them to the game. #### Entities: A Composition of One or More Components & Their Behaviors An entity consists of one or more components and one or more behaviors — think of it as how an object looks and behaves in a game. For example, a car entity in a game could consist of multiple components (such as wheels, axle, and a body, which may all be separate components) and multiple behaviors (such as moving and turning). Entities are the basic building blocks of your game. In Very Good Ranch, we have a unicorn entity that contains a unicorn component (the visual representation), along with state properties such as the current evolution phase. Various behaviors are applied to the unicorn entity, such as the evolving behavior, moving behavior, petting behavior, and more, to determine how the unicorn interacts with its surroundings and other entities (food) within the game. ```dart class Unicorn extends Entity with Steerable, HasGameRef { factory Unicorn({ required Vector2 position, UnicornComponent? unicornComponent, required UnicornGaugeCallback onMountGauge, required UnicornGaugeCallback onUnmountGauge, }) { final _unicornComponent = unicornComponent ?? BabyUnicornComponent(); final size = _unicornComponent.size; return Unicorn._( position: position, size: size, behaviors: [ DraggingBehavior(), EvolvingBehavior(), PropagatingCollisionBehavior(RectangleHitbox()), MovingBehavior(), FoodCollidingBehavior(), FullnessDecreasingBehavior(), EnjoymentDecreasingBehavior(), LeavingBehavior(), PettingBehavior(), PositionalPriorityBehavior(anchor: Anchor.bottomCenter), ], enjoyment: UnicornPercentage(1), fullness: UnicornPercentage(1), unicornComponent: _unicornComponent, onMountGauge: onMountGauge, onUnmountGauge: onUnmountGauge, ); } ... ``` #### Behaviors: How an Entity Acts As we mentioned above, our unicorn entity contains many behaviors such as the **EvolvingBehavior** to decouple the logic of how a unicorn acts from the state of a unicorn. Behaviors have access to their respective entity and can manipulate state, position, and other properties on the entity. In the case of the **EvolvingBehavior**, we inspect the evolution stage along with fullness, happiness, and number of times fed in order to determine whether a unicorn should evolve. If it’s time for the unicorn to evolve, we update the evolution stage on the entity accordingly. This triggers the evolution animation and the unicorn’s appearance changes. ```dart class EvolvingBehavior extends Behavior with FlameBlocReader { @override void update(double dt) { if (!shouldEvolve || parent.waitingCurrentAnimationToEvolve) { return; } final nextEvolutionStage = getNextEvolutionStage(); parent.setEvolutionStage(nextEvolutionStage).then((value) { bloc.add(UnicornEvolved(to: nextEvolutionStage)); }); } bool get shouldEvolve { if (parent.evolutionStage == UnicornEvolutionStage.adult) { return false; } return parent.timesFed >= Config.timesThatMustBeFedToEvolve && parent.happiness >= Config.happinessThresholdToEvolve; } UnicornEvolutionStage getNextEvolutionStage() { final currentEvolutionStage = parent.evolutionStage; if (currentEvolutionStage == UnicornEvolutionStage.baby) { return UnicornEvolutionStage.child; } if (currentEvolutionStage == UnicornEvolutionStage.child) { return UnicornEvolutionStage.teen; } if (currentEvolutionStage == UnicornEvolutionStage.teen) { return UnicornEvolutionStage.adult; } return currentEvolutionStage; } } ``` ### Managing Game State with Flame Bloc In addition to **flame\_behaviors**, Very Good Ranch also uses **flame\_bloc** in order to manage state that is shared by Flame components as well as vanilla Flutter widgets (learn more about [flame_bloc in this article](https://verygood.ventures/blog/flutter-games-with-bloc-and-flame/)). Building on the evolution example, when a unicorn evolves, the **EvolvingBehavior** notifies a **BlessingBloc** (did you know a herd of unicorns is called a blessing?) which keeps track of the number of unicorns at your ranch in each stage. The **EvolvingBehavior** does this using the **FlameBlocReader** mixin which allows the behavior to have access to the **BlessingBloc** instance: ```dart class EvolvingBehavior extends Behavior with FlameBlocReader { @override void update(double dt) { ... bloc.add(UnicornEvolved(to: nextEvolutionStage)); ... } } ``` The **BlessingBloc** state looks like: ```dart class BlessingState extends Equatable { BlessingState.initial() : this._( babyUnicorns: 0, childUnicorns: 0, teenUnicorns: 0, adultUnicorns: 0, ); ... } ``` When the **BlessingBloc** receives a **UnicornEvolved** event, it emits a new state with the updated unicorn count. In addition, the **BlessingBloc** reacts to other events such as **UnicornSpawned**, and **UnicornDespawned**. ```dart /// Blessing is a collective noun for unicorns. /// This [Bloc] keeps count of how many unicorns are in the ranch. class BlessingBloc extends Bloc { BlessingBloc() : super(BlessingState.initial()) { on(_onUnicornSpawned); on(_onUnicornDespawned); on(_onUnicornEvolved); } ... } ``` The **\_UnicornCounter** widget reacts to changes in the **BlessingBloc** state using a **BlocBuilder** and renders the updated count accordingly. ```dart class _UnicornCounter extends StatelessWidget { const _UnicornCounter({ required this.type, }); final UnicornType type; @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { final count = state.getUnicornCountForType(type); return UnicornCounter( isActive: count > 0, type: type, child: Text(count.toString()), ); }, ); } } ``` This approach allows us to share state across Flame and Flutter in a reactive way while keeping things easy to test and using familiar mechanisms such as **BlocProvider**, **FlameBlocProvider**, and **BlocBuilder** to interface with a bloc. ### Testing #### Component Tests One of the side effects of the layered, decoupled architecture is we can easily test things in isolation. For example, when testing a component we can leverage golden tests to ensure it looks as it should at any point in its animation cycle. The following snippet illustrates what a golden test for the baby unicorn’s idle animation looks like. ```dart flameTester.testGameWidget( 'idle animation', setUp: (game, tester) async { final unicorn = BabyUnicornComponent(); await game.ensureAdd( PositionComponent( position: Vector2(150, 150), children: [unicorn], ), ); }, verify: (game, tester) async { for (var i = 0.0, index = 0; i <= UnicornSpriteComponent.idleAnimationDuration; i += 0.3, index++) { game.update(0.3); await tester.pump(); await expectLater( find.byGame(), matchesGoldenFile( 'golden/baby_unicorn_component/idle/frame_$index.png', ), ); } }, ); ``` ![Golden test frame snapshots of baby unicorn idle animation](/assets/images/blog/very-good-ranch-game/body-2.png) *snapshots from golden tests* #### Entity Tests When testing an entity, we can ensure the entity contains the correct behaviors in response to changes in its state. For example, when a unicorn has the correct level of fullness, enjoyment, and number of times fed, we can verify it evolves to the next stage. ```dart flameTester.test( 'evolves to next stage', (game) async { final unicorn = Unicorn( position: Vector2.all(1), onMountGauge: (gauge) {}, onUnmountGauge: (gauge) {}, ); await game.background.ensureAdd(unicorn); expect(unicorn.evolutionStage, UnicornEvolutionStage.baby); unicorn.timesFed = Config.timesThatMustBeFedToEvolve; // First ensure that the evolution effect is added. game.update(0); await game.ready(); // Then wait for the evolution effect to finish. game.update(4.2); await game.ready(); expect(unicorn.evolutionStage, UnicornEvolutionStage.child); expect(unicorn.size, ChildUnicornComponent().size); }, ); ``` #### Behavior Tests When it comes to testing behaviors, we can verify that when the behavior is applied to an entity with a predefined state, it should apply the correct changes to the entity. For example, we can verify that when the **EvolvingBehavior** is added to a baby unicorn that is ready to evolve, it evolves to the next stage and notifies the **BlessingBloc**. ```dart flameTester.test('from baby to kid', (game) async { final enjoymentDecreasingBehavior = _MockEnjoymentDecreasingBehavior(); final fullnessDecreasingBehavior = _MockFullnessDecreasingBehavior(); final evolvingBehavior = EvolvingBehavior(); final unicorn = Unicorn.test( position: Vector2.zero(), behaviors: [ enjoymentDecreasingBehavior, fullnessDecreasingBehavior, evolvingBehavior, ], ); await game.ready(); await game.background.ensureAdd(unicorn); expect(unicorn.evolutionStage, UnicornEvolutionStage.baby); unicorn.timesFed = Config.timesThatMustBeFedToEvolve; unicorn.enjoyment.value = 1; unicorn.fullness.value = 1; game.update(0); await game.ready(); game.update(4.2); await game.ready(); expect(unicorn.evolutionStage, UnicornEvolutionStage.child); expect(unicorn.timesFed, 0); await Future.microtask(() { verify( () => blessingBloc.add( const UnicornEvolved(to: UnicornEvolutionStage.child), ), ).called(1); }); }); ``` #### Testing Automation When building scalable applications, it’s really important to be able to enforce best practices via automation. We use [Very Good Workflows](https://github.com/VeryGoodOpenSource/very_good_workflows) to run various checks in our CI, such as ensuring all tests are passing and that the affected packages are fully tested (in terms of line coverage). ```yaml name: ranch_components concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: push: paths: - "packages/ranch_components/**" - ".github/workflows/ranch_components.yaml" pull_request: paths: - "packages/ranch_components/**" - ".github/workflows/ranch_components.yaml" jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 with: flutter_channel: stable flutter_version: 3.3.3 working_directory: packages/ranch_components coverage_excludes: "lib/gen/*.dart" ``` For more about testing, check out our [Flutter testing resource blog](https://verygood.ventures/blog/flutter-testing-resources/). ### Preloading Assets Games can include a variety of different assets including images, sprite sheets, custom fonts, audio files, and more. Because games are constantly updating, any asset loading in the middle of the game can impact performance. In Very Good Ranch, we wanted to be able to preload the assets so that they are available immediately when the user starts a game. To do this, we created a **LoadingPage** which handles rendering a loading progress bar while assets are being loaded. Once all assets are loaded, the user is redirected to the **TitlePage**. The **LoadingPage** integrates with a **PreloadCubit** that manages the current preload progress and schedules the preloading of each asset group (**FoodComponent, UnicornComponent**, etc.). The **PreloadCubit** is provided at the root of the application via **BlocProvider** and initiates the preloading phase immediately: ```dart class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ ... BlocProvider( create: (_) => PreloadCubit( UnprefixedImages(), RanchSoundPlayer(), )..loadSequentially(), ) ], child: const AppView(), ); } } ``` Internally, the **LoadingPage** listens for state changes in the **PreloadCubit via BlocBuilder** and updates the progress bar using an **AnimatedProgressBar**. ```dart return BlocBuilder( builder: (context, state) { final loadingLabel = l10n.loadingPhaseLabel(state.currentLabel); final loadingMessage = l10n.loading(loadingLabel); return Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 24), child: Assets.images.loading.image(), ), Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: AnimatedProgressBar(progress: state.progress), ), Text( loadingMessage, style: primaryTextTheme.bodySmall!.copyWith( color: const Color(0xFF0C5A4D), fontWeight: FontWeight.w900, ), ), ], ); }, ); ``` The **LoadingPage** also uses a **BlocListener** to redirect to the **TitlePage** when the assets have all been loaded successfully: ```dart return BlocListener( listenWhen: _isPreloadComplete, listener: (context, state) => onPreloadComplete(context), child: ..., ); ``` The end result is a loading screen that reports the progress to the user and ensures all assets are available by the time the user enters the game. This process of loading the assets to the time you can start playing the game should only take a few seconds (depending on the speed of your internet connection, of course). ### Game Juiciness There is a [saying](https://en.wikipedia.org/wiki/Ninety%E2%80%93ninety_rule) in software development: "The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time." We’ve found that this can be true of game development when it came to Very Good Ranch. The last ten percent of development certainly seemed to account for 90 percent of the time spent due to final tweaks, polish, bug fixes, playtesting, and generally making the game [juicy](https://www.youtube.com/watch?v=Fy0aCDmgnxg) — or, as we like to think of it, taking our game from good to *very good*. One of the most difficult challenges that we faced was synchronizing the unicorn animations. Animations can become stacked easily. For example, when a unicorn is in the process of evolving, the user can feed or even pet the unicorn, which can cause interruptions in the evolution animation. While there's no super secret tip here on how to solve that, plenty of playtests were used to identify any issues and unit tests were added to ensure that any bugs we fixed would not resurface. Separation of concerns played a big role, as it made writing these tests easier and less fragile. ## What’s Next Very Good Ranch was a fun opportunity to continue to explore how Flutter (and Flame) can be used for game development. While VGV is primarily a Flutter app development consultancy, it’s pretty cool that we can easily switch gears and use the same tech stack we use to build apps to make robust games. Building this Very Good Ranch demo allowed us to stretch our development capabilities, test our existing tools, and provide some additional resources and tooling for the Flutter Community. While we don’t plan to actively iterate on Very Good Ranch, we will continue to maintain it and hope that it serves as a good resource for Flutter developers looking to learn more about building complex games in Flutter with a focus on architecture, testing, and automation. We also hope you enjoy taking care of your unicorns and watching them grow — how many unicorns can you keep in your ranch? ### [**Play Very Good Ranch here →**](https://ranch.vgv.dev/) ### [**Take a closer look at Very Good Ranch on GitHub →**](https://github.com/VeryGoodOpenSource/very_good_ranch) *Renan Araújo contributed to this article.* ### Meet the Unicorns: Marcos, Parker, Jaime Source: https://verygood.ventures/blog/meet-the-unicorns-marcos-parker-jaime/ For over four years, we've been building [high-quality Flutter applications](https://verygood.ventures/blog/why-build-apps-with-flutter-2022/) with companies that span a variety of industries, from financial services to entertainment. Our goal is to create high-quality software that is flexible, adaptive, and built to last – for any platform. Of course, none of this would be possible without our fellow unicorns! We talked to a few of our teammates to learn more about how they got into tech, their day-to-day work at VGV, and what they’re currently learning more about right now. ## Marcos Sevilla, Engineer ![Marcos Sevilla, Engineer at VGV](/assets/images/blog/meet-the-unicorns-marcos-parker-jaime/body-0.jpeg) **Why did you first become interested in tech?** The first thing that made me interested in tech was receiving an Android tablet at Christmas. After messing with developer options, I was interested in building apps in general. My dad is also a software engineer. When I was younger, he got me into a course on Objective C. I didn’t have the required background at that time, so it was kind of a bad experience.  But, after that, I went to one of his meetings to learn more about how they work on a daily basis. I really liked it! That was the thing that got me into wanting to study Computer Science. When I heard about [Google Developer Groups (GDGs)](https://developers.google.com/community/gdg) at my college, I joined because I wanted to gain some experience with communities and Google technologies. **What’s one interesting thing you’re learning more about now?** I’m taking a course on cloud technologies, such as Google Cloud. I’d like to get some experience with full stack development. I’m understanding how the cloud works so that I can build backends in Dart. I think Dart is a good language to learn to code in general. It has many features that other languages lack. If you come from JavaScript, you should be familiar with the syntax. Dart also has many other features that are new, such as null safety (only a few languages have this). Things like asynchronous programming and real-time communication are native within Dart. It has really cool stuff that I think can be used to build pretty robust backends. **What’s your day-to-day like?** I just changed the project I was on, so right now my typical day is pretty different from how a typical day looked just last week.  On my previous project, we had teammates in different time zones, so we would have our daily stand-up first thing in the day. Then, I’d review PRs from the day before. After that, I’d pair program if someone needs help or I’m facing a challenge in the codebase. Afternoons are pretty focused on actual development of features — sometimes through pairing, other times it’s me focusing on development. **What do you enjoy most about being a Flutter engineer at VGV?** I think it would be working with big clients on projects that are challenging. We usually learn a lot from those challenges. It’s the best way to get motivated and do the best work.  I also like how we have a positive influence in the community. It’s cool to see what we do on the inside being put out there and being used by many other people. That’s one of the best things — to make open source tools and help people from the community.  ## Parker Stromberg, Senior Engineer ![Parker Stromberg, Senior Engineer at VGV](/assets/images/blog/meet-the-unicorns-marcos-parker-jaime/body-1.jpeg) **Why did you first become interested in tech?** I’ve always been intensely fascinated by computers. My dad is in software as well and was someone I could always go to for questions. When I was a kid, around 12 or 13, he got me this book on JavaScript that went completely over my head. But I understood enough to spark a fire to continue learning. At that point, I started scouring for more information online and built a bunch of little projects. I also signed up for free sites like [Codecademy](https://www.codecademy.com/).  Ever since, tech has continued to be something I thoroughly love doing and learning about. **How did you get into Flutter? How did you hear about VGV?** I learned about Flutter back when it was in beta. I remember thinking that it was a really smart way to do cross-platform tech. Other platforms out there sacrificed performance or quality, or they were very limited with what they could do. I thought Flutter was an elegant way to handle the cross-platform problem, so I started using it in a few side projects and loved it. When it became stable, I pulled it into a project at a previous role, and I’ve been using Flutter ever since!  **What’s one interesting thing you’re learning more about now?** I’m learning about the lower level internals of Flutter, specifically how rendering and layout are achieved. One of the reasons is because of a library I’ve created that adds a customizable spring effect to scroll views. Right now I’m focused on revamping how it renders and simulates the spring effect.   **What do you enjoy most about being a Flutter engineer at VGV?** At VGV, I’ve come to really enjoy the consulting aspect. It is a more niche type of consultancy, as we specialize in a specific framework with a strong focus on quality. Being able to boost other people’s journey to learn is a really fulfilling and satisfying to do. As an engineer, I just love figuring out how things work. I become restless if I can’t take stuff apart. It’s a very creative or open endeavor. Coding is a low-cost, but high-value activity. You’re on the computer, writing code, and what you’re getting is useful, impactful, and beautiful software.  **What’s your day-to-day like?** I would say it depends on the stage of the project. I started on a new client project, so I have been assisting with the set-up process, such as making sure we have well-defined stories and the collaboration that we need. Recently my role has turned more into assisting with technical design and code reviewing. Usually I start my day with heavy communicating, hopping on calls, then code reviewing. After that, it depends on if I have stories that I need to work on, then I’ll code those.  **Has anything surprised you about VGV?**  I was very surprised how open-minded people are. It’s cool, for example, to openly challenge ideas in one of our engineering channels on Slack. It’s okay if you don’t understand or disagree with something. Everyone on the team wants to figure out how to find better-engineered solutions together, so everyone embraces constructive conversations. ![Jaime Blasco, Engineer at VGV](/assets/images/blog/meet-the-unicorns-marcos-parker-jaime/body-2.jpeg) ## Jaime Blasco, Engineer **Why did you first become interested in tech?** My dad worked in tech, so that’s where I got the initial inspiration. When I was around 14, I tried to build a website and chose the best tool ever to do so: PowerPoint! I started by converting the presentation to use HTML. Then I moved to code on the web and tried to build better websites. I even built the website for my theater school when I was 15 (and they still have it deployed!).  From there, I got more serious. When I was older, I studied Industrial engineering where we didn't touch much code (although I drew a Yoshi with OpenGL at some point). The real deal came when I took an exhaustive course about native mobile development in my spare time. This course helped me learn multiple programming languages at the same time. It really inspired me to begin making apps for myself. A few years ago, I attended the Apple Developer Academy in Naples for a year. It was super cool because there were people from all around the world who were passionate about the same things. I even had the chance to go to WWDC. After that course, I went to build a startup and moved on to Flutter.  **What are some of your go-to resources for learning about Flutter or Dart?** I love to go deep into the code. I love how Flutter and Flutter packages are open source because you can really dig into how it’s made. Other people may prefer APIs or documentation, but I prefer to go straight to the source. As a developer, I also have very good search skills. I often use them to dig into issues on GitHub. You can get a lot of info just doing that because people will propose different solutions. Also, reviewing PRs is very interesting. My teammates Alejandro and Óscar always laugh at me because when they mention a specific issue or PR, I know exactly which one they’re talking about.  **What do you enjoy most about being an engineer at VGV?** Everyone feels passionate and committed about the work. That helps a lot in dynamics internally and keeps conversations constructive. We know that we’re there to help and so you need to be open to people helping you. It’s a compassionate and collaborative workspace in general. Everyone I’ve been working with has been really aligned around the same level of standards for working.  **What’s your day-to-day like?** People might think that most of the work as a developer is just coding on your own, but it’s more than that. A lot of my work has involved code reviews, pair programming, spring planning, and estimating tasks. There are lots of processes that we’ve adapted working with clients. After working for a year on one project, I am now in between projects, so I’m going to be working with the open source team for a few weeks. The nice thing at VGV is that you never get bored!  **Anything else?** At VGV, it’s more than just work. It’s about providing quality, enjoying the process, and learning a lot. It’s not just working, it’s doing something meaningful in your life. You feel like you progress a lot! ### [**Join our team! Check out open job roles at VGV.**](https://verygood.ventures/careers/) ### [**Hear from our other teammates! Read another article in our "Meet the Unicorns" series with Elizabeth, Kyle, and Jochum.**](https://verygood.ventures/blog/meet-the-unicorns-elizabeth-kyle-jochum/) ### New VGV leaders: Growing our team and our culture Source: https://verygood.ventures/blog/new-vgv-leaders-growing-our-team-and-our-culture/ Today, we are pleased to [announce four new leaders at VGV](https://www.prnewswire.com/news-releases/very-good-ventures-appoints-erik-manley-coo-as-part-of-leadership-expansion-301629349.html) – Erik Manley as Chief Operating Officer, Torrie Power as Head of People Operations, Josh Musick as Head of Design, and Greg Wallace as Head of Marketing. These new teammates represent our culture-first approach to growing VGV and our impact.  Together with our existing team and leaders, VGV is working to bring the transformational capabilities of Flutter to more businesses and influence more aspects of how they do business — and our culture is core to our ability to have an impact. ## The VGV culture As the leading Flutter development consultancy working to empower software excellence for any platform, VGV works with the biggest companies to design, build, and scale successful apps using Flutter.  Our team is guided by these four core values: ### 1. Do the best, professional work We create confidence and eliminate worry. We are the global experts in what we do. We provide authoritative knowledge and solutions. Our work is always professional-grade. ### 2. Be collaborative partners We put trust at the center of what we do. We are “one team” and deeply transparent with our clients. We confidently manage the process based on our experience. We tailor our approach to client requirements and preferences. We make setting and managing expectations a top priority. ### 3. Scale with standards We create scale through solid, repeatable practices. We grow client capabilities, in addition to creating software. We are principled with our work and avoid shortcuts. We seek out and build solutions to repeating needs. We embrace automation and long-run optimization. ### 4. Be open, be friendly, be fun We always treat everyone, inside VGV and out, with respect and positivity. We are always inclusive and open to new ideas. We stay on the edge by exploring and taking calculated risks. We pursue opportunities that make our culture and jobs better. We enjoy our time together (because otherwise what is the point?). ## Why these leaders for these roles The power of Flutter, when implemented in a scalable way, is attracting the attention of developers and technology leaders across sectors and around the world. Even as we try not to take ourselves too seriously, we do take our role in the Flutter ecosystem seriously. We are committed to bringing our recipe for success with Flutter to all the businesses and teams that believe what we believe. We are helping clients succeed with projects ranging from smart homes, to in-vehicle displays, to global employee engagement apps that may someday replace the intranet, to media and entertainment platforms. Our commitments to customer success and to helping clients scale with standards has resulted in VGV more than doubling year-over-year.  In addition to growing new opportunities, we are also seeing demand in existing clients for our help with additional aspects of the application design-development-deployment pipeline. All of which is to say, we need more amazing people in the business to help us grow. Torrie, Josh, Greg, and Erik bring deep experience operating in their respective disciplines. But most importantly, they bring the humility and collaboration to ensure that we preserve what makes us special and successful. As with every single new employee that chooses to join VGV, I am grateful to each of these four new leaders for joining our team and making our collective success part of their life story.  ## In their Own Words ### Erik Manley, COO ![Erik Manley, Chief Operating Officer at VGV](/assets/images/blog/new-vgv-leaders-growing-our-team-and-our-culture/body-0.jpeg) I was immediately attracted to VGV’s commitment to growing rapidly while maintaining its culture and values. VGV’s dedication to quality is not just externally focused, but internally on the employee experience, with an emphasis on collaboration, respect and freedom to experiment, innovate, and be creative. The best work will come from people who love what they’re doing, and I’m excited by (and committed to) the opportunity to take advantage of Flutter’s market potential while reinforcing all of the reasons people work at VGV to begin with. ### Josh Musick, Head of Design ![Josh Musick, Head of Design at VGV](/assets/images/blog/new-vgv-leaders-growing-our-team-and-our-culture/body-1.jpeg) I came to VGV because it’s a great group of people that solves real-world problems in a way that, in my experience, a lot of product organizations need. I believe software can genuinely improve the quality of people’s lives in meaningful ways, big and small, and much of the time what companies can deliver is undermined by organizational constraints colliding with suboptimal technologies. I think the VGV approach offers a way out: it provides rich expressive capability in a comparatively simple codebase, that can scale beautifully across a huge range of touch points an organization might offer its constituents. Design at VGV will be about delivering excellent multi-platform experiences and helping client teams extend and sustain them over time. We will focus not just on the quality of experiences, but on the capabilities organizations need to create, scale and orchestrate them effectively. It’s going to be fun! ## Torrie Power, Head of People Ops ![Torrie Power, Head of People Operations at VGV](/assets/images/blog/new-vgv-leaders-growing-our-team-and-our-culture/body-2.jpeg) From my first interactions with David and other VGV leaders, I felt that the mission and culture were genuine. It was clear that leaders and their respective departments work together as one team. As the company grows and expands, I am excited to be part of this talented organization where I can drive positive change through global partnership with our ever growing number of unicorns. As Head of People Operations, I have a unique opportunity to lead the team building the foundation of best practices, processes, policies, benefits, and programs that will enable VGV and its culture to scale. For employees, a large part of culture is knowing that their organization will do right by them. The work of building and maintaining a strong cultural foundation is ongoing, and People Operations is at the heart of this strategy. ### Greg Wallace, Head of Marketing ![Greg Wallace, Head of Marketing at VGV](/assets/images/blog/new-vgv-leaders-growing-our-team-and-our-culture/body-3.jpeg) Perhaps the most important of VGV’s values to me is collaboration. Not only is this something that makes work enjoyable and productive for me, I also believe it is essential to delivering great outcomes as a business.  As I settle into the role, I am focused on learning the tech, the business, and the community, adding value in the marketing white spaces, and helping us scale our messages and programs to reach every business that can benefit from VGV’s uniquely powerful approach to building scalable apps and teams.   ## A sneak peek at our roadmap When we announced our [Series A from Celesta Capital](https://verygood.ventures/blog/series-a/) earlier this year, I wrote: *Flutter enables companies to deploy their applications on virtually any platform from the same codebase. This simplifies the engineering process and adds efficiencies that expand beyond the software engineering team. From strategy, product, design, backend, infrastructure, compliance, security, marketing, and so on — all aspects of how an organization manages technology is affected if a company embraces Flutter. It is this impact on adjacent teams and capabilities that drive our long-term vision.*  A few months on, and we are well on our way to building the capacity to scale both VGV’s and Flutter’s impact. > We imagine a future where organizations can efficiently manage an entire ecosystem of applications with the same tools, skills, and teams. Flutter (and Dart) enable a future where apps are not only multi-platform, but are complementary and build on shared foundations of common code and processes.  Simplifying the development process means that colleagues in design, strategy, product, operations and other disciplines can more effectively integrate and collaborate with development teams. With the burden of maintaining multiple codebases removed, technical organizations can dramatically accelerate delivery, shrink technical debt, and become radically more responsive to customer feedback. Torrie, Josh, Greg, and Erik will add their collective expertise to help us realize this roadmap. They are joining an incredible team of talented people that have built VGV into a company capable of global impact. ### VGV Goes to Flutter Vikings Source: https://verygood.ventures/blog/vgv-goes-to-flutter-vikings/ Earlier this month, some members from our team gathered in Oslo, Norway to attend the first in-person conference in a few years: [Flutter Vikings](https://fluttervikings.com/). This conference brought together around 500 in-person attendees, with hundreds more streaming online. We were one of the conference sponsors, so we were lucky to have a booth where we could easily meet everyone and talk about our favorite topic: Flutter!  ![VGV team at the Flutter Vikings 2022 sponsor booth in Oslo](/assets/images/blog/vgv-goes-to-flutter-vikings/body-0.jpeg) Before we get into our takeaways from the conference, we want to give a shoutout to [Majid Hajian](https://twitter.com/mhadaily) who put a lot of work into organizing the conference. We were very impressed with how smoothly the event went. The energy and excitement of the Flutter Community was palpable and we were excited to be a part of it. Now, onto our impressions from the conference! ## Flutter 3.3 The day before Flutter Vikings began, we were surprised to see a new Flutter release: [Flutter 3.3](https://medium.com/flutter/announcing-flutter-3-3-at-flutter-vikings-6f213e068793)! Incremental releases are always fun because they deliver nice quality updates, while giving us a look into what big things are coming next. Included are numerous performance improvements, a new version of the **go\_router** package, and new widgets like **SelectableArea**, among other updates. **Check out** [**this article**](https://medium.com/flutter/whats-new-in-flutter-3-3-893c7b9af1ff) **for a full list of what’s new.** One big thing that caught our eye from the Flutter 3.3 announcement was [Impeller](https://github.com/flutter/engine/tree/main/impeller), a new rendering engine poised to take the place of Skia. It’s designed to be more performant, especially when it comes to rendering animations. We are eagerly awaiting Impeller’s continued development and look forward to experimenting with it in the future. ### Wonderous Also announced alongside Flutter 3.3 was the [Wonderous app](https://medium.com/flutter/wonderous-explore-the-world-with-flutter-f43cce052e1). Built by GSkinner, Wonderous is a visually impressive showcase of Flutter at its finest. It is filled with crisp images, smooth animations (thanks to Impeller), and a pleasant design. We were also impressed that the app makes use of haptics with gestures, such as when swiping through the photo gallery for a selected location. ![Wonderous app showcasing Flutter animations and Impeller rendering](/assets/images/blog/vgv-goes-to-flutter-vikings/body-1.png) We will definitely be referring to this app for visual design inspiration in the future — especially when it comes to those small moments where a small animation or a bit of visual flair will take our app to the next level. We were also happy to see that GSkinner also open sourced the library [Flutter Animate](https://blog.gskinner.com/archives/2022/09/introducing-flutter-animate.html), making a wide range of animations available to any Flutter developer.  ## The Talks There were so many talks that we wanted to see, but with three tracks, it was hard to catch them all. 😅 Here are some that caught our interest, including the talks by our teammates Renan and Felix. ### Roaa Khaddam: Take You Flutter Animations To The Next Level Roaa showed us how we can easily add animations to improve the user experience using built-in Flutter components. She covered the difference between explicit and implicit animations, as well as how to easily implement beautiful scrolling animations with **AnimationController**. One cool package that she showcased was [**sensors_plus**](https://pub.dev/packages/sensors_plus) which allows you to access a device’s accelerometer, gyroscope, and magnetometer sensors. This package can be used to create dynamic animations that respond to how a user handles a device. This was a [great talk](https://youtu.be/-BcQAWdtq5E?t=291) for taking your animations to the next level! ### Tim Sneath: Flutter, FFI and Fun: Windows Development with Dart and Win32 Tim [showed us](https://youtu.be/m2YGUCMYcqw?t=366) how to call native windows APIs directly from Dart using FFI. He introduced us to the [**dart_console**](https://pub.dev/packages/dart_console) package which encapsulates a lot of the complexities of working with ANSI Escape Sequences and demoed how it can be used to implement your own text editor (inspired by the [Kilo editor](https://github.com/antirez/kilo)). Tim then gave an overview of the [**win32**](https://pub.dev/packages/win32) package which exposes thousands of Win32 API calls directly from Dart and wrapped things up by demonstrating how to build your own notepad app entirely in Dart. ### Renan Araújo: Advanced Scroll Techniques ![Renan Araujo presenting on advanced scroll techniques at Flutter Vikings](/assets/images/blog/vgv-goes-to-flutter-vikings/body-2.jpeg) Our teammate Renan gave a talk on scrolling behaviors in Flutter, which was extremely informative. He broke down scrolling into its separate parts and showed us how to make the most of scrolling behaviors using slivers and lazy loading. For a wow factor: Renan created a demo app containing Oslo photos. He then combined Flutter’s scroll functionality with the package [**umbra**](https://pub.dev/packages/umbra) (maintained by our teammate Jochum) to achieve a cool blur effect when scrolling. [View the full slides here](https://github.com/renancaraujo/slides). ### Felix Angelov: Meet Mason: Intro to Templating and Custom Code Generation ![Felix Angelov presenting the Mason templating and code generation package](/assets/images/blog/vgv-goes-to-flutter-vikings/body-3.jpeg) Our teammate Felix gave a talk on the [**mason**](https://pub.dev/packages/mason) package and showed us how to use it to generate our own templates that are consistent, fast, customizable, and reusable. He gave a closer look at all of the tooling around mason, including the core templating engine, reusable **mason\_logger**, the command-line tool, and the API client for managing remote bricks. [View all of the slides here](https://github.com/felangel/meet_mason). ## The Community By far the best part of Flutter Vikings was being a part of the community. Meeting people in real life, whom we’ve previously only interacted with virtually, was so exciting! We had many conversations with other Flutter developers, developer advocates, recruiters, and more. Throughout it all, we were struck by how positive and friendly everyone was. It was also heartwarming to hear that our blog posts (like [this one on layered architecture](https://verygood.ventures/blog/very-good-flutter-architecture/)) and [open source tools](https://github.com/VeryGoodOpenSource) have been widely read and used respectively by members of the community. ### Dart Frog We received a lot of interest in [Dart Frog](https://dart-frog.dev/), our lightweight backend framework for Dart. Many commented on how they were looking for a tool for Dart on the server and that Dart Frog was a good solution for this. We were also excited to talk to others working in this space, such as Viktor Lidholt, who is working on [ServerPod](https://serverpod.dev/), another backend solution for Dart. We believe in the potential for Dart on the backend and can’t wait to see what others do in this space.  ### Open Source One question that we received about our open source tools was: **"Why do you put all this great stuff out there for free?"**  We open source things that we believe are generally useful to the community. Many of these tools have been informed and created by our work with clients — if we can benefit from our solutions, we want others to have the opportunity to do so as well. As a consulting company, we don’t have one product that we are actively developing; rather, we are in the business of sharing our expertise with clients. We want to help them build the knowledge and processes to succeed with Flutter long after we’re out of the picture. Open source tools help support this endeavor since they’re available for anyone to use for free forever.  Finally, open source is a part of our identity as a team. We believe that we should be good citizens of the global Flutter community so we give back where we can by open sourcing tools, packages, and content that we find useful ourselves — in the hope that they will help others who wish to use them. ### Swag Of course, we couldn’t attend the conference without bringing some swag. We gave away many Very Good Ventures t-shirts and unicorn and Dart Frog stickers at our sponsors booth. ![VGV t-shirts, unicorn stickers, and Dart Frog stickers at the booth](/assets/images/blog/vgv-goes-to-flutter-vikings/body-4.jpeg) Some memes of our teammate Jochum even came out of the event 😆  ![Meme of Jochum van der Ploeg from Flutter Vikings](/assets/images/blog/vgv-goes-to-flutter-vikings/body-5.png) ![Another meme of Jochum van der Ploeg from Flutter Vikings](/assets/images/blog/vgv-goes-to-flutter-vikings/body-6.png) ### We are honored to have been a part of this year’s Flutter Vikings, and we’ll see you at the next conference! ### Generate a Dart CLI with Very Good CLI Source: https://verygood.ventures/blog/generate-command-line-application-cli/ There are few resources more crucial to software development than [command-line interfaces (CLIs)](https://en.wikipedia.org/wiki/Command-line_interface). As one of the simplest forms of [Human-Computer Interface (HCI)](https://www.interaction-design.org/literature/book/the-glossary-of-human-computer-interaction/interaction-styles), a CLI allows you to simplify complex tasks and can be used to compose rich applications. Most developers use CLIs every day. Even Flutter comes with one: [**flutter_tools**](https://github.com/flutter/flutter/tree/master/packages/flutter_tools). Knowing how to build a command-line application can unlock a world of possibilities for a Dart developer. There are several excellent examples of CLI packages out there, for instance: [**mason_cli**](https://pub.dev/packages/mason_cli), [**fvm**](https://pub.dev/packages/fvm), [**dart_frog_cli**](https://pub.dev/packages/dart_frog_cli), and [**umbra**](https://pub.dev/packages/umbra_cli). Creating one from scratch is not an easy task though. If you're making a CLI package for the first time, you may face some challenges, such as handling exceptions and keeping the code organized. Due to Dart’s flexible nature, it can take some time to figure out how to organize the code, make it testable, and configure the details of what makes a CLI application that is not only good, but *very good*. [Our team](https://verygood.ventures/resources/open-source-packages/) has gathered some experience building CLI tools and turned them into a package template, so that you don’t have to go through the same challenge. You can simply start creating great stuff. **This is a guide on designing a kick-ass CLI application written in Dart using the** [**Very Good Dart CLI**](https://github.com/VeryGoodOpenSource/very_good_dart_cli) **template. Let’s go!** ## Getting started [Very Good CLI](https://pub.dev/packages/very_good_cli) is a tool made by Very Good Ventures that, among other things, helps to ease the process of setting up any type of Dart project, including Flutter applications, [federated plugins](https://verygood.ventures/blog/generate-flutter-plugins-with-very-good-cli/), Flutter and Dart packages, and of course, Dart CLI applications. If you didn’t yet, activate it via: ```shell $ dart pub global activate very_good_cli ``` To take the first step into the *Dart-CLI-making* world, use the **create** command with the appropriate template: ```shell $ very_good create dart_cli my_simple_cli ``` And voila! You now have generated a CLI package with everything ready to get started: a sample command with colorful outputs, a plug-and-play update mechanism, a file structure that scales, and last but not least, everything stamped with that [beautiful unicorn wearing shades](https://twitter.com/VGVentures/status/1554116996542889984): testable! Now, let's look at the generated file structure. ## The anatomy of a Dart CLI package Like everything created using Dart, a CLI application is essentially just a [pub package](https://dart.dev/guides/packages), which defines the basic structure: A **pubspec.yaml** in the project root, accompanied by the directories **lib** and **bin**. These are three crucial things to be mindful of about the generated project’s structure: 1. **The lib directory:** This may be familiar if you've worked with Dart before. This is where packages should put the classes and objects that are to be used by your scripts ([library code](https://dart.dev/tools/pub/package-layout#public-libraries)). 2. **The bin directory:** This, combined with the lib directory [constitute the package's public API](https://dart.dev/tools/pub/package-layout#public-directories): This should include the actual script files, which will call the entry-point function (**main**) when executed. Most CLIs have only one script file. 3. **And last but not least, the good old pubspec.yaml:** This describes the [metadata of the package](https://dart.dev/tools/pub/pubspec). Note that for CLI applications, there is one additional field: executables. This entry should match the executable name (the first word in a command that your application will interpret) as keys, to files in the bin directory (values). The value can be left blank if the executable's name is the same as its corresponding script file. The executable name doesn't need to be the same as the package name. To customize that upon generation, use the **\--executable-name** option: ```shell $ very_good create my_simple_cli -t dart_cli --executable-name my_simple ✓ Generated 19 file(s) (3.3s) ✓ Running "flutter pub get" in my_simple_cli (3.4s) ✓ Running "dart fix --apply" in my_simple_cli (3.2s) Created a Very Good Dart CLI application! 🦄 ``` Resulting **pubspec.yaml** file: ```yaml name: my_simple_cli # ... executables: my_simple: ``` Then, after [activation](https://dart.dev/tools/pub/cmd/pub-global#activating-a-package) through the **dart pub global activate** command, the CLI application will be invoked by commands that start with **my\_simple**. ## What makes a CLI delightful Before going deep into the code, let's look at a few essential components of CLI design and the common patterns that most CLI applications follow. Although CLI apps have a very simple visual interface, we can't ignore UX completely. Some classical design principles such as familiarity, hierarchy, and consistency, can make your CLI app easier to use. ### Familiarity People may be used to some expected outputs and input formats that are common in most CLIs. For example, it is almost intuitive to type the flag **\-h** at the end of a command to understand what it does and the options available. Example of a great help message: **dart pub add**: ```shell $ dart pub add -h Add dependencies to pubspec.yaml. Usage: dart pub add [:] [[:]...] [options] -h, --help Print this usage information. -d, --dev Adds to the development dependencies instead. --git-url Git URL of the package --git-ref Git branch or commit to be retrieved --git-path Path of git package in repository --hosted-url URL of package host server ... ``` ### Hierarchy CLI applications that have multiple scopes of actions may split into numerous sub-commands. Example: **commit** and **remote** are two sub-commands of the [git CLI](https://git-scm.com/book/en/v2/Getting-Started-The-Command-Line). Once you type them, you enter their context. The following options, arguments, and sub-commands will be specific to the context of the given command. ### Consistency Sometimes, different commands may share similar options and argument formats, but people expect consistency across them. For example, **flutter build** has several platforms as subcommands, like **ios** and **android**. The user can execute each of these in either debug or release. For that, both accept **\--debug** or **\--release** as flags. As with any HCI, A CLI is just a series of input and output standards. To know how to build each of these parts is the key to success. ## What makes excellent input syntax Options? Arguments? Flags? Commands? Oh my! Do not fear these names. They are pretty helpful in understanding the input formats of a CLI. Let's analyze a command with plenty of these things to understand what is what. For example, imagine one of the most used commands in history: creating a state management package for Flutter. ```shell $ flutter create barc -t package --description="Just like bloc, but for dogs" -v --no-pub ``` - **flutter**: This is called the root command or executable. It should be unique in the current path context, so avoid publishing a CLI with existing executable names, like **git** or **ls**. Commands are the ones mapped in the **pubspec.yaml** file under the **executables** property. - **create**: After that, you have the sub-command (or just command). Commands are predefined unique words that execute a task in a specific context. Some applications have zero sub-commands (ex: **cp** and **ls**), while others have multiple levels of sub-commands (ex: **git remote add**). - **barc**: In this example, it is the name of the package. This is called an argument. Unlike commands, arguments are not predefined, but the preceding command should know how many (if any) arguments are expected from the user. - **\-t package**: This is an option. Options are composed of a key (predefined) and a value. The latter can be either predefined by a set of acceptable strings or not. Specifically, in this case, **\-t** accepts only **package**, **app**, and **plugin**, among other values; anything other than what is predefined will result in an error. This option is in a shorthand format. That is why it starts with a single dash and consists of only one letter. It is the same as using **\--template**. - **\--description="..."**: This is also an **option**, except this one is not in a shorthand format (not all options need to have a shorthand available). It accepts any value from the user, as opposed to the previous option. - **\-v**: Now there are flags. The first one, **\-v**, indicates that this command will output lots of information that otherwise would be hidden. Flags are just like options: they are formatted with dashes and can also have shorthands. The difference is that they don't accept explicit values. Flags are ideal for options that have boolean values. - **\--no-pub**: The second flag, as opposed to the previous one, has a true default value. To override that behavior, the user has to use the negated form. For that, it contains a **no** prefix to indicate its value being set explicitly to false. ### The args package As you may notice, a command syntax can assume several different forms. To specify which are the commands, options, flags, and arguments in an idiomatic way, we have the [**args**](https://pub.dev/packages/args) package. It contains classes and functions that help to bring the same organization and hierarchy of your desired CLI syntax into your code. In the template generated by **very\_good\_cli**, we have some sample commands and options to illustrate that mapping. The root command, as said earlier, calls the entry-point function on the related file under **bin**. The main function receives a list of strings corresponding to everything in the user input after the root command, separated by space. That entry-point function invokes the **CommandRunner**, the root of our tree of sub-commands. Each sub-command extends the **Command** class and an [**ArgParser** object](https://pub.dev/documentation/args/latest/args/ArgParser-class.html) describes its syntax: ```dart /// Example generated by very good CLI class SampleCommand extends Command { SampleCommand({ Logger? logger, }) : _logger = logger ?? Logger() { argParser.addFlag( 'cyan', abbr: 'c', help: 'Prints the same joke, but in cyan', negatable: false, ); } // ... ``` ### Writing valuable “help” documentation The best way to let the user discover the correct syntax of options and flags for a command is to provide a common “help” flag (**—help** or **\-h**). The **args** package generates the help flag automatically for you, given the option mapping (configured via the **argParser** object) and some overridable fields in the **Command** class, like **name**, **description**, and **argumentsDescription**. Override them in your **Command** class to customize the usage output. ```shell # Help available in the sample command $ my_simple sample -h A sample sub command that just prints one joke Usage: my_simple sample [arguments] -h, --help Print this usage information. -c, --cyan Prints the same joke, but in cyan Run "my_simple help" to see global options. ``` ## Enhancing outputs Now that we learned how to provide a straightforward way for people to interact with your application, it is time to learn the best ways to respond to those inputs. There are two main ways you can communicate the progress and result of a user’s command: the standard output and exit codes. One package that is useful is [**mason_logger**](https://pub.dev/packages/mason_logger). The template generated from the **very\_good\_cli create -t dart\_cli** has a sample command that shows: ```dart import 'package:args/command_runner.dart'; import 'package:mason_logger/mason_logger.dart'; class SampleCommand extends Command { SampleCommand({ Logger? logger, // <-- Look here: inject a logger in the constructor to make it more testable. }) : _logger = logger ?? Logger() { argParser.addFlag( 'cyan', abbr: 'c', help: 'Prints the same joke, but in cyan', negatable: false, ); } @override String get description => 'A sample sub command that just prints one joke'; @override String get name => 'sample'; final Logger _logger; // <-- Look here: a logger to abstract stdout @override Future run() async { var output = 'Which unicorn has a cold? The Achoo-nicorn!'; if (argResults?['cyan'] == true) { output = lightCyan.wrap(output)!; } _logger.info(output); return ExitCode.success.code; // <--- Look here: Exit code } } ``` Notice that it uses **mason\_logger**’s [**Logger**](https://pub.dev/documentation/mason_logger/latest/mason_logger/Logger-class.html) and [**ExitCode**](https://pub.dev/documentation/mason_logger/latest/mason_logger/ExitCode-class.html) classes. Let's understand a little bit about each: ### The standard output The [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_\(stdout\)), also known as “write stuff on the user’s screen,” is a vital part of any CLI. [**dart:io**](https://api.dart.dev/stable/2.17.5/dart-io/dart-io-library.html) has a **stdout** getter in which the raw API uses this system resource: ```dart import 'dart:io' as io; final stdout = io.stdout; stdout.writeln("some output"); ``` One problem with writing directly at this level is that there is no API to style the output. You will have to use [ANSI escape codes](https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html). Also, using it directly in **Command** classes makes it hard to test with unit tests. The **Logger** class abstracts the styling codes as wrapping API, which can be mocked for tests. Example of a command that styles output: ```dart @override Future run() async { _logger.err('This is a error!'); _logger.warn('This is your last warning!'); _logger.confirm('Do you really want that?'); _logger.success('Flawless victory'); _logger.info(backgroundCyan.wrap(red.wrap('This will be colorful'))); return ExitCode.success.code; } ``` Output produced: ![Styled terminal output with error, warning, confirm, success, and colored text](/assets/images/blog/generate-command-line-application-cli/body-0.png) Example of mocking the logger using **mocktail** on a test: ```dart import 'package:mocktail/mocktail.dart'; import 'package:mason_logger/mason_logger.dart'; // ... class MockLogger extends Mock implements Logger {} test('prints stuff', () async { final logger = MockLogger(); final command = SampleCommand( logger: logger, ); final exitCode = await command.run(); expect(exitCode, ExitCode.success.code); verify( () => logger.info('Which unicorn has a cold? The Achoo-nicorn!'), ).called(1); }); ``` ### Exit codes When a process or script finishes, it returns a code that indicates the circumstances in which it occurred. On most systems, 0 is the code for success, and anything besides that defines an error. **Mason\_logger**’s **ExitCode** abstracts some error codes and success into constants. ```dart @override Future run() async { _logger.err('This command will fail'); /// A service is unavailable. /// /// This may occur if a support program or file does not exist. This may also /// be used as a catch-all error when something you wanted to do does not /// work, but you do not know why. return ExitCode.unavailable.code; } ``` Returning something like that rather than some random **int** makes your code more readable. ## Dealing with exceptions As with any software, A CLI application execution can produce exceptions. It is essential to deal with all exceptions and provide clear reports to the user on what went wrong aligned with the proper exit code. As an example, the generated CLI application has in the [**CommandRunner**](https://pub.dev/documentation/args/latest/command_runner/CommandRunner-class.html) a **try/catch** statement that trades thrown exceptions for exit codes: ```dart Future run(Iterable args) async { try { final topLevelResults = parse(args); return await runCommand(topLevelResults) ?? ExitCode.success.code; } on FormatException catch (e, stackTrace) { // On format errors, show the commands error message, root usage and // exit with an error code _logger ..err(e.message) ..err('$stackTrace') ..info('') ..info(usage); return ExitCode.usage.code; } on UsageException catch (e) { // On usage errors, show the commands usage message and // exit with an error code _logger ..err(e.message) ..info('') ..info(e.usage); return ExitCode.usage.code; } } ``` Since the **CommandRunner** is the root of a command execution, keeping the exception handlers in its **run** method can be more effective. ## Keeping the CLI up to date One of the hardest things when publishing a CLI package into [pub.dev](http://pub.dev/) is making sure people are always using the most up-to-date version. Users have to check pub for newer versions, and when there is one, the package has to be activated again: ```shell dart pub global activate my_cli ``` Fortunately, the project generated by the **very\_good\_cli** already includes infrastructure to make this process easier. Through [**pub_updater**](https://pub.dev/packages/pub_updater) and with the help of [**build_version**](https://pub.dev/packages/build_version), all commands always check for newer versions of the CLI package on pub.dev. If there are any, the user will receive an alert to update: ![Terminal output: Update available! 0.0.1 -> 0.7.11](/assets/images/blog/generate-command-line-application-cli/body-1.png) This mechanism works based on the **lib/src/version.dart** file generated by **build\_version**, which makes the current CLI runtime's version accessible. Always make sure to generate that code before publishing new versions of your CLI package to pub.dev. The sample also includes an update command that uses **pub\_updater** to let the application update itself. ```shell my_cli update # this will update my_cli ``` ## Conclusion This is just the tip of the iceberg to what is possible to do with a Dart CLI application. We created the Dart CLI template in [Very Good CLI](https://pub.dev/packages/very_good_cli) to provide the basics of what our team thinks any Dart CLI application should include from the start. Since it is an open source project, ideas and suggestions are always welcome. If you have any, head over to [**very_good_dart_cli**](https://github.com/VeryGoodOpenSource/very_good_dart_cli) and open an issue. Go build something awesome! ### Meet the Unicorns: Elizabeth, Kyle, Jochum Source: https://verygood.ventures/blog/meet-the-unicorns-elizabeth-kyle-jochum/ For over four years, we've been building [high-quality Flutter applications](https://verygood.ventures/blog/why-build-apps-with-flutter-2022/) with companies that span a variety of industries, from financial services to entertainment. Our goal is to create high-quality software that is flexible, adaptive, and built to last – for any platform. Of course, none of this would be possible without our fellow unicorns! We talked to a few of our teammates to learn more about how they got into tech, their day-to-day work at VGV, and what they’re currently learning more about right now. ## Elizabeth, Program Manager ![Elizabeth, Program Manager at VGV](/assets/images/blog/meet-the-unicorns-elizabeth-kyle-jochum/body-0.jpeg) **Why did you first become interested in tech?** In college I studied bio-electrical engineering with the intention of going to medical school at some point. When I graduated, I wasn’t ready to commit to medical school, so I took a job at Epic Systems because I wanted some exposure to the healthcare industry. It wasn’t my intention to get into tech, but working at Epic made it happen. Now that I’m working full time in the tech industry, I couldn’t imagine anything else. In most other industries, your day would be a lot more standardized. Tech is definitely for fast-paced people. Every day looks so different, especially at VGV! Because we’re a consultancy, we do so many different things in one day and the tech we work on actually touches all of the industries. For example, I’ve worked on fintech, travel, and games projects. **What is a typical day like at VGV?**  At VGV, Program Managers (PMs) are on multiple projects concurrently. My day usually consists of doing check-ins on each of these projects, sending weekly status reports to clients, and updating and delegating tasks to the VGV engineers on these projects. The full PM team also gets together on a regular basis and does roadmap and project planning.  The PM team at VGV is really trusted by clients and so we often get to stretch our skills into product management and even account management. We help shape where the product is going and maintain relationships with clients. Working with clients is one of my favorite parts about being a PM. I love helping them figure out where they’re going next and how they can achieve their goals.  **What’s one interesting thing you’re learning more about now?** The PM team is really focused on ramping up our internal training right now. Two things we’re doing right now: 1. We decided to all take a Udemy Flutter course, which is exciting! I’ve managed Flutter projects, and now I’m actually learning how to write my own Flutter app. 2. We’re also taking a Google Project Management certificate course to make sure our PM competencies are strong. **In your opinion, what is the most important thing a program manager does?** I think the biggest thing is communication. So much of our job is making sure everyone is on the same page — both internally and with clients. I focus on making sure that all of our communication is transparent, positive, and concise.  **What is it like working with engineers at VGV?**  My favorite thing about VGV engineers is that they’re actually wizards! 🧙‍♂️ I’ve worked with developers in the past, but they were not like VGV engineers. At VGV, I can ask an engineer to help solve a problem and they actually get excited. Instead of saying, “This looks hard,” they figure out how to solve it. The biggest thing I’ve learned from working with engineers (and I’m still learning) is how to properly estimate tasks within a project. I’m always striving to create reasonable timelines. This is one area where developers have grown — and have taught us PMs how to set reasonable expectations and deadlines. **Anything else?** The PM team at VGV is very much a team! Even though we’re on different clients, we do a really good job of collaborating with each other and trying to improve as a PM team. ## Kyle Finlinson, Engineer ![Kyle Finlinson, Engineer at VGV](/assets/images/blog/meet-the-unicorns-elizabeth-kyle-jochum/body-1.jpeg) **Why did you first become interested in tech?** My dad worked on IT for the state of Utah, so my family always had tons of computers around the house. Growing up, we always had DSL — that was one thing he would not compromise on. I remember when I was eight years old, I went over to a friend’s house and heard their modem dialing up. I thought, *What is that? Why is it taking us four hours to download RollerCoaster Tycoon?* So I was always around tech even from a young age.  When I was in high school, I attended an engineering camp at Utah State. One of the coding tasks they had us do was program metal pins in a PVC pipe. The goal was to get the pins to move up and down to allow a ping pong ball to fall through. It was so cool to write the code and watch things happen!  **What are some of your go-to resources for learning about Flutter or Dart?** The [official docs](https://docs.flutter.dev/) are always really helpful. I especially like the [Widget of the Week](https://youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG) videos. They give such a helpful overview — in a few minutes they cover what it is, how to use it, and provide a simple example. These videos make it really easy to understand the rest of the documentation. **What do you enjoy most about being a Flutter engineer at VGV?** One of my very favorite things about VGV is learning really useful skills with great people, and then using those skills across completely different projects. At VGV, we have a *lot* of expertise on the team. Learning from them and being able to build real solutions and solve real problems with companies across all kinds of industries and having a consistent toolkit, consistent workflow, and consistent solutions is awesome.  It’s kind of the opposite of what I was doing before. Before I was switching around to different projects with completely different toolsets and completely different languages. At VGV, I get to dig in really deep and *really* understand Dart and how it all works. For me, that's exciting and motivating. **What’s your day-to-day like?** I usually have a couple of stand-up meetings, and read and write lots of technical design proposals and code.  I work with teammates across the world in multiple time zones, so a lot of our communication is written communication. At times it can be frustrating, but it is also nice because it forces the team into an asynchronous workflow. I’m very interested in learning more about asynchronous workflows, deep work, and digital minimalism ([as described in Cal Newport’s book](https://www.calnewport.com/books/digital-minimalism/)), so time zones have been kind of helpful for task management.  **Anything else?** Working with good people is huge! The phrase that I’ve heard to describe VGV is “humbly opinionated.” I really love that. It conveys the idea that this is how we think we should do something, but we’re completely open to suggestions if you have better ways to do it. It’s not arrogant, but it’s also not wishy-washy either. We have consistent best practices that are always open to improvement, discussion, and criticism. ## Jochum, Open Source Engineer ![Jochum van der Ploeg, Open Source Engineer at VGV](/assets/images/blog/meet-the-unicorns-elizabeth-kyle-jochum/body-2.jpeg) **Why did you first become interested in tech?** It all started when I was in elementary school. My brother installed Ubuntu on my computer and I was super mad at him because I just wanted to play my PC games. I had to figure out how to get my games to work on the new operating system, which was quite interesting. When I was around 12 years old, I started programming with HTML and CSS to make websites. From there I moved on to PHP and JavaScript. Around this time, I started playing with a thing called [ComputerCraft](https://www.computercraft.info/), which allowed me to program within the Minecraft world. I found that really fun to do — I was essentially programming inside of the game (not too far off from what I’m doing now with [Flame](https://flame-engine.org/)). **What is your typical day like at VGV?**  I’m in a different timezone than some of my colleagues, so I start my day by checking my email and Slack to see what activity happened overnight. I follow up on any open source issues or comments and then have lunch. After, I try to focus on a particular task while taking breaks in between. It’s nice because my teammate Renan is in a similar time zone so we have a lot of overlap where we can collaborate. A lot of my work recently has focused on [Very Good CLI](https://pub.dev/packages/very_good_cli) and [Mason](https://pub.dev/packages/mason). With Mason, I’ve helped implement some new features. Based on my discussions with Felix, we’ll come up with new ideas and see if we can implement it. With Very Good CLI, I’ll often review code for bug fixes or new features. I’ve also done some work on [Dart Frog](https://dart-frog.dev/), mostly reviewing pull requests and issues, and discussing where we’re going to take it next with Felix and Renan. Nothing is a one-person project on the Tooling and Open Source team! **What are some of your favorite open source tools or packages?** [Flame](https://pub.dev/packages/flame) is for certain one of my favorites. I’m a big fan of what my package [Umbra](https://pub.dev/packages/umbra) is capable of. In my opinion, it’s the way shaders should have been implemented in Flutter with a more friendly API. Everything is strongly typed, so instead of having to pass a list of numbers, you can pass in real instances of the thing you want to make. No need to do the conversion yourself.  Another open source project I like is [raylib](https://www.raylib.com/), which is a rendering framework for video games. It’s developed and maintained by one guy who has been doing it for eight years. It has a massive community and is a really interesting project. **What’s one interesting thing you’re learning more about now?** Right now I’m learning more about physics for my physics engine [Physik](https://pub.dev/packages/physik). I’m trying to understand more about how to read math equations and implement them.  Flame already has a physics engine called [Forge2D](https://pub.dev/packages/forge2d), which is a port of a popular physics engine called [Box2D](https://box2d.org/). Physik is another approach to implementing a physics engine that integrates nicely with Flame. I’m creating it to make it easier to create simulations. I noticed that you can make simulations with Forge2D, but it’s hard to set them up properly. With Physik, it’s a bit simpler. I’ve already created a particle collision simulation within a sphere, as well as a fire particle simulation. Next, I’m going to create a simulation with grass. If it all works well, I’m hoping to use these within a game in the future. **What do you enjoy most about being an open source engineer at VGV?** I enjoy the fact that I give back to the community. My job is to make sure that the community has high-quality tools that are up to VGV standards. This includes both tools maintained by VGV, but also tools used in the broader community, like Mason and the Flame Engine. ### [Join our team! Check out open job roles at VGV.](https://verygood.ventures/careers/) ### [Check out the first article in our "Meet the Unicorns" series with Elias, Traci, and Óscar.](https://verygood.ventures/blog/meet-the-unicorns-elias-traci-oscar/) ### Dart Frog is now stable 🎉 Source: https://verygood.ventures/blog/dart-frog-stable/ Since releasing [Dart Frog a few months ago](https://verygood.ventures/blog/dart-frog/), we have seen a lot of interest from the community in our approach to Dart on the server. We’ve been hard at work on updates to the package. We've also spent time testing it out in various use cases across a few of our projects. Today we're excited to announce that Dart Frog is stable! 🎉 To us, “stable” means the project is no longer just an experiment. We’ve used Dart Frog internally across multiple projects, fixed many bugs and stability issues across multiple platforms, and have addressed the critical items on our roadmap. Because of this, we have removed the experimental warning from the [docs](https://dart-frog.dev/) and [GitHub repo](https://github.com/VeryGoodOpenSource/dart_frog). Let’s take a closer look at what’s in this stable release! ## What’s included 📦 ### Hot Reload Improvements ⚡️ We have spent a lot of time improving the developer experience when it comes to using Dart Frog in dev mode. One of the biggest focus areas was on hot reload performance and reliability to ensure that it behaves the way you might expect. For example, if you’re in the middle of implementing something, hot reload should be able to recover from situations in which an error might interrupt your workflow — you shouldn’t have to constantly restart the dev server. It was important for us to have this seamless experience, not just on Unix-based systems, but also on Windows. Updates include: - Performance improvements - Stability improvements (especially on Windows) - Logging improvements - Error reporting improvements - Route conflict detection - Rogue route detection - Error message formatting - Resilience and recovery from errors ### CLI verbose mode 📢 The Dart Frog CLI now supports a **\--verbose** flag on all commands (**build**, **create**, and **dev**). You can now toggle verbose mode when working with the Dart Frog CLI, which provides additional visibility into what’s going on behind the scenes. This feature is particularly helpful for debugging issues that are hard to reproduce. ![Dart Frog CLI verbose mode output showing detailed build logs](/assets/images/blog/dart-frog-stable/body-0.png) ### Interoperability with package:shelf ↔️ From the beginning, we wanted Dart Frog to complement the existing Dart ecosystem. The core Dart Frog runtime ships with APIs that enable interop between Dart Frog and [shelf](https://pub.dev/packages/shelf), which means you can use existing shelf packages in your Dart Frog apps. Check out the following APIs for getting started with shelf interop: - [**fromShelfMiddleware**](https://pub.dev/documentation/dart_frog/latest/dart_frog/fromShelfMiddleware.html) - [**fromShelfHandler**](https://pub.dev/documentation/dart_frog/latest/dart_frog/fromShelfHandler.html) ![Dart Frog interoperability with package:shelf using fromShelfMiddleware](/assets/images/blog/dart-frog-stable/body-1.jpeg) ### Custom Server Entrypoint 🎬 Dart Frog now supports creating a custom **main.dart** which will run before the server starts. This gives you full control over initialization of the server. It also allows you to customize the http server, handlers, port, internet address, and run migration code while still taking full advantage of the generated handler. To use the custom entrypoint feature, simply create a **main.dart** at the root of your Dart Frog project and define a run function with the following signature: ```dart import 'dart:io'; import 'package:dart_frog/dart_frog.dart'; Future run(Handler handler, InternetAddress ip, int port) { // Execute custom code prior to starting the server here... print('Starting the server...'); // Use the built-in `serve` method from package:dart_frog to start the server. return serve(handler, ip, port); } ``` ### Rogue route detection 🥷 You can now detect rogue routes when running your Dart Frog server. A route is considered rogue when it is defined outside of an existing subdirectory with the same name. For example: ```shell ├── routes │ ├── foo │ │ └── example.dart │ ├── foo.dart ``` In the above scenario, **foo.dart** is rogue because it is defined outside of the existing foo directory. Instead, **foo.dart** should be renamed to **index.dart** and placed within the foo directory like: ```shell ├── routes │ ├── foo │ │ ├── example.dart │ │ └── index.dart ``` ## Tutorials 📚 This update comes with a brand new section on the Dart Frog docs site for [tutorials](https://dart-frog.dev/tutorials/hello-world/). We have four up right now: - [**Hello World**](https://dart-frog.dev/tutorials/hello-world/) - [**Echo**](https://dart-frog.dev/tutorials/echo/) - [**Counter**](https://dart-frog.dev/tutorials/counter/) - [**Todos**](https://dart-frog.dev/tutorials/todos/) ## What’s next 🗺 We’re planning to focus on Dart API client and documentation generation next. This will enable you to hook up your Dart Frog backend to an existing Flutter application seamlessly and will ensure your API documentation stays in sync as your backend evolves. As always, you can refer to the [roadmap](https://dart-frog.dev/roadmap/) for features that we’re actively developing. If you have ideas for what you’d like to see, feel free to open an issue on the [GitHub repo](https://github.com/VeryGoodOpenSource/dart_frog) and tell us more. We’d love to see what you build with Dart Frog, so tag us on [Twitter](https://twitter.com/VGVentures) or [LinkedIn](https://www.linkedin.com/company/very-good-ventures/). ### Meet the Unicorns: Elias, Traci, Óscar Source: https://verygood.ventures/blog/meet-the-unicorns-elias-traci-oscar/ For over four years, we've been building [high-quality Flutter applications](https://verygood.ventures/blog/why-build-apps-with-flutter-2022/) with companies that span a variety of industries, from financial services to entertainment. Our goal is to create high-quality software that is flexible, adaptive, and built to last – for any platform. Of course, none of this would be possible without our fellow unicorns! We talked to a few of our teammates to learn more about how they got into tech, their day-to-day as an engineer at VGV, and what they’re currently learning more about right now. ## Elias Reis, Engineer ![Elias Reis, Engineer at VGV](/assets/images/blog/meet-the-unicorns-elias-traci-oscar/body-0.jpeg) **Why did you first become interested in tech?** When I was a kid, I liked to play video games and repair my toys (or break them trying to fix something that wasn’t broken). My parents bought a computer for my sister when she was 15, so of course, I also used it. This was when I had my first interaction with technology. My parents later introduced me to hardware school where I was able to learn how to fix computers. My first job was in a software company, but my primary task was to repair computers. When I wasn’t working on hardware, I would sit down next to another developer and start asking questions. I loved that job because I could write code, see it on the screen, and solve problems. One day I decided I wanted to do that as a professional. I looked for courses and learned more about programming languages and software development, and got my first promotion to junior developer. Then I learned more about mobile development, and that is when my friend (and now VGV teammate) Erick Zanardo, introduced me to Flutter. It was love at first sight! **What is a typical day like at VGV?**  As an engineer, a lot of my day is spent working through PRs. I’m a few hours ahead of my team so my mornings don’t have many meetings. During this time, I’m able to focus on coding, writing tests, fixing bugs, and other project tasks. In the afternoons, I have meetings with my teammates who have just started working in their time zones to align as a team on our progress. Sometimes we do pair programming to fix any issues, or if other teammates ask for help on Slack, I try to answer their questions. **What’s one interesting thing you’re learning more about now?** One of the projects I’m working on uses federated plugins. This is a topic I’ve needed to dive deeper into. It’s interesting because I’m learning a lot about native code. I’ve worked a bit with Android in the past, but learning how to implement the same things on iOS is very new.  Another area I’m learning more about is building games. I’ve started building a small game by myself and in doing so, learning a lot about how games work. I’ve learned a lot by looking at the code for I/O Pinball, as well as by using Flame and Flutter since they work together nicely.  **What do you enjoy most about being a Flutter engineer at VGV?** Being a unicorn is awesome! At VGV I see how much clients trust us. In my previous roles, I’ve had experiences where clients can be skeptical about the solutions proposed. At VGV, clients look to us for answers. They ask questions about the best way to do things and it’s exciting to have that level of trust. **What is one thing you’ve learned from working at VGV?** I really am drawn to the idea of [boring code](https://verygood.ventures/blog/boring-code-part-1/)! I’ve seen other companies that do things in a more custom way. But at VGV, we make our code boring whenever possible.  The approach to [open source software](https://github.com/VeryGoodOpenSource) at VGV is also awesome. We are always looking for ways to share what we know with the community. For example, if we’re thinking about building a package that would be helpful for our team, we ask, could we also share it with the community? ## Traci Goreham, Engineer ![Traci Goreham, Engineer at VGV](/assets/images/blog/meet-the-unicorns-elias-traci-oscar/body-1.jpeg) **Why did you first become interested in tech?** I first became interested in tech because my husband was a software engineer, and I just wanted to understand a bit more about what he did all day at work. This led to trying out a free Udacity class, which led to taking an introduction to programming class at a local community college, which led to getting a second bachelor's degree in computer science. I'm honestly not super interested in technology consumption; I don't watch all that much TV, don't play video games, and don't participate on social media. However, I do like using technology as a means of production. I love making new things and solving interesting problems. **What is a typical day like?** I’m working on a project for an international company with employees across the globe. Consequently, a lot of work is happening while I am offline, so it is important that I am up to date on any discussions and merge requests as I start my day.  After daily stand-up with the team, I have time to really focus on whatever task I am currently assigned. Sometimes these tasks include writing proposals to share with other members of the broader team. Since we’re not the only team working on this project, we try to have architectural design discussions before any large changes are implemented within the project.  Occasionally throughout the day I will spend some time pair programming with a teammate to work through any issues I run into. I really value this collaborative work time with my colleagues, as it provides me with opportunities to develop my programming and debugging skills.  **What are some of your go-to resources for learning about Flutter or Dart?** I like to go directly to the docs. Or, I’ll go directly to the codebase to see if there is an example of something I am trying to accomplish. My VGV teammates are also great resources. There are many smart people internally at VGV and on our client teams. I try to learn as much as I can from speaking to them — as they learn from us, we’re also learning from them. **What do you enjoy most about being a Flutter Engineer?** I’m always learning something new! There is always a twist on something I’ve done before and I’ve always liked puzzles and figuring things out. I also like that each day is a little bit different. If it was always the same problem to solve, it would be boring. I’m coding most days, but there is a lot of variation that keeps things interesting. ## Óscar Martín, Senior Engineer ![Oscar Martin, Senior Engineer at VGV](/assets/images/blog/meet-the-unicorns-elias-traci-oscar/body-2.png) **Why did you first become interested in tech?** Childhood memories! When I was five years old, my sister got a computer with Windows 98. I started to grow interested in the tech world when I was playing video games with her. **What is a typical day like?**  As a Senior Engineer, my first priority is to make sure that nobody has any blockers to start their day. Usually my day starts by reviewing pending conversations in different threads. Once all of them are resolved, I proceed to review open PRs and help people if they have questions on any open discussions. After that, I take a look at the project board. We have a column for blocked items, so I prioritize them to see if there is any update on the topic and then we can move the task to TODO. If there is no update on a blocked ticket for more than a day, I will raise the conversation. This is key when working on big teams or teams that are working across multiple companies and communication is less agile. You would be surprised by the number of tickets that move to IN-PROGRESS/TODO after doing a daily check on the BLOCKED column. If I have short time slots between meetings, I prefer not to start coding since I feel less productive. Instead, I review Slack conversations or PRs, or even check tasks for next sprints. If the time between meetings is greater than 30 mins, then usually I block off my time to totally focus on completing my tasks, meaning no distractions from Slack or any other conversations.  **What’s one thing that has surprised you about being a Flutter Engineer/your role at VGV?** When working on projects in the past, I would just get something to work, then deliver that. Here, I always have the feeling that VGV is going beyond “just working.” Sometimes that means having to go beyond the code. For example, looking at the app, I might ask the question: it makes more sense to move this button here, can we be flexible and change the design? As a team, we’re always thinking about the bigger picture. I’ve also never found a company with so many people trying to help and share their technical expertise. When I run into a problem, I will turn to our internal Slack and 99% of the time, I get an answer. Often, I don’t have to look beyond that. Not only do my teammates have the answer, but they are willing to take the time to help explain it. ### An introduction to Flame Behaviors Source: https://verygood.ventures/blog/build-games-with-flame-behaviors/ We have all read articles about how to structure scalable Flutter applications (if you haven't, here is a very good recommendation on [layered architecture in Flutter](https://verygood.ventures/blog/very-good-flutter-architecture/)). What if you want to write a game in Flutter? Writing games in Flutter is quite easy thanks to the [Flame Engine](https://flame-engine.org/), but designing your game and making sure your code is scalable and structured might be more of a challenge. ## Building scalable games with the Flame Behaviors package At VGV, we have some experience working on games. We even teamed up with the Flutter team to create [I/O Pinball](https://pinball.flutter.dev/) for Google I/O ([read more about that here](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d)). As we worked on these games, we had the same questions: - How do we scale the game? - How do we test the game's functionality? - How do we structure the game? These questions helped us come up with the [Flame Behaviors](https://pub.dev/packages/flame_behaviors) package, which provides a structured way of writing game code that is both scalable and testable. It does this by applying [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) in the form of Entities and Behaviors. An entity is a visual representation of a game object. Each entity can have one or more behaviors to define how it should behave within your game. These behaviors handle a single aspect of an entity and can be made generic so they are reusable over different types of entities. By separating the entity's behavioral aspects into behaviors, we can then test each behavior without having to worry about the others. By defining these entities, we can describe our game without having to touch code. The Flame Behaviors [repository](https://github.com/VGVentures/flame_behaviors_pong_example) has a good breakdown of entities and behaviors that we'll address in this article: *Imagine you want to build an old school* [*Pong*](https://en.wikipedia.org/wiki/Pong) *game. At its very core there are two objects: a paddle and a ball. If you have a look at the paddle, you could say its game logic is: move up and move down. The ball has the simple game logic of: on collision with a paddle reverse the movement direction.* *These objects, paddles and balls, are what we call entities and the game logic we just described are their behaviors. By applying these behaviors to each individual entity we get the core gameplay loop of Pong: hitting balls with our paddles until we win.* ## Building Pong with Flame Behaviors To show how we can use Flame Behaviors to structure games, we are going to remake Pong. The first step in creating any new game is analyzing the gameplay loop and determining our entities and behaviors. As noted above, these will be the entities: - **A ball** that will move over the playfield. - **Two paddles** that players use to try and reflect the ball away from them. The gameplay loop also consists of the different behaviors each entity can hold. The paddle is the easiest to describe in behaviors, as it only needs to be able to move up and down the playfield. We can call this the moving behavior of the paddle. The ball is, compared to the paddle, more complex in terms of its behaviors: - **Moving behavior**: The ball needs to be able to move across the whole playfield. - **Colliding behavior**: The ballneeds to be able to collide with any paddle to reverse its own velocity. - **Scoring behavior**: When the ball hits either the left or the right side of the playfield, the opposite player scores a point. ## Setting up our game project Let's start by creating our project by running the following command: ```shell flutter create pong_game ``` Once that command succeeds we can add both the [**flame**](https://pub.dev/packages/flame) and [**flame_behaviors**](https://pub.dev/packages/flame_behaviors) packages: ```shell flutter pub add flame flame_behaviors ``` Now that that the project files are set up we can prepare the **lib** directory for our game: ```txt ├── lib/ | ├── entities/ │ │ ├── paddle/ | | | └── paddle.dart │ │ ├── ball/ | | | └── ball.dart │ │ └── entities.dart │ └── pong_game.dart │ └── main.dart ``` The **main.dart** will start up the pong game: ```dart void main() { runApp(GameWidget(game: PongGame()); } ``` We can rewrite our **pong\_game.dart** to reflect the following code: ```dart class PongGame extends FlameGame with HasKeyboardHandlerComponents, HasCollisionDetection { /// Default paint used in the game. static final paint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; @override Future onLoad() async { // Set the viewport to the original game's size. camera.viewport = FixedResolutionViewport(Vector2(512, 256)); } @override @mustCallSuper KeyEventResult onKeyEvent( RawKeyEvent event, Set keysPressed, ) { super.onKeyEvent(event, keysPressed); // Return handled to prevent macOS noises. return KeyEventResult.handled; } } ``` There are a few things going on in the code. First we have a **PongGame** that uses the mixin **HasKeyboardHandlerComponents**. This detects keyboard input, which we will need later for one of the behaviors. The game also uses the mixin **HasCollisionDetection**, which enables collision within the game and checking for collisions on every frame. In the **onLoad** method, we set the camera's viewport to a resolution of 512x256 pixels. Everything that is being rendered will be rendered within that resolution (while this is not the original Pong resolution, it does make it easier for us to position our paddles on the screen so we don't have to work with dynamic resolutions). We also overwrite the **onKeyEvent** method from the **HasKeyboardHandlerComponents** mixin because on macOS if you don't return the **KeyEventResult.handled** value, the window will play an error sound (see [this issue](https://github.com/flutter/flutter/issues/74287) for more explanation). ## Creating the paddle entity and its behaviors The first entity that we are going to implement is the paddle. Let's edit the **paddle.dart** file to represent the following code: ```dart /// {@template paddle} /// A paddle that can be controlled by the user. /// {@endtemplate} class Paddle extends PositionedEntity { Paddle._({ required Vector2 center, required Behavior movingBehavior, }) : super( position: center, size: _paddleSize, anchor: Anchor.center, children: [ RectangleComponent.relative( Vector2.all(1), parentSize: _paddleSize, paint: PongGame.paint, ), RectangleHitbox(), ], behaviors: [movingBehavior], ); Paddle._withKeys({ required Vector2 center, required LogicalKeyboardKey upKey, required LogicalKeyboardKey downKey, }) : this._( center: center, movingBehavior: KeyboardMovingBehavior( upKey: upKey, downKey: downKey, ), ); /// {@macro paddle} /// /// Uses arrow keys. Paddle.arrows({ required Vector2 center, }) : this._withKeys( center: center, upKey: LogicalKeyboardKey.arrowUp, downKey: LogicalKeyboardKey.arrowDown, ); /// {@macro paddle} /// /// Uses WASD keys. Paddle.wasd({ required Vector2 center, }) : this._withKeys( center: center, upKey: LogicalKeyboardKey.keyW, downKey: LogicalKeyboardKey.keyS, ); static final Vector2 _paddleSize = Vector2(8, 50); } ``` We start by defining a **Paddle** class that extends an **Entity**, which is just a **PositionComponent** with some extra APIs related to behaviors. The paddle will be controlled through keyboard inputs. To make this possible, we define two public constructors, **Paddle.wasd** and **Paddle.arrows**. Each calls the private constructor **Paddle.\_withKeys**, which in turn calls the main private constructor. This constructor requires a behavior that will define its moving behavior. The **Paddle.\_withKeys** passes a **KeyboardMovingBehavior** with the up and down arrow keys set by either the **Paddle.wasd** or **Paddle.arrows** constructor. By applying multiple constructors that eventually call the main private constructor, we can easily add a new type of moving behavior later without having to duplicate code. The main constructor defines how it looks by passing components as **children** and the kind of behaviors it has by passing default behaviors to the **behaviors** argument. One thing to note is that the paddle has a **RectangleHitbox** as one of its children, which allows for collision detection in Flame. Because the paddle does not have its own collision behaviors, we don't need to do anything else. When we get to the ball entity, we will showcase how collision behaviors work. ### The moving behavior As described above, the moving behavior will be controlled by keyboard input. For that we need to create a **keyboard\_moving\_behavior.dart** that will live in a **behaviors** directory within the **lib/entities/paddle** directory: ```text ├── lib/ | ├── entities/ │ │ ├── paddle/ │ │ │ ├── behaviors/ │ │ │ │ ├── keyboard_moving_behavior.dart │ │ │ │ └── behaviors.dart | | | └── paddle.dart ``` Then create a barrel file called **behaviors.dart** in which we export the newly created **keyboard\_moving\_behavior.dart**: ```dart export 'keyboard_moving_behavior.dart'; ``` Let's edit the **keyboard\_moving\_behavior.dart** file and add the following code: ```dart /// {@template keyboard_moving_behavior} /// A behavior that makes a paddle move up and down based on the user's /// keyboard input. /// {@endtemplate} class KeyboardMovingBehavior extends Behavior with KeyboardHandler, HasGameRef { /// {@macro keyboard_moving_behavior} KeyboardMovingBehavior({ this.speed = 100, required this.downKey, required this.upKey, }); /// The up key. final LogicalKeyboardKey upKey; /// The down key. final LogicalKeyboardKey downKey; /// The speed at which the paddle moves. final double speed; /// The direction the paddle is moving in. /// /// 0 is not moving, -1 is moving up, 1 is moving down. double _movement = 0; @override bool onKeyEvent(RawKeyEvent event, Set keysPressed) { if (keysPressed.contains(upKey)) { _movement = -1; } else if (keysPressed.contains(downKey)) { _movement = 1; } else { _movement = 0; } return super.onKeyEvent(event, keysPressed); } @override void update(double dt) { parent.position.y += _movement * speed * dt; parent.position.y = parent.position.y.clamp( parent.size.y / 2 + Field.halfWidth, gameRef.size.y - parent.size.y / 2 - Field.halfWidth, ); } } ``` As you can see, this behavior is quite compact, simply because it only has one goal: move a paddle up or down based on which key is pressed. The class extends a **Behavior** that can only have a parent entity of the type **Paddle**. We add the **KeyboardHandler** mixin from Flame so that we can detect key events and overwrite the **onKeyEvent** to check if the **upKey** is pressed. If it is, we set the **\_movement** to **\-1** to indicate that we are moving upwards. If the **downKey** is pressed we set it to **1**, if neither keys are pressed, we reset it to **0**. Then in the update method, we update our parent position (the paddle) by multiplying the speed by our movement (to either move it up or down), and then multiply that by the delta value **dt**. Finally, we clamp the y-axis so that our paddle can't get out of the playfield bounds. ## Creating the ball entity and its behaviors As mentioned earlier, the ball entity consists of multiple behaviors. Behaviors may also require persistent data, which should live on the entity. For instance, if we talk about the ball's moving behavior, there is a velocity value that indicates which direction the ball is moving, but does not indicate anything about the moving behavior. This kind of data should always live on the entity. Let's change the code in the **ball.dart** file: ```dart /// {@template ball} /// A ball that will move over the field. /// {@endtemplate} class Ball extends PositionedEntity with HasGameRef { /// {@macro ball} Ball() : this._(); Ball._({ Vector2? startVelocity, super.position, Iterable? behaviors, }) : super( size: _ballSize, anchor: Anchor.center, children: [ CircleComponent.relative(1, parentSize: _ballSize) ..paint = PongGame.paint ], behaviors: [ PropagatingCollisionBehavior(CircleHitbox()), if (behaviors != null) ...behaviors, ], ) { if (startVelocity != null) { velocity.setFrom(startVelocity); } } static final _ballSize = Vector2.all(8); /// The maximum speed of the ball. static const maxSpeed = 128.0; /// The velocity of the ball. final velocity = Vector2.zero(); /// The range of angle in degrees. final range = 45; } ``` Just like the paddle entity, the ball has a private constructor and an entry constructor. The private constructor defines how the ball looks by passing a **CircleComponent** as one of its children. In its behaviors, we define a **PropagatingCollisionBehavior** that has a **CircleHitbox**. This is a behavior that comes from Flame Behaviors and propagates any collision to the correct **CollisionBehavior**, which we will define later on. We also define a property called **maxSpeed**, which will be used to set the **velocity** of the ball, and finally we have a **range** value. This value describes the range of angles that the ball can go when it starts moving. Let's use these values and create a method that allows us to reset the ball and move it in a random direction: ```dart class Ball extends Entity with HasGameRef { ... /// Resets the ball to its initial position. void reset([Random? rnd]) { final random = rnd ?? Random(); final angle = (-range + random.nextInt(range) + range) * degrees2Radians; final goingLeft = random.nextBool() ? -1 : 1; velocity.setValues( cos(angle) * goingLeft * maxSpeed, sin(angle) * goingLeft * maxSpeed, ); position.setFrom(gameRef.size / 2); } } ``` The reset method does four things: - Randomly calculates an angle between **\-range..+range**. - Randomly indicates if the ball should go left or to the right. - Sets the velocity based on the angle and direction and multiplied by the **maxSpeed**. - Finally, resets the ball's position to the center of the screen. The reset method allows us to start the game with the ball at the center and have it move in a random direction at an angle. We can call the method every time we need to reset the ball, for instance when the ball scores a point. Before we start adding any of the ball behaviors, let's update the file structure to represent the following: ```text ├── lib/ | ├── entities/ │ │ ├── ball/ │ │ │ ├── behaviors/ │ │ │ │ ├── moving_behavior.dart │ │ │ │ ├── paddle_colliding_behavior.dart │ │ │ │ ├── scoring_behavior.dart │ │ │ │ └── behaviors.dart | | | └── ball.dart ``` Inside the **behaviors.dart**, export the newly created behaviors files. ### The moving behavior The moving behavior for the ball is quite simple. We only need to update the ball's position based on its velocity and then confirm if we have hit either the top or the bottom of the playfield. If we do hit the top or bottom, the ball needs to invert its velocity on the y-axis. Let's update the **moving\_behavior.dart** file: ```dart /// {@template moving_behavior} /// A behavior that makes the ball move in a certain direction. /// /// If it hits the top or bottom of the field it will reverse the velocity /// y-axis. /// {@endtemplate} class MovingBehavior extends Behavior with HasGameRef { @override void update(double dt) { parent.position += parent.velocity * dt; final hitTop = parent.position.y - parent.size.y <= 0; final hitBottom = parent.position.y >= gameRef.size.y - Field.halfWidth - parent.size.y / 2; // If we hit top or bottom, reverse the velocity value. if (hitTop || hitBottom) { parent.velocity.y *= -1; } } } ``` We can now update our **Ball** entity and pass the newly defined **MovingBehavior**: ```dart class Ball extends Entity with HasGameRef { Ball() : this._( behaviors: [ ... MovingBehavior(), ], ); ... } ``` ### The paddle colliding behavior Our ball needs to be able to collide with a paddle, and when it does, it should invert the velocity x-axis. To do this, we need to define a **CollisionBehavior** that has a **Collider** of the type **Paddle**. As mentioned before when we defined the **Ball** entity, there is a **PropagatingCollisionBehavior** that propagates any collision to the correct **CollisionBehavior**. By adding a **CollisionBehavior** for the **Paddle** entity, the **PropagatingCollisionBehavior** will then be able to confirm if we are colliding with a **Paddle**. If we do, it will call the **onCollision** methods with the entity with which we are colliding. This sounds complex, but it all boils down to the following code: ```dart /// {@template paddle_colliding_behavior} /// A behavior that collides with the paddle and bounces it off by reversing /// the velocity x-axis. /// {@endtemplate} class PaddleCollidingBehavior extends CollisionBehavior { @override void onCollisionStart(Set intersectionPoints, Paddle other) { parent.velocity.x *= -1; } } ``` Thanks to the **PropagatingCollisionBehavior** we don't have to handle collision logic and we can just focus on writing what should happen if we do collide with a **Paddle**. We can add our newly created **PaddleCollidingBehavior** to our ball constructor: ```dart class Ball extends Entity with HasGameRef { Ball() : this._( behaviors: [ ... PaddleCollidingBehavior(), ], ); ... } ``` ### The scoring behavior The scoring behavior has the same logic that we implemented for velocity inversion of the ball's moving behavior, but in this case, we have to check if the ball hits the left or right side of the playfield. If so, then we assign a point to the player opposite the side that was hit. Finally, we reset the ball to start a new round. The **scoring\_behavior** can be updated like this: ```dart /// {@template scoring_behavior} /// A behavior that adds a point to the score when the ball hits the other side. /// /// It also resets the ball to the center of the field. /// {@endtemplate} class ScoringBehavior extends Behavior with HasGameRef { @override void update(double dt) { final hitLeft = parent.position.x - parent.size.x <= 0; final hitRight = parent.position.x >= gameRef.size.x - parent.size.x / 2; // If we hit the sides, reset the ball. if (hitLeft) { gameRef.playerOneScore!.score++; parent.reset(); } else if (hitRight) { gameRef.playerTwoScore!.score++; parent.reset(); } } } ``` Just like before, we can now add our **ScoringBehavior** to the ball's constructor: ```dart class Ball extends Entity with HasGameRef { Ball() : this._( behaviors: [ ... ScoringBehavior(), ], ); ... } ``` ## Testing entities and behaviors At VGV, we love testing, so it should not come as a surprise that the Flame Behaviors package is 100% testable. When writing tests for entities, we only have to focus on the entity's data and methods. Any behavior that an entity might have should contain their own test files. This allows us to focus on testing only the specific behaviors of entities and keeps our test files compact and ordered. We can test the entities and behaviors in this Pong example by using the [testing library](https://pub.dev/packages/flame_test) from Flame. To test an entity, we can define a test constructor that exposes parameters to make testing easier. Let's add such a constructor to the ball entity: ```dart class Ball extends Entity with HasGameRef { ... /// {@macro ball} /// /// Test constructor. @visibleForTesting Ball.test({ Behavior? behavior, Vector2? velocity, Vector2? position, }) : this._( startVelocity: velocity, position: position, behaviors: [if (behavior != null) behavior], ); ... } ``` This test constructor can now be used to test both the ball entity and any behavior that it might have. Here is an example of the test file for the ball entity: ```dart class _MockRandom extends Mock implements Random {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); group('Ball', () { flameTester.test( 'loads correctly', (game) async { final ball = Ball(); await game.ready(); await game.ensureAdd(ball); expect(game.contains(ball), isTrue); }, ); flameTester.test( 'reset ball to center with a random direction', (game) async { final random = _MockRandom(); when(() => random.nextInt(any())).thenReturn(0); when(random.nextBool).thenReturn(false); final ball = Ball.test( position: Vector2.zero(), velocity: Vector2.zero(), ); await game.ready(); await game.ensureAdd(ball); expect(ball.position, closeToVector(0, 0)); expect(ball.velocity, closeToVector(0, 0)); ball.reset(random); expect(ball.position, equals(game.size / 2)); expect(ball.velocity, closeToVector(Ball.maxSpeed, 0)); }, ); }); } ``` As you can see, the tests are focused on the ball's functionality, not its behaviors, which are tested separately. A good example of a behavior test is the **PaddleCollidingBehavior** of the ball: ```dart class _MockPaddle extends Mock implements Paddle {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('PaddleCollidingBehavior', () { test('reverse x-axis of velocity when hitting a paddle', () { final behavior = PaddleCollidingBehavior(); final ball = Ball.test( behavior: behavior, velocity: Vector2(-10, 0), position: Vector2.zero(), ); expect(ball.velocity.x, equals(-10)); behavior.onCollisionStart({}, _MockPaddle()); expect(ball.velocity.x, equals(10)); }); }); } ``` Both entities and behaviors are easy to test. Since a behavior always describes a single aspect of an entity, there should be a test file for each separate behavior. For more examples of testing entities and behaviors, see the test directory in the [repository](https://github.com/VGVentures/flame_behaviors_pong_example). ## Games are so much more than entities and behaviors We have mostly talked about entities and behaviors in this article, but a game consists of quite a lot of additional elements: backgrounds, title screens, game modes, options, and so much more. While we did not cover these elements in this article, we did implement all of these in the Pong game. To see how, take a closer look at the full code in the [repository](https://github.com/VGVentures/flame_behaviors_pong_example). ## Summary We now know how to design our games by using entities and their behaviors. We can implement these elements by using Flame Behaviors, which allows us to create a scalable and well-structured code base. To top it off, we can easily test every single behavior without having to worry about overlapping code functionality. We hope this sheds some light on how easy it is to write games in Flutter while maintaining best practices in your codebase. We can't wait to see what kind of games everyone will be making with Flame Behaviors! *Felix Angelov contributed to this article.* ### Build a Flutter app with Very Good CLI and Supabase Source: https://verygood.ventures/blog/flutter-app-very-good-cli-supabase/ In this tutorial we're going to take the Flutter example that Supabase provides and rewrite it using VGV tooling, such as [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli), [Flutter Bloc](https://pub.dev/packages/flutter_bloc), layered architecture, and the most important thing, 100% test coverage 🧪. This tutorial is based on the [Quickstart: Flutter](https://supabase.com/docs/guides/with-flutter) tutorial that Supabase has on their website. It will target Android, iOS, and the web. Let's get started! 🙌 ## But first, what is Supabase? Supabase is an open source Firebase alternative. As a quick overview, Supabase offers you a bunch of services, like authentication with different providers (*email*, *Apple*, *Azure*, *Discord*, *GitHub*, *GitLab*, *Bitbucket*, *Slack* and much more), database (PostgreSQL), storage, functions, etc. If you're looking to have more auth providers than the typical ones (*email/password*, *Twitter*, *Facebook*, *Google*, and *Apple*) or a SQL database instead of a NoSQL database, you could consider Supabase for your project. To learn more about Supabase, [visit their official website here](https://supabase.com/). ## 💡Overview These are the things that we are going to cover in this tutorial: - Login with email using deep linking. - Handle user status (*authenticated* or *unauthenticated*) to know where we need to navigate using [Flow Builder](https://pub.dev/packages/flow_builder). - Create a simple database with a script on the *Supabase SQL Editor* console. - Add rules to the database. - Update user information in the database. ## Supabase initial configuration ### Create a new project The first step is to visit the Supabase website and create a login. When we have access to the dashboard, we can create a new organization and project. - Click on *New Project* and select your organization (if you don't have one, you should create a new one by clicking on *New organization*). The next step is to fill out the form to create a new project. ![Supabase new project](/assets/images/blog/flutter-app-very-good-cli-supabase/body-0.png) - We can now see the new project on our dashboard. ![Dashboard with new project](/assets/images/blog/flutter-app-very-good-cli-supabase/body-1.png) ### Create a database with SQL Editor To create a new database in our project, we should navigate to SQL Editor where we can create a script to create the table and the rules to apply in this database. ![SQL Editor](/assets/images/blog/flutter-app-very-good-cli-supabase/body-2.png) In this case, we are going to create a table called *account*. We need to modify the *User Management Started* script and add the following code: ```sql -- Create a table for Public Account create table account ( id uuid references auth.users not null, username text unique, companyName text, primary key (id), unique(username), constraint username_length check (char_length(username) >= 3) ); alter table account enable row level security; create policy "Public account are viewable by everyone." on account for select using (true); create policy "Users can insert their own account." on account for insert with check (auth.uid() = id); create policy "Users can update their own account." on account for update using (auth.uid() = id); -- Set up Realtime! begin; drop publication if exists supabase_realtime; create publication supabase_realtime; commit; alter publication supabase_realtime add table account; ``` Then, we need to click on RUN in the right bottom corner. Finally we have our new database created. ### Supabase table editor Once the database is created, we can navigate throughout the dashboard to view the table and rules. In the left menu, we can navigate to *table editor* to see the account table. It is currently empty, but you can see that the table has three columns *id*, *username* and *companyname*. ![Table editor](/assets/images/blog/flutter-app-very-good-cli-supabase/body-3.png) If you want to check the rules that we created with the script, you can click on *RLS enabled* to see them. ![Account table rules](/assets/images/blog/flutter-app-very-good-cli-supabase/body-4.png) ### Authentication configuration As I mentioned at the beginning, the authentication method that we are going to use in this tutorial is email deep linking. We need to add a redirect URL in our authentication dashboard (this is only if your application is on the web). ![Redirect URL](/assets/images/blog/flutter-app-very-good-cli-supabase/body-5.png) In this case, we are going to use the redirect URL that Supabase provides in the Flutter example. You should add the provider redirect URL if you are building a real project. ```shell io.supabase.flutterquickstart://login-callback/ ``` Now, we have everything set up and the next step is to create our Flutter project! 🙌 ## Flutter project and packages In this section we are going to create the Flutter project and different packages using Very Good CLI. The first step is to activate Very Good CLI. ```shell dart pub global activate very_good_cli ``` Once we have Very Good CLI activated, we can create the Flutter project. We just have to run the following line in the terminal. ```shell very_good create very_good_supabase --desc "Example of a Flutter application using Very Good CLI and Supabase" ``` ### Which packages do we need for this project? 🤔 I've created a few of them under the *package* folder (you should create them in your application root). - **Supabase auth client**: this package is in charge of the *sign-in* and *sign-out* methods on Supabase. - **Supabase database client**: this package is in charge of *get user information* from the database and *updating user information*. - **User repository**: this repository is going to have the ability to use the *SupabaseAuthClient* to call the *sign-in* and *sign-out* methods, use the *SupabaseDatabaseClient* to *get user profile* information, and *update* the user on Supabase. - **Form inputs**: this package hosts the different form inputs to use on the view. - **Email launcher**: opens a default email app on Android and iOS. You can visit the [GitHub repository](https://github.com/VGVentures/supabase_example) to see the code of the *Form inputs* and *Email launcher* packages. Here I'm only going to cover the packages related to Supabase. Let's create them! 😀 #### Supabase auth client First we need to run the following command: ```shell very_good create supabase_auth_client -t flutter_pkg --desc "Supabase auth client" ``` Then we should modify the **pubspec.yaml** file to add the necessary dependencies. ```yaml name: supabase_auth_client description: Supabase auth client publish_to: none environment: sdk: ">=2.17.0 <3.0.0" dependencies: flutter_test: sdk: flutter supabase_flutter: ^0.3.1 dev_dependencies: flutter: sdk: flutter mocktail: ^0.3.0 very_good_analysis: ^3.0.1 ``` Now we can add the logic for this package. In this part we are going to be focused on the sign-in and sign-out methods. ```dart /// {@template supabase_auth_client} /// Supabase auth client /// {@endtemplate} class SupabaseAuthClient { /// {@macro supabase_auth_client} SupabaseAuthClient({ required GoTrueClient auth, }) : _auth = auth; final GoTrueClient _auth; /// Method to do sign in on Supabase. Future signIn({ required String email, required bool isWeb, }) async { try { await _auth.signIn( email: email, options: AuthOptions( redirectTo: isWeb ? null : 'io.supabase.flutterquickstart://login-callback/', ), ); } catch (error, stackTrace) { Error.throwWithStackTrace(SupabaseSignInFailure(error), stackTrace); } } /// Method to do sign out on Supabase. Future signOut() async { try { await _auth.signOut(); } catch (error, stackTrace) { Error.throwWithStackTrace(SupabaseSignOutFailure(error), stackTrace); } } } ``` ### Supabase database client First you need to create the package. ```shell very_good create supabase_database_client -t flutter_pkg --desc "Supabase database client" ``` Then modify the **pubspec.yaml** file. ```yaml name: supabase_database_client description: Supabase database client publish_to: none environment: sdk: ">=2.17.0 <3.0.0" dependencies: equatable: ^2.0.3 flutter: sdk: flutter json_annotation: ^4.5.0 json_serializable: ^6.2.0 mocktail: ^0.3.0 supabase_flutter: ^0.3.1 dev_dependencies: build_runner: ^2.1.11 flutter_test: sdk: flutter very_good_analysis: ^3.0.1 ``` The next step is to add the logic to this package. In this case, this package is going to be in charge of retrieving user information from the account table and also to updating the data on the account table. First, we need to create a *SupabaseUser* model. To do that, we can create a new folder inside *src* called *models*. Then add the new **supabase\_user.dart** file inside it. For this model I used the [Json Serializable](https://pub.dev/packages/json_serializable) package. ```dart /// {@template supabase_user} /// Supabase user model /// {@endtemplate} @JsonSerializable() class SupabaseUser extends Equatable { /// {@macro supabase_user} const SupabaseUser({ String? id, required this.userName, required this.companyName, }) : id = id ?? ''; /// Connect the generated [_$SupabaseUserFromJson] function to the `fromJson` /// factory. factory SupabaseUser.fromJson(Map json) => _$SupabaseUserFromJson(json); /// Id of the user. final String id; /// Name of the supabase user. @JsonKey(name: 'username') final String userName; /// Company name of the supabase user. @JsonKey(name: 'companyname') final String companyName; @override List get props => [id, userName, companyName]; /// Empty Supabase object. static const empty = SupabaseUser( userName: '', companyName: '', ); /// Connect the generated [_$SupabaseUserToJson] /// function to the `toJson` method. Map toJson() => _$SupabaseUserToJson(this); } ``` The next step is to add the logic in the **supabase\_database\_client.dart** file. ```dart /// {@template supabase_database_client} /// Supabase database client /// {@endtemplate} class SupabaseDatabaseClient { /// {@macro supabase_database_client} const SupabaseDatabaseClient({ required SupabaseClient supabaseClient, }) : _supabaseClient = supabaseClient; final SupabaseClient _supabaseClient; /// Method to get the user information by id /// from the profiles database on Supabase. Future getUserProfile() async { try { final response = await _supabaseClient .from('account') .select() .eq('id', _supabaseClient.auth.currentUser?.id) .single() .execute(); final data = response.data as Map return SupabaseUser.fromJson(data); } catch (error, stackTrace) { Error.throwWithStackTrace( SupabaseUserInformationFailure(error), stackTrace, ); } } /// Method to update the user information on the profiles database. Future updateUser({required SupabaseUser user}) async { try { final supabaseUser = SupabaseUser( id: _supabaseClient.auth.currentUser?.id, userName: user.userName, companyName: user.companyName, ); await _supabaseClient .from('account') .upsert(supabaseUser.toJson()) .execute(); } catch (error, stackTrace) { Error.throwWithStackTrace( SupabaseUpdateUserFailure(error), stackTrace, ); } } } ``` ### User repository The first step is to create the package. ```shell very_good create user_repository -t flutter_pkg --desc "A package which manages the user domain." ``` The second step is to modify the **pubspec.yaml** file to add the dependencies. ```yaml name: user_repository description: A package which manages the user domain publish_to: none environment: sdk: ">=2.17.0 <3.0.0" dependencies: equatable: ^2.0.3 flutter: sdk: flutter mocktail: ^0.3.0 supabase_auth_client: path: ../supabase_auth_client supabase_database_client: path: ../supabase_database_client supabase_flutter: ^0.3.1 dev_dependencies: flutter_test: sdk: flutter very_good_analysis: ^3.0.1 ``` Now we can add the logic for this package. In this repository we can use the *SupabaseAuthClient* and *SupabaseDatabaseClient* to be able to use the methods inside of each class. With this repository we can do the following: - Sign-in - Sign-out - Fetch User information - Update user ```dart /// {@template user_repository} /// A package which manages the user domain. /// {@endtemplate} class UserRepository { /// {@macro user_repository} UserRepository({ required SupabaseAuthClient authClient, required SupabaseDatabaseClient databaseClient, }) : _authClient = authClient, _databaseClient = databaseClient; final SupabaseAuthClient _authClient; final SupabaseDatabaseClient _databaseClient; /// Method to access the current user. Future getUser() async { final supabaseUser = await _databaseClient.getUserProfile(); return supabaseUser.toUser(); } /// Method to update user information on profiles database. Future updateUser({required User user}) { return _databaseClient.updateUser(user: user.toSupabaseUser()); } /// Method to do signIn. Future signIn({required String email, required bool isWeb}) async { return _authClient.signIn(email: email, isWeb: isWeb); } /// Method to do signOut. Future signOut() async => _authClient.signOut(); } extension on SupabaseUser { User toUser() { return User( id: id, userName: userName, companyName: companyName, ); } } extension on User { SupabaseUser toSupabaseUser() { return SupabaseUser( id: id, userName: userName, companyName: companyName, ); } } ``` We're finished configuring the packages. Now we can jump into the business logic and views. Note: These packages need different workflows to work with GitHub workflows. We can find these files in the *.github* folder in the [example repository](https://github.com/VGVentures/supabase_example). ## Supabase in Flutter ### Native configuration The first step is to configure Supabase in our project. First we need to add a few native configurations on Android and iOS. - Android: add these lines to the **AndroidManifest.xml** file. ```xml ``` - iOS: add these lines to the **Info.plist** file. ```xml CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLSchemes io.supabase.flutterquickstart ``` ### Prepare the Flutter project to use Supabase Here we need to do a few things. First, we need to update the **pubspec.yaml** file in our project to be able to use all the packages that we just created, as well as the Supabase dependency. ```yaml name: very_good_supabase description: Example of a flutter application to use Very Good CLI and Supabase version: 1.0.0+1 publish_to: none environment: sdk: ">=2.17.0 <3.0.0" dependencies: bloc: ^8.0.3 email_launcher: path: packages/email_launcher equatable: ^2.0.3 flow_builder: ^0.0.8 flutter: sdk: flutter flutter_bloc: ^8.0.1 flutter_dotenv: ^5.0.2 flutter_localizations: sdk: flutter form_inputs: path: packages/form_inputs intl: ^0.17.0 mockingjay: ^0.3.0 mocktail_image_network: ^0.3.1 supabase_auth_client: path: packages/supabase_auth_client supabase_database_client: path: packages/supabase_database_client supabase_flutter: ^0.3.0 user_repository: path: packages/user_repository dev_dependencies: bloc_test: ^9.0.3 flutter_test: sdk: flutter mocktail: ^0.3.0 very_good_analysis: ^3.0.1 flutter: uses-material-design: true generate: true assets: - assets/images/ - assets/.env ``` The second step is to prepare the **main\_development.dart** file to use the necessary packages and initialize Supabase. ```dart void main() async { WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: 'assets/.env'); await Supabase.initialize( url: dotenv.get('SUPABASE_URL'), anonKey: dotenv.get('ANON_KEY'), ); await bootstrap(() { final authClient = SupabaseAuthClient( auth: Supabase.instance.client.auth, ); final databaseClient = SupabaseDatabaseClient( supabaseClient: Supabase.instance.client, ); final userRepository = UserRepository( authClient: authClient, databaseClient: databaseClient, ); return App(userRepository: userRepository); }); } ``` As you might have noticed, to initialize Supabase, we need to provide the *supabase url* and the *anon key*, which can be found in the project settings. ![Supabase URL and Anon Key on project settings](/assets/images/blog/flutter-app-very-good-cli-supabase/body-6.png) ### Create the features In this project, we are going to have four features: - **Auth States Supabase**: here we are going to add two classes that will tell us if a user is authenticated or not and if the authentication is required. These classes are also going to be used in our widgets. - **App**: this is going to be in charge of listening to the user status (authenticated or unauthenticated) to determine whether to navigate to the login page or account page. - **Login**: this feature is going to host all of the sign-in logic for this project. - **Account**: this feature is going to get the user information and display them into a text field. It will also update the information in the Supabase database and handle the sign-out. #### Supabase states - *AuthStateSupabase* ```dart class AuthStateSupabase extends SupabaseAuthState { @override void onUnauthenticated() { if (mounted) { context.read().add(AppUnauthenticated()); } } @override void onAuthenticated(Session session) { if (mounted) { context.read().add(const AppAuthenticated()); } } @override void onPasswordRecovery(Session session) {} @override void onErrorAuthenticating(String message) {} } ``` - *AuthStateSupabaseRequired* ```dart class AuthRequiredState extends SupabaseAuthRequiredState { @override void onUnauthenticated() { if (mounted) { context.read().add(AppUnauthenticated()); } } } ``` #### App This feature is in charge of recovering the Supabase session to know if a user was authenticated in the previous session or not. With this information, this feature is going to determine where the application should navigate. We are going to use [Flow Builder](https://pub.dev/packages/flow_builder) to handle this navigation. Here we have the following structure: ```txt ├── lib | ├── app │ │ ├── bloc │ │ │ └── app_bloc.dart | | | └── app_event.dart | | | └── app_state.dart │ │ └── view │ │ | ├── app.dart │ │ | └── app_view.dart | | | └── view.dart | | └── routes | | | └── routes.dart │ ├── app.dart ``` - *AppView*: here we are extending the class with an *AuthStateSupabase* and not a *StatefulWidget*. This is important to know if the user is authenticated. ```dart class AppView extends StatefulWidget { const AppView({super.key}); @override State createState() => _AppViewState(); } class _AppViewState extends AuthStateSupabase { @override void initState() { super.initState(); recoverSupabaseSession(); // <--- Here we recover the supabase session } @override Widget build(BuildContext context) { return MaterialApp( title: 'Very Good Supabase', theme: ThemeData( appBarTheme: const AppBarTheme(color: Colors.teal), colorScheme: const ColorScheme.light().copyWith( primary: Colors.teal, ), ), localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, ], supportedLocales: AppLocalizations.supportedLocales, home: FlowBuilder( state: context.select((AppBloc bloc) => bloc.state.status), onGeneratePages: onGenerateAppViewPages, ), ); } } ``` - *AppBloc* ```dart class AppBloc extends Bloc { AppBloc() : super(const AppState()) { on(_onAppAuthenticated); on(_onLogoutRequest); } Future _onAppAuthenticated( AppAuthenticated event, Emitter emit, ) async { emit(state.copyWith(status: AppStatus.authenticated)); } Future _onLogoutRequest( AppUnauthenticated event, Emitter emit, ) async { emit(state.copyWith(status: AppStatus.unauthenticated)); } } ``` - Routes: this method *onGenerateAppViewPages* is used in our *AppView*. ```dart List onGenerateAppViewPages(AppStatus state, List> pages) { switch (state) { case AppStatus.unauthenticated: return [LoginPage.page()]; case AppStatus.authenticated: return [AccountPage.page()]; } } ``` #### Login This feature is in charge of sign-in and checking the email text field using the form inputs package that we created. The structure is: ```txt ├── lib | ├── login │ │ ├── bloc │ │ │ └── login_bloc.dart | | | └── login_event.dart | | | └── login_state.dart │ │ └── view │ │ | ├── login_page.dart │ │ | └── login_view.dart | | | └── view.dart │ ├── login.dart ``` - *LoginView*: here we are going to have all the widgets needed to log in to our app. Also, we are going to use a *AuthStateSupabase* class to know if the user is currently logged in or not. In addition, we are going to use the input form package to check if the email is correct. ```dart class LoginView extends StatefulWidget { const LoginView({super.key}); @override State createState() => _LoginViewState(); } class _LoginViewState extends AuthStateSupabase { @override Widget build(BuildContext context) { return ListView( padding: const EdgeInsets.symmetric( vertical: 50, horizontal: 32, ), children: [ const _Header(), const SizedBox(height: 18), const _EmailInput(), const SizedBox(height: 28), const _SendEmailButton(), const SizedBox(height: 28), if (!kIsWeb) OpenEmailButton() ], ); } } class _Header extends StatelessWidget { const _Header(); @override Widget build(BuildContext context) { return Column( key: const Key('loginView_header'), crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ SizedBox.square( dimension: 100, child: Assets.images.supabase.image(), ), SizedBox( width: 200, height: 100, child: Assets.images.vgv.image(), ), ], ), Padding( padding: const EdgeInsets.only(top: 100), child: Text( 'Sign in via the magic link', style: Theme.of(context).textTheme.headline5, ), ), ], ); } } class _EmailInput extends StatelessWidget { const _EmailInput(); @override Widget build(BuildContext context) { final isInProgress = context.select( (LoginBloc bloc) => bloc.state.status == FormzSubmissionStatus.inProgress, ); return TextFormField( key: const Key('loginView_emailInput_textField'), readOnly: isInProgress, onChanged: (email) { context.read().add(LoginEmailChanged(email)); }, decoration: const InputDecoration(labelText: 'Email'), ); } } class _SendEmailButton extends StatelessWidget { const _SendEmailButton(); @override Widget build(BuildContext context) { final state = context.watch().state; return ElevatedButton( key: const Key('loginView_sendEmail_button'), onPressed: state.status.isInProgress || !state.valid ? null : () => context.read().add( LoginSubmitted( email: state.email.value, isWeb: kIsWeb, ), ), child: Text( state.status.isInProgress ? 'Loading' : 'Send Magic Link', ), ); } } class OpenEmailButton extends StatelessWidget { OpenEmailButton({ EmailLauncher? emailLauncher, super.key, }) : _emailLauncher = emailLauncher ?? EmailLauncher(); final EmailLauncher _emailLauncher; @override Widget build(BuildContext context) { final state = context.watch().state; return OutlinedButton( key: const Key('loginView_openEmail_button'), onPressed: state.status.isInProgress || !state.valid ? null : _emailLauncher.launchEmailApp, child: const Text('Open Email App'), ); } } ``` - *LoginBloc*: the main aim of this class is communicating with the *UserRepository* to do login. Also, here we are going to handle all of the possible states of the email text field when the user changes the value. ```dart class LoginBloc extends Bloc { LoginBloc(this._userRepository) : super(const LoginState()) { on(_onSignIn); on(_onEmailChanged); } final UserRepository _userRepository; void _onEmailChanged(LoginEmailChanged event, Emitter emit) { final email = Email.dirty(event.email); emit( state.copyWith( email: email, valid: Formz.validate([email]), ), ); } Future _onSignIn( LoginSubmitted event, Emitter emit, ) async { try { emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); await _userRepository.signIn( email: event.email, isWeb: event.isWeb, ); emit(state.copyWith(status: FormzSubmissionStatus.success)); } catch (error) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error); } } } ``` #### Account This feature is in charge of multiple things: - Retrieving information from the database. - Updating user information in the database. - Signing out. - Checking the inputs of the *userName* and *companyName* text fields. The structure is: ```txt ├── lib | ├── account │ │ ├── bloc │ │ │ └── account_bloc.dart | | | └── account_event.dart | | | └── account_state.dart │ │ └── view │ │ | ├── account_page.dart │ │ | └── account_view.dart | | | └── view.dart │ ├── account.dart ``` - *AccountView*: this class is going to extend to *AuthRequiredState*, which determines if the user is logged in. This is necessary because we are going to show the user account information. ```dart class AccountView extends StatefulWidget { const AccountView({super.key}); @override AccountViewState createState() => AccountViewState(); } class AccountViewState extends AuthRequiredState { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Account')), body: BlocConsumer( listener: (context, state) { if (state.status.isUpdate) { context.showSnackBar(message: 'Updated!'); } }, buildWhen: (previous, current) => current.status.isSuccess || current.status.isUpdate || current.status.isEditing, builder: (context, state) { return ListView( padding: const EdgeInsets.all(28), children: const [ _Header(), _UserNameTextField(), _UserCompanyNameTextField(), SizedBox(height: 50), _UpdateUserButton(), SizedBox(height: 18), _SignOutButton(), ], ); }, ), ); } } class _Header extends StatelessWidget { const _Header(); @override Widget build(BuildContext context) { return Column( key: const Key('account_header'), crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ SizedBox.square( dimension: 100, child: Assets.images.supabase.image(), ), SizedBox( width: 200, height: 100, child: Assets.images.vgv.image(), ), ], ), Padding( padding: const EdgeInsets.only(top: 100), child: Text( 'Update your information', style: Theme.of(context).textTheme.headline5, ), ), ], ); } } class _UserNameTextFieldState extends State<_UserNameTextField> { final _controller = TextEditingController(); @override Widget build(BuildContext context) { return BlocConsumer( listener: (context, state) { if (state.status.isSuccess) { _controller.text = state.userName.value; } }, builder: (context, state) { return Padding( padding: const EdgeInsets.only(top: 18), child: TextFormField( controller: _controller, key: const Key('accountView_userName_textField'), readOnly: state.status.isLoading, textInputAction: TextInputAction.next, onChanged: (userName) => context .read() .add(AccountUserNameChanged(userName)), decoration: const InputDecoration(labelText: 'User Name'), ), ); }, ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class _UserCompanyNameTextField extends StatefulWidget { const _UserCompanyNameTextField(); @override State<_UserCompanyNameTextField> createState() => _UserCompanyNameTextFieldState(); } class _UserCompanyNameTextFieldState extends State<_UserCompanyNameTextField> { final _controller = TextEditingController(); @override Widget build(BuildContext context) { return BlocConsumer( listener: (context, state) { if (state.status.isSuccess) { _controller.text = state.companyName.value; } }, builder: (context, state) { return Padding( padding: const EdgeInsets.only(top: 18), child: TextFormField( controller: _controller, key: const Key('accountView_companyName_textField'), readOnly: state.status.isLoading, onChanged: (companyName) => context.read().add( AccountCompanyNameChanged(companyName), ), decoration: const InputDecoration( labelText: 'Company Name', ), ), ); }, ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class _UpdateUserButton extends StatelessWidget { const _UpdateUserButton(); @override Widget build(BuildContext context) { final state = context.watch().state; return ElevatedButton( key: const Key('accountView_update_button'), onPressed: state.status.isLoading || !state.valid ? null : () => context.read().add( AccountUserUpdated( user: User( id: state.user.id, userName: state.userName.value, companyName: state.companyName.value, ), ), ), child: Text(state.status.isLoading ? 'Saving...' : 'Update'), ); } } class _SignOutButton extends StatelessWidget { const _SignOutButton(); @override Widget build(BuildContext context) { final isLoading = context.select( (AccountBloc bloc) => bloc.state.status == AccountStatus.loading, ); return OutlinedButton( key: const Key('accountView_signOut_button'), onPressed: isLoading ? null : () => context.read().add(AccountSignedOut()), child: const Text('Sign Out'), ); } } ``` - *AccountBloc*: this class is in charge of multiple events. Here we need to use *UserRepository* to retrieve the user information, update the database, and sign out. Also, we have other events to handle user interaction with the *userName* and *companyName* text fields. ```dart class AccountBloc extends Bloc { AccountBloc(this._userRepository) : super(const AccountState()) { on(_onGetUserInformation); on(_onUpdateUser); on(_onSignOut); on(_onUserNameChanged); on(_onCompanyNameChanged); } final UserRepository _userRepository; Future _onGetUserInformation( AccountUserInformationFetched event, Emitter emit, ) async { try { emit(state.copyWith(status: AccountStatus.loading)); final user = await _userRepository.getUser(); emit( state.copyWith( status: AccountStatus.success, user: user, userName: UserName.dirty(user.userName), companyName: CompanyName.dirty(user.companyName), ), ); } catch (error) { emit(state.copyWith(status: AccountStatus.error)); addError(error); } } Future _onUpdateUser( AccountUserUpdated event, Emitter emit, ) async { try { emit(state.copyWith(status: AccountStatus.loading)); await _userRepository.updateUser(user: event.user); emit(state.copyWith(status: AccountStatus.update, valid: false)); } catch (error) { emit(state.copyWith(status: AccountStatus.error)); addError(error); } } Future _onSignOut( AccountSignedOut event, Emitter emit, ) async { try { emit(state.copyWith(status: AccountStatus.loading)); await _userRepository.signOut(); emit(state.copyWith(status: AccountStatus.success)); } catch (error) { emit(state.copyWith(status: AccountStatus.error)); addError(error); } } Future _onUserNameChanged( AccountUserNameChanged event, Emitter emit, ) async { final userName = UserName.dirty(event.userName); emit( state.copyWith( status: AccountStatus.edit, userName: userName, valid: Formz.validate([userName, state.companyName]), ), ); } Future _onCompanyNameChanged( AccountCompanyNameChanged event, Emitter emit, ) async { final companyName = CompanyName.dirty(event.companyName); emit( state.copyWith( status: AccountStatus.edit, companyName: companyName, valid: Formz.validate([companyName, state.userName]), ), ); } } ``` ## Demo It's time to see everything working together! Fingers crossed 🤞 It's working! 🎉 #### Extra: Personalize your email body content You can personalize the body of the email that you send after sign-in. It is quite easy to do with Supabase. Go to the *project settings* in your Supabase console and follow the steps. ![Magic link customization](/assets/images/blog/flutter-app-very-good-cli-supabase/body-7.png) ## Summary Supabase is a great tool if you are looking for login options beyond the typical ones like *email/password*, *Twitter*, *Facebook*, *Google*, and *Apple*. You can [see here all the auth providers](https://supabase.com/docs/guides/auth) that Supabase can provide. Also, if you prefer to manage your data in a SQL database, it is a great choice because Supabase uses a PostgreSQL database, in contrast with Firebase. You can find more info in the [official documentation](https://supabase.com/docs/guides/database). I highly recommend you check all the options that Supabase provides because there are tons of them with extensive documentation. Here are a few reference links: - [Database](https://supabase.com/database) - [Authentication](https://supabase.com/auth) - [Storage](https://supabase.com/storage) - [Edge Functions](https://supabase.com/edge-functions) Hope you enjoyed it! If you are interested in testing this application, you can find all the tests working in the [GitHub repository](https://github.com/VGVentures/supabase_example). Thanks for your time! 😃 Happy coding! *Felix Angelov and Óscar Martín contributed to this article.* ### Why Build Apps with Flutter in 2022 Source: https://verygood.ventures/blog/why-build-apps-with-flutter-2022/ Following the past few years marked by the pressures of the pandemic, 2022 is presenting a new set of challenges to product and development teams. The labor market is competitive. Costs are rising. Budgets are tight. All this while consumer expectations and competition for engagement remain higher than ever.  Cross-platform development continues to emerge as a key solution for development teams looking to overcome these challenges and deliver consistent digital experiences for multiple platforms. In doing so, teams can unite around a single framework instead of managing platform-specific teams which can quickly fall out of sync. For years, teams have used tools like React Native, Flutter, Cordova, and Ionic to [build cross-platform apps](https://www.jetbrains.com/lp/devecosystem-2019/), with React Native leading in popularity until last year.  Flutter use in particular is rapidly growing within today’s developer ecosystem. According to the 2021 [State of Developer Ecosystem survey from JetBrains](https://www.jetbrains.com/lp/devecosystem-2021/miscellaneous/), Flutter has “surpassed React Native to become the most popular cross-platform mobile framework.”  To date, [over 500,000 apps have been built with Flutter](https://medium.com/flutter/introducing-flutter-3-5eb69151622f), from small games to production apps built by major global brands. With benefits like a single codebase for multiple platforms and a friendly developer experience, Flutter is a go-to solution for building high-quality mobile applications. We anticipate that web and desktop apps will follow suit now that Flutter is officially stable for web, Windows, macOS, and linux. Beyond these platforms, Flutter on embedded devices is another area where Flutter’s potential is starting to manifest.  We’re proud to be at the forefront of Flutter’s evolution and success. For four years, we’ve been working with companies to build powerful, premium apps for multiple platforms. We’ve [built mobile, web, and desktop apps with Flutter](https://verygood.ventures/success-stories/), [helped guide companies in their transition from native apps to Flutter](https://verygood.ventures/success-stories/betterment/), [created games using Flutter’s existing capabilities](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d), and much more. Here’s why we continue to use Flutter to build high-quality multi-platform app experiences in 2022. ## Team Transformation One of the biggest benefits we’ve seen working with clients at VGV is that Flutter can [unite teams](https://verygood.ventures/blog/betterment-flutter-revitalized-our-codebase/). With Flutter, teams no longer have to be organized around platforms. Instead, they can be organized around features. This particularly benefits mobile teams. Previously, organizations building native apps would have separate teams for iOS and Android. When using Flutter, mobile teams can build for both iOS and Android from the same codebase, eliminating the possibility that one platform team could fall behind.  Flutter can transform entire engineering teams by allowing developers to focus on building features, not platform differences. The [tooling around Flutter is top-notch](https://flutter.dev/development), adding another layer of productivity compared to other native technologies. With a sprawling ecosystem, developers can find high quality open source tools from the community and support from well-known brands like [Canonical](https://ubuntu.com/blog/flutter-and-ubuntu-so-far) and [Microsoft](https://docs.microsoft.com/en-us/dual-screen/flutter/). ## Flutter is Growing & Maturing This year, we saw Flutter take the stage at Google I/O [during the developer keynote](https://youtu.be/qBkyU1TJKDg?t=1814). It was an exciting moment to see Flutter featured alongside other well-known Google products like Chrome and Android. It was the kind of moment that made us take a step back and reflect on how far Flutter has come since its initial release in 2017.  [Flutter reached version 2.0 in March 2021](https://medium.com/flutter/whats-new-in-flutter-2-0-fe8e95ecc65), which brought with it null safety, a huge breaking change. With plenty of advanced warning and a handy migration tool, developers were able to update codebases and packages without many hiccups. The fact that the Flutter Community was able to weather a big breaking change like this demonstrates that Flutter has reached a certain level of maturity.  Most recently, the release of [Flutter 3.0](https://medium.com/flutter/whats-new-in-flutter-3-8c74a5bc32d0) and [Dart 2.17](https://medium.com/dartlang/dart-2-17-b216bfc80c5d) came with a handful of updates that include important investments in the stability and maturity of Flutter and Dart. Flutter was announced to be stable for Linux and macOS, which means that Flutter is now stable on six total platforms: iOS, Android, web, Windows, and now Linux and macOS. There were also a handful of useful updates, such as advanced enum support, new lint rules, performance improvements, and Material Design 3 support. This is the kind of update that indicates Flutter is reaching a comforting level of maturity and gives us faith in the longevity of Flutter.  ## Build (and Learn) in the Open We strongly believe in the value of open source software. The fact that Flutter is an open source framework is a huge reason why we still use it, and recommend it to our clients, today. Just by looking at the [source code](https://github.com/flutter/flutter), developers can see exactly how Flutter works. If we ever run into a problem, we can open an issue on the repo, to which the Flutter team will promptly respond. The Flutter team is also very open about where they are taking Flutter, maintaining an [up-to-date roadmap](https://github.com/flutter/flutter/wiki/Roadmap), as well as providing [annual updates about the strategy behind the UI toolkit](https://medium.com/flutter/flutter-in-2022-strategy-and-roadmap-8c5eaf7c4275). Because Flutter is open source, there are no hidden surprises, which means teams can be confident that what they see is what they get. Most importantly, using Flutter means developers have access to more than just the framework. There is an extensive global community of other passionate developers who are continually open sourcing helpful packages, bug fixes, and relevant content. Whether it’s finding a helpful open source package on [pub.dev](https://pub.dev/) ([check out this list with our favorites](https://verygood.ventures/blog/top-flutter-dart-packages/)), or asking for help from other developers, leaning into open source can greatly improve the experience for anyone using Flutter. ## Do More with Dart Dart is a language that is increasing in popularity. Research from RedMonk shows that Dart is now entering the playing field with other languages that have been around for years, like JavaScript and Python. According to RedMonk analyst James Governor: “Dart is, like so many technologies, a 10 year overnight success story.” We couldn’t agree more. Dart is certainly a language to watch as more teams adopt Flutter and accordingly, enable more developers to use Dart in their day-to-day work. The good news is: we’ve heard multiple times that Dart is extremely easy to learn, especially if you’re coming from another language like Javascript or Swift.  Dart is more than just the language powering Flutter. We’ve seen much interest in exploring Dart on the server. With solutions like [Serverpod](https://serverpod.dev/) and [alfred](https://pub.dev/packages/alfred), we predict that Dart’s full stack capabilities will really shine going forward. We are also exploring this capability with our recently open sourced [Dart Frog](https://verygood.ventures/blog/dart-frog/), an experimental backend framework for Dart. It can be used to spin up a simple backend, as well as create middleware, which may be especially helpful when interacting with a legacy backend. This solution also provides a unified tech stack with shared tooling and models. With Dart Frog, developers familiar with Dart aren’t just restricted to building for the frontend. We hope to see additional explorations in this area as developers continue to expand the limits of what’s possible with Dart. ## Multi-Platform & Multi-Capability Flutter’s multi-platform support has been a significant selling point when talking to companies on the fence about Flutter. We’ll go one step further and say that Flutter’s multi-capability benefits are exciting as well. By multi-capability, we mean that Flutter can be useful for more than building apps that target different screens.  Flutter enables developers to expand beyond traditional app development and into areas that previously may have required entirely new workstreams and tooling. Some of these areas include [building games](https://medium.com/flutter/announcing-the-flutter-casual-games-toolkit-c22e401d8fee), [writing backend code](https://verygood.ventures/blog/dart-frog/), and [creating experiences for embedded devices](https://flutter.dev/multi-platform/embedded). Flutter developers can use familiar tooling without having to learn a new tech stack from scratch. In this context, Flutter becomes a tool that can enable really interesting products, such as a program on a Raspberry Pi ([like this impressive project by Flutter GDE Roman Jaqeuz](https://twitter.com/drcoderz/status/1529099908925358081?s=20&t=6nhE5hq5kxzkuxw_b2RAzw)), a [game with 2D effects and physics](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d), or even a [car infotainment system](https://www.youtube.com/watch?v=zSbsIiluixw&t=1964s). We’re excited to see what’s next! ### Say Hello to the VGV Tooling & Open Source Team Source: https://verygood.ventures/blog/tooling-and-open-source-team/ Today we announce the addition of two teammates to our growing Tooling and Open Source team at Very Good Ventures: Jochum van der Ploeg and Renan Araújo. They will work alongside **Felix Angelov**, Principal Engineer and Head of Architecture. - **Felix Angelov**: Felix leads the Tooling and Open Source team at Very Good Ventures. As an active open source maintainer, Felix has extensive experience creating and maintaining high-quality software including [**flutter_bloc**](https://pub.dev/packages/flutter_bloc), [**equatable**](https://pub.dev/packages/equatable), and [**mason**](https://pub.dev/packages/mason), among others. He also supports a global community of thousands of developers who use bloc library. - **Renan Araújo**: Renan previously worked at Rows, which is focused on harnessing the performant power of Flutter on the desktop spreadsheet application. He maintains some open source Flutter packages, among them the [Flame 2D Game Engine](https://flame-engine.org/) via the [Blue Fire](https://mobile.twitter.com/bluefireoss) team. - [**Jochum van der Ploeg**](https://verygood.ventures/team/jochum-van-der-ploeg/): Jochum is a member of the [Blue Fire](https://mobile.twitter.com/bluefireoss) team, the people behind the [Flame 2D Game Engine](https://flame-engine.org/). He maintains a multitude of open source projects for Flutter, among them the Twilio [Programmable Video](https://pub.dev/packages/twilio_programmable_video) and [Conversations](https://pub.dev/packages/twilio_conversations) plugins. ![Felix Angelov, Renan Araújo, and Jochum van der Ploeg headshots](/assets/images/blog/tooling-and-open-source-team/body-0.png) *From left to right: Felix Angelov, Renan Araújo, Jochum van der Ploeg* ## About the VGV Tooling & Open Source Team The VGV Tooling and Open Source Team team is in charge of upholding one of our key missions at Very Good Ventures: creating knowledge and setting it free. They achieve this through a variety of activities, including: - **Open source stewardship**: The team maintains repositories in our Very Good Open Source organization and beyond, including popular Flutter and Dart packages that have received thousands of stars. They frequently address issues and interface with the community on new features and fixes. - **High-quality open source software**: As influenced by Very Good Ventures coding standards, Felix, Jochum, and Renan will build new open source packages to be used by Flutter and Dart developers around the world. - **Developer tooling**: This team will also create tools, both for our engineering team at VGV and beyond, to increase developer efficiency and happiness. - **Giving back**: This team contributes to the Flutter Community by creating high-quality documentation and tutorials, participating and sponsoring community events, and maintaining a positive open source ecosystem. ## Very Good Open Source You can see all of our open source repositories in one place in our [Very Good Open Source GitHub organization](https://github.com/VeryGoodOpenSource). Popular repositories include: - [**Very Good CLI**](https://github.com/VeryGoodOpenSource/very_good_cli): A scalable starter project built with VGV best practices. - [**Very Good Analysis**](https://github.com/VeryGoodOpenSource/very_good_analysis): The opinionated lint rules we use at VGV. - [**Very Good Workflows**](https://github.com/VeryGoodOpenSource/very_good_workflows): The reusable GitHub Workflows we use at VGV. - [**Very Good Coverage**](https://github.com/VeryGoodOpenSource/very_good_coverage): A GitHub Action to enforce a code coverage threshold with lcov. - [**Dart Frog**](https://github.com/VeryGoodOpenSource/dart_frog): A fast, minimalistic backend framework for Dart (currently experimental). The team also has an active role in maintaining the following popular open source repositories: - [**Bloc library**](https://bloclibrary.dev/): Including **flutter\_bloc**, **bloc\_test**, and more. - [**Mason**](https://github.com/felangel/mason): Tools which allow developers to create and consume reusable templates called bricks. - [**Flame**](https://github.com/flame-engine/flame): A minimalist Flutter game engine managed by Blue Fire. ### Dart on the server with Dart Frog Source: https://verygood.ventures/blog/dart-frog/ We're very excited to announce that we've open sourced [Dart Frog](https://pub.dev/packages/dart_frog), an **experimental**, minimalistic backend framework for Dart 🎉 ## What is Dart Frog? Our goal is to help developers effectively build backends in Dart. Dart Frog is intended to help Flutter and Dart developers maximize their productivity by having a unified tech stack that enables sharing tooling, models, and more! Dart Frog provides a simple core with a small API surface area in order to reduce the learning curve and ramp-up time for developers. It optimizes the process of building backends which aggregate, compose, and normalize data from multiple sources. Dart Frog is built on top of [shelf](https://pub.dev/packages/shelf) and [mason](https://pub.dev/packages/mason) and is inspired by many tools including [remix.run](https://remix.run/), [next.js](https://nextjs.org/), and [express.js](https://expressjs.com/). ## Getting Started 🚀 The easiest way to get started is by installing the [**dart_frog_cli**](https://pub.dev/packages/dart_frog_cli) via [pub.dev](https://pub.dev/). ### Installation 💻 ```shell # Install from pub.dev dart pub global activate dart_frog_cli ``` At this point, **dart\_frog** should be available as a command. You can verify by running **dart\_frog** in your terminal. ```shell A fast, minimalistic backend framework for Dart. Usage: dart_frog [arguments] Global options: -h, --help Print this usage information. --version Print the current version. Available commands: build Create a production build. create Creates a new Dart Frog app. dev Run a local development server. Run "dart_frog help " for more information about a command. ``` ### Create a Project 📦 Use the **dart\_frog** create command to generate a brand new project. ```shell # 🚀 Create a new project called "my_project" dart_frog create my_project ``` ### Start the Dev Server 🏁 Next, we can open the newly created project and start the dev server via: ```shell # 🏁 Start the dev server dart_frog dev ``` This will start the server on [localhost:8080](http://localhost:8080/) with hot-reload enabled ⚡️ ## Creating a new Route 🚏 In Dart Frog, a route consists of an **onRequest** function (called a route handler) exported from a dart file in the **routes** directory. Each endpoint is associated with a routes file based on its filename. Files named, **index.dart** will correspond to a **/** endpoint. For example, if you create **routes/hello.dart** that exports an **onRequest** method like below, it will be accessible via the **/hello** endpoint. ```dart import 'package:dart_frog/dart_frog.dart'; Response onRequest(RequestContext context) { return Response(body: 'Hello World'); } ``` In addition, all route handlers have access to a **RequestContext** which can be used to access the incoming request as well as dependencies provided to the request context (see middleware below). ```dart import 'package:dart_frog/dart_frog.dart'; Response onRequest(RequestContext context) { // Access the incoming request. final request = context.request; // Return a response. return Response(body: 'Hello World'); } ``` We can customize the status code of the response via the **statusCode** parameter on the **Response** object: ```dart import 'package:dart_frog/dart_frog.dart'; Response onRequest(RequestContext context) { return Response(statusCode: 204); } ``` In addition, we can return JSON via the **Response.json** constructor: ```dart import 'package:dart_frog/dart_frog.dart'; Response onRequest(RequestContext context) { return Response.json( body: {'hello': 'world!'}, ); } ``` Route handlers can be synchronous or asynchronous. To convert the above route handlers to async, we just need to update the return type from **Response** to **Future**. We can also add the **async** keyword in order to **await** futures within our handler before returning a **Response**. ```dart import 'package:dart_frog/dart_frog.dart'; Future onRequest(RequestContext context) async { final result = await _someFuture(); return Response.json(body: result); } ``` #### Dynamic Routes 🌓 Dart Frog supports dynamic routes. For example, if you create a file called **routes/posts/\[id\].dart**, then it will be accessible at endpoints: **/posts/1**, **/posts/2**, etc. Routing parameters are forwarded to the **onRequest** method as seen below. ```dart import 'package:dart_frog/dart_frog.dart'; Response onRequest(RequestContext context, String id) { return Response(body: 'post id: $id'); } ``` ### Middleware 🍔 Middleware in Dart Frog allows you to execute code before and after a request is processed. You can modify the inbound request and outbound responses, provide dependencies, and more! This can be useful for validating authorization, adding common headers, logging, etc. In Dart Frog, a piece of middleware consists of a **middleware** function exported from a **\_middleware.dart** file within a subdirectory of the **routes** folder. There can only ever be one piece of middleware per route directory with **routes/\_middleware.dart** being middleware that is executed for all inbound requests. ```dart import 'package:dart_frog/dart_frog.dart'; Handler middleware(Handler handler) { return (context) async { // Execute code before request is handled. // Forward the request to the respective handler. final response = await handler(context); // Execute code after request is handled. // Return a response. return response; }; } ``` We can chain built-in middleware, such as the **requestLogger** middleware via the **use** API. For example, if we create **routes/\_middleware.dart** with the following contents, we will automatically log all requests to our server. ```dart import 'package:dart_frog/dart_frog.dart'; Handler middleware(Handler handler) { return handler.use(requestLogger()); } ``` #### Dependency Injection 💉 Middleware can also be used to provide dependencies to a **RequestContext** via a provider. **provider** is a type of middleware that can create and provide an instance of type **T** to the request context. The create callback is called lazily and the injected **RequestContext** can be used to perform additional lookups to access values provided upstream. In the following example, we'll use a **provider** to inject a **String** into our request context. ```dart import 'package:dart_frog/dart_frog.dart'; Handler middleware(Handler handler) { return handler .use(requestLogger()) .use(provider((context) => 'Welcome to Dart Frog!')); } ``` We can later access the provided via from within a route handler using **context.read()**: ```dart import 'package:dart_frog/dart_frog.dart'; Response onRequest(RequestContext context) { final greeting = context.read(); return Response(body: greeting); } ``` ### Testing 🧪 In Dart Frog, we can unit test our route handlers and middleware effectively because they are plain functions. For example, we can test our route handler above using **package:test**: ```dart import 'dart:io'; import 'package:dart_frog/dart_frog.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; import '../../routes/index.dart' as route; class _MockRequestContext extends Mock implements RequestContext {} void main() { group('GET /', () { test('responds with a 200 and greeting.', () async { const greeting = 'Hello World!'; final context = _MockRequestContext(); when(() => context.read()).thenReturn(greeting); final response = route.onRequest(context); expect(response.statusCode, equals(HttpStatus.ok)); expect(response.body(), completion(equals(greeting))); }); }); } ``` In the above test, we're using [**package:mocktail**](https://pub.dev/packages/mocktail) to create a mock **RequestContext** and stub the return value when calling **context.read()**. Then, all we need to do is call **onRequest** with the mocked context and we can assert that the response is what we expect. In this case, we're checking the **statusCode** and response body to ensure that the response is a 200 with the provided greeting. ## What's Next Check out the [Dart Frog Roadmap](https://dart-frog.dev/roadmap/) to see where we're headed. If you're as excited about Dart Frog as we are, please let us know! We'd love to hear any feedback from the community to help shape our direction and look forward to seeing Dart becoming more prevalent on the backend ☁️ ### Very Good Ventures Raised a Series A! Source: https://verygood.ventures/blog/series-a/ Today we have some very good news: we raised Series A financing from the venture capital firm [Celesta Capital](https://www.celesta.vc/)! ## Why we’re excited ✨ Raising a Series A is a huge opportunity to accelerate our growth as a company in a variety of ways: 🦄 **Grow our global team.** Yes, [we’re hiring](https://verygood.ventures/careers/)!  **📈 Help more companies** [**find success**](https://verygood.ventures/success-stories/) **with Flutter** through our Flutter app development and consulting services. **🔧 Invest in tools, products, and** [**open source**](https://github.com/VeryGoodOpenSource) **projects** that will benefit not only our internal team and clients, but the broader Flutter community. **📝 Continue sharing our best practices and vision** for how multi-platform development can transform technology companies through our [blog](https://verygood.ventures/blog/) and other forms of content. ## VGV 💙 Flutter Flutter has been a critical part of our story since the beginning. We turned to Flutter to build an app for Hamilton the musical and quickly became fans of how much the development process improved because of the UI toolkit.  In the beginning, we had a small team of three employees: David DeRemer, Al Lardizabal, and [Kevin Gray](https://verygood.ventures/blog/remembering-our-friend-and-teammate-kevin-gray/), whom we remember fondly. Then came Martin Rybak from the Flutter NYC meetup group, who also believed in the potential of Flutter and helped us expand our Flutter app development services to even more clients. Now we’ve seen our team grow to 45 employees in multiple locations around the world. As we grew, Flutter grew around us. We’re especially grateful to Tim Sneath and the entire Flutter team for having included us in key moments in Flutter’s growth, such as demoing Flutter on the [web](https://verygood.ventures/success-stories/new-york-times/) with KENKEN for The New York Times and Flutter on [desktop](https://verygood.ventures/success-stories/flutter-slides/) with Flutter Slides.  It’s been a marvel to watch Flutter become a powerful solution for companies looking to build out multi-platform experiences, complete with a friendly community and supportive developer ecosystem. Today, we have the privilege of working with global brands to design, build, and scale successful Flutter apps. Check out our recent work with Google on [I/O Pinball](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d) and [Betterment on transitioning their native apps to Flutter](https://verygood.ventures/success-stories/betterment/). ## Flutter’s Bright Future 🌟 [Flutter has recently been named the most popular cross-platform UI toolkit, according to multiple surveys](https://medium.com/flutter/announcing-flutter-for-windows-6979d0d01fed). Its popularity continues to grow as more companies adopt Flutter, with [56% of users reporting that they use Flutter as part of their main job](https://medium.com/flutter/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development-5d33341954e9) (up from 47% in 2020 Q4). We’ve seen firsthand how it can dramatically change the way entire engineering teams develop for the better.  Flutter enables companies to deploy their applications on virtually any platform from the same codebase. This simplifies the engineering process and adds efficiencies that expand beyond the software engineering team. From strategy, product, design, backend, infrastructure, compliance, security, marketing, and so on — all aspects of how an organization manages technology is affected if a company embraces Flutter. It is this impact on adjacent teams and capabilities that drive our long-term vision.  ## What’s Next for VGV 🦄 This Series A investment will enable us to invest in new initiatives sooner and be better prepared for opportunities ahead. We’ll continue to work with clients and help them find success with Flutter, create and maintain top-notch open source tooling, and grow our global team in a scalable way. We also want to push VGV to do even more. We are hoping to experiment with [games using Flame x Flutter](https://verygood.ventures/blog/flutter-games-with-bloc-and-flame/), make [Very Good Start](https://verygood.ventures/solution/very-good-start/) more widely available, and explore additional capabilities beyond Flutter. **Stay tuned for many more exciting developments!** [Check out the press release.](https://www.prnewswire.com/news-releases/very-good-ventures-raises-3m-series-a-funding-from-celesta-capital-to-further-scale-rapidly-growing-flutter-practice-301548658.html?tc=eml_cleartime) [Read more about the investment from Celesta.](https://www.celesta.vc/insights/very-good-ventures-why-we-invested) [Want to be a part of the excitement? Join our very good team!](https://verygood.ventures/careers/) ### Very good Layered Architecture in Flutter Source: https://verygood.ventures/blog/very-good-flutter-architecture/ This article is an extension of my talk: Layering your Flutter App Architecture from Flutter Festival London. {% video url="https://www.youtube.com/watch?v=h7Hjtj-iw_c" title="Video" %} Structuring code is one of the big – if not the most important – challenges when building large-scale applications. The decisions you make regarding architecture will impact how fast you can deliver new features in the future and how well you can maintain the existing functionality with the latest language and SDK requirements. ## Architecture Let’s start with the basics: what is app architecture? App architecture is the logical way we organize our projects and how the various components interact with each other to fulfill the business requirements. We want to follow standards and make it easy to identify the components that we'll need to develop features in our codebase. The way we establish the relationship and interactions between these components can reduce or add complexity to our projects, which has a significant impact on the team's productivity. ## Layers Now that we know what architecture is, let’s define layers. Layers are the components that compose your architecture. We can define these by assigning a specific responsibility to them. We should keep these layers simple, yet isolated enough to achieve a maintainable codebase. ## Single Responsibility Principle Single Responsibility is one of the [SOLID principles](https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design) from Object-Oriented Design. To sum it up, it tells us to: - Have smaller and simpler classes that have a unique purpose. - Better isolate components for testing. We shouldn't have to mock a lot of components to test specific functionality. - Maintain specific functionality without affecting other parts of the code. By decoupling code, we make each chunk of less dependent on the other. This way, when we make changes, they will have less of an impact on the overall codebase. ## Responsibilities From my experience on mobile applications at scale, there are three main responsibilities in an app: - **Data layer:** This layer interacts directly with an API (REST API or a device API). - **Domain layer:** This layer transforms or manipulates the data that the API provides. - **Presentation layer:** This layer presents the app content and triggers events that modify the application state. Depending on the architecture you choose, these components can have different names. In some architectures, there may be multiple layers to handle specific aspects of these responsibilities, but these are implementation details. The essence will remain independent of the architecture. ## Architectural Choices We’re going to mention what we think you should and shouldn’t do to structure your app the best way possible, so you have the foundation to build a scalable and maintainable application. Note that this is an opinionated approach to architecture, as always, feel free to implement the architecture that makes the most sense for you and your team. ## Define your layers As I said before, you can create different layers to handle the three main responsibilities in your app. Defining layers is a critical step, as you may want to keep them as simple as possible without compromising maintainability. ## Very Good Architecture At VGV, we follow an architecture that has four layers. - **Data layer:** This layer is the one in charge of interacting with APIs. - **Domain layer:** This is the one in charge of transforming the data that comes from the data layer. And finally, we want to manage the state of that data and present it on our user interface, that’s why we split the presentation layer in two: - **Business logic layer:** This layer manages the state (usually using **flutter\_bloc**). - **Presentation layer:** Renders UI components based on state. ![VGV four-layer architecture: data, domain, business logic, and presentation](/assets/images/blog/very-good-flutter-architecture/body-0.png) The domain layer converts raw data into domain-specific models that are consumed by the business logic layer. The business logic layer keeps an immutable state of the domain models that the repository provides. Also, the business logic layer reacts to the inputs coming from the UI and communicates with the repository when changes need to be made based on the state. Depending on the implementation of the API, we can manipulate data without creating an actual API package, but we will talk more about APIs and packages in the next sections. ## Avoid mixed responsibilities This is the most common cause of chaos in projects. Not having a proper separation of concerns provokes errors that are difficult to track. For example, some functionality that would ideally be in a data layer component is located in a presentation layer component, causing an issue that at first sight seems like a presentation issue, but in fact is a data issue. ```dart import 'package:firebase_auth/firebase_auth.dart'; class StartPage extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( stream: FirebaseAuth.instance.userChanges(), builder: (context, snapshot) { if (snapshot.hasData) { return const ProfilePage(); } return const SignInPage(); }, ); } } ``` **TL;DR: don’t leave data or domain interactions within widgets.** ## Be consistent Follow the patterns your team has defined for the project. Don’t take shortcuts with the layers. Each layer should have a chain of relation that prevents interactions between layers that are not directly related. For example, the presentation layer should not call or interact in any way directly with the APIs on the data layer. If we establish that our data layer will be a dependency of our domain layer, we must respect that in all the features we develop. Things like naming can be key in making it easier to know where things are in a project. Remember naming is just a convention. In the end, some developers call the data layer infrastructure and it’s okay, it’s just a name. But try to keep that naming consistent for your architecture. If we’re not consistent, the codebase gets messy and more difficult to navigate through. You may also run the risk of teammates getting confused if you're not following the rules you've established in the codebase. ```txt ├── lib | ├── posts │ │ ├── bloc │ │ │ └── post_bloc.dart | | | └── post_event.dart | | | └── post_state.dart | | └── models | | | └── models.dart | | | └── post.dart │ │ └── view │ │ | ├── posts_page.dart │ │ | └── posts_list.dart | | | └── view.dart | | └── widgets | | | └── bottom_loader.dart | | | └── post_list_item.dart | | | └── widgets.dart │ │ ├── posts.dart │ ├── app.dart │ ├── simple_bloc_observer.dart │ └── main.dart ├── pubspec.lock ├── pubspec.yaml ``` ## Build with reusability in mind This advice is for the data layer specifically. Since it is a raw implementation, it can be used in many domains for different purposes. Components in the data layer are also often excellent candidates for open source because they are not tied to the specific product/application and could benefit the larger community. That’s why we isolate our data and domain layers in packages. This allows us to import them into other apps or publish them on [pub.dev](https://pub.dev/) if desired. Sometimes we build these layers tied to a specific use case, for example, adding a local storage solution. We can create a generic API to store encoded JSON strings instead of expecting a specific model, so we can reuse that API as a local storage API in other domains. You can see how to implement a local storage package with an abstract declaration and concrete implementation in the [flutter_todos](https://bloclibrary.dev/#/fluttertodostutorial) example from the [bloc library](https://bloclibrary.dev/). ![Package structure showing abstract API declaration and concrete implementation](/assets/images/blog/very-good-flutter-architecture/body-1.png) ## Use the right amount of abstraction When we develop a new feature that uses an external package as API for the data layer, we have two choices: - Create a repository that has that API as a direct dependency. - Create an API wrapper for the package. I suggest you go for the first approach if the package or plugin you’re using as a dependency: - Has a clean and simple API. - Does not need too many dependencies to test it. - Does not need any configuration for the classes it uses for the functionality you have. - Solves a specific use case and you are willing to have it as the unique implementation of your API. A good example of a simple API that can be used directly with a repository is **package:stream\_chat\_flutter**. A full architecture example with this dependency can be seen in our [chat_location](https://github.com/VGVentures/chat_location) application, which also has a full [tutorial](https://verygood.ventures/blog/very-good-location-sharing-chat-app/). Otherwise, it’s better to create an API package with a class that wraps that dependency to avoid having all the implementation details in the repository. You can see an example of a custom API package in our [spacex_demo](https://github.com/VGVentures/spacex_demo) application. ## Don’t over-engineer This is related to the previous choice, we don’t want to abstract less than we should, but over abstracting or over-engineering can be time-consuming and increase complexity. An example of this can be with models. We usually create JSON serialization and deserialization functionality for models, but we know we can change to an API with a different format. To be honest, how often are we using anything other than JSON to parse data? In our case, rarely. In this case, abstracting the base properties of the model so we can later add the JSON implementation to it sounds like a waste of time if we never change the current format. ## To use or not to use packages Okay, speaking of over-engineering, here is an opinionated take: If your project is small, maintaining it with packages may be a bit overkill. I personally always use packages because I’m obsessed with component isolation and things like migrations come easier. This way, you can have one teammate migrating the accounting repository to Very Good Analysis 2.4.0 and another migrating the retail API to Dio 4.0.0 at the same time, so you can tackle fixing lints and null safety refactors without stepping on each other’s toes. ![Mono repo with isolated packages enabling parallel migration work](/assets/images/blog/very-good-flutter-architecture/body-2.png) ## Choose the right tools Let’s be honest, we can’t do it all by ourselves. It’s important to have strict criteria to decide on which package or tool you are going to use to solve a problem in your app. I suggest taking into consideration things like activity on the GitHub repository, [pub.dev](https://pub.dev/) metrics, test coverage, who’s maintaining it, and other factors that are important to you and your team. You can check out our favorite tools in our [Top Flutter and Dart Packages](https://verygood.ventures/blog/top-flutter-dart-packages/) blog. ## See what the experts are doing One thing that helps you get your projects to the next level is to check out repositories of remarkable developers from the community. Many Flutter community members have a lot of public repositories on GitHub that you can study and criticize to learn from and also improve. We at VGV have some interesting ones that expose our best practices for how we build apps, and we’re open to answering your questions about them! Be sure to check out [I/O Photo Booth](https://github.com/flutter/photobooth) (a great production app that uses Flutter on the web), [chat_location](https://github.com/VGVentures/chat_location) (a good example of bootstrapping data providers and repositories), and [flutter_todos](https://github.com/felangel/bloc/tree/master/examples/flutter_todos) in the bloc library repo (pay attention to the architecture in this one). ## Refactor Because we’re humans, we make mistakes. Recognizing and learning from those mistakes is as important as layering your code. Your first approach might not be the best one, and you need to analyze every idea or practice you’re following to see if it’s necessary. After all, remember you don’t need to be perfect to be consistent and make your project successful. I strongly believe we should do our best when building apps, but there’s always a level of good that will be enough to help you succeed. If you need to refactor, don’t be hard on yourself! ## Share what you learn! I’m sure you have a lot of new ideas from this blog post, and you will have even more if you keep investigating. We’re excited to see what new pattern or practice you discover, so make sure you do some type of tutorial about it so the community can learn too! Or just tag us on a Twitter thread where you explain it, that works too. *Felix Angelov and Dominik Roszkowski contributed to this article.* ### VGV Expands Poland Team Source: https://verygood.ventures/blog/vgv-expands-poland-team/ 2022 is all about scalable growth at Very Good Ventures. In the past few months, we’ve been fortunate to add multiple members to the [Very Good Ventures team](https://verygood.ventures/contact-us/) — many of them talented Flutter developers, along with a variety of other roles. Today we are excited to welcome five additional teammates to Very Good Ventures! Krzysztof Krasiński-Sroka, Marta Korzeniowska, Artur Płaczek, Jan Stępień, and Dominik Szocik have joined our team. They previously formed Widgeters, a Flutter app development consultancy based in Poland. This group will also allow us to open a second VGV Poland office in Wrocław, following on the heels of the office we opened in Warsaw last year led by Dominik Roszkowski. VGV has additional EU hubs in Rotterdam, the Netherlands, and Madrid, Spain, as well as U.S. hubs in Chicago, New York, and Stamford. We are a remote-first company and having teammates around the world allows us to help companies find success with Flutter in international time zones that allow close collaboration. Since day one, VGV has been fortunate to be able to provide expertise to over 30 companies. Throughout 2022, we'll continue to work with our long-term partners, and new ones, as we build high-quality apps for any platform with Flutter. *David DeRemer and Dominik Roszkowski contributed to this article.* ### Supercharge your Flutter tests with Very Good CLI Source: https://verygood.ventures/blog/flutter-tests-very-good-cli/ Last week we made a number of exciting updates to [Very Good CLI](https://pub.dev/packages/very_good_cli) focused on improvements to testing Dart and Flutter apps 🎉. [Very Good CLI v0.6.0](https://github.com/VeryGoodOpenSource/very_good_cli/releases/tag/v0.6.0) now features a new test command that provides additional functionality on top of the existing command line tooling for [**dart test**](https://dart.dev/tools/dart-test) and [**flutter test**](https://docs.flutter.dev/reference/flutter-cli). Included are performance optimizations, improvements to the developer experience when measuring and enforcing tests coverage metrics, and improvements when working with multimodule monorepos. ## Overview 🚀 Very Good CLI's new **test** command includes the following flags and arguments. You can always see the most up-to-date list by running **very\_good test --help**. ```shell $ very_good test --help Run tests in a Dart or Flutter project. Usage: very_good test [arguments] -h, --help Print this usage information. --coverage Whether to collect coverage information. -r, --recursive Run tests recursively for all nested packages. --[no-]optimization Whether to apply optimizations for test performance. (defaults to on) --exclude-coverage A glob which will be used to exclude files that match from the coverage. -x, --exclude-tags Run only tests that do not have the specified tags. --min-coverage Whether to enforce a minimum coverage percentage. --test-randomize-ordering-seed The seed to randomize the execution order of test cases within test files. Run "very_good help" to see global options. ``` ## How its made 🛠 In order to build a custom test command, we first needed a way to programatically run tests that would fit nicely with our existing tooling for Very Good CLI. As a result, we created the [Very Good Test Runner](https://pub.dev/packages/very_good_test_runner) package which provides APIs to run Dart and Flutter tests programatically thanks to the [JSON Reporter Protocol](https://github.com/dart-lang/test/blob/master/pkgs/test/doc/json_reporter.md). Internally, **very\_good\_test\_runner** uses the [Process class](https://api.dart.dev/stable/2.16.2/dart-io/Process-class.html) to spawn **dart test** and **flutter test** processes respectively. We specify the **\--reporter=json** option to receive a machine-readable representation of the underlying test runner's progress. **very\_good\_test\_runner** exposes a stream of **TestEvent** objects which consumers can subscribe to in order to build custom testing tooling. ```dart import 'package:very_good_test_runner/very_good_test_runner.dart'; void main() { const arguments = ['--coverage']; const workingDirectory = 'path/to/project'; // Run Dart tests and collect coverage in the working directory. dartTest( arguments: arguments, workingDirectory: workingDirectory, ).listen((TestEvent event) { // React to `TestEvent` instances. }); // Run Flutter tests and collect coverage in the working directory. flutterTest( arguments: arguments, workingDirectory: workingDirectory, ).listen((TestEvent event) { // React to `TestEvent` instances. }); } ``` Custom testing tools can enable you to customize how test results are displayed, detect tests that are abnormally slow, re-run only the tests that have failed from previous test runs, and more. Another great example of a custom testing tool is the [spec_cli](https://pub.dev/packages/spec_cli) from [Invertase](https://github.com/invertase). The possibilities for custom test tooling are endless and we're very excited to see what other tools the community builds. ## Testing Improvements ✨ ### Performance Optimizations Very Good CLI takes advantage of a [known performance optimization](https://github.com/flutter/flutter/issues/86722#issuecomment-914688482) to decrease test run times. From our experience, this is particularly significant in large codebases when collecting coverage metrics. As the number of tests increases, the cost of running the tests and collecting coverage metrics also increases so applying this optimization can reduce test runs quite dramatically (see below comparison). ![Test run time comparison without Very Good CLI optimization](/assets/images/blog/flutter-tests-very-good-cli/body-0.png) ![Test run time comparison with Very Good CLI optimization enabled](/assets/images/blog/flutter-tests-very-good-cli/body-1.png) This additional optimization step is performed by default but can be disabled with the **\--no-optimization** flag. You may want to disable optimizations in order to improve the traceability of failing tests or for smaller codebases with fewer tests to reduce the initial overhead of performing the optimization step. ```shell # run tests with performance optimizations very_good test # run tests without performance optimizations very_good test --no-optimization ``` As part of the performance optimization step, Very Good CLI uses a [mason](https://github.com/felangel/mason) brick to aggregate all tests and generate a single **.test\_runner.dart** file. ```dart // GENERATED CODE - DO NOT MODIFY BY HAND // Consider adding this file to your .gitignore. import 'package:test/test.dart'; import 'app/view/app_test.dart' as app_view_app_test_dart; import 'counter/cubit/counter_cubit_test.dart' as counter_cubit_counter_cubit_test_dart; import 'counter/view/counter_page_test.dart' as counter_view_counter_page_test_dart; void main() { group('app_view_app_test_dart', app_view_app_test_dart.main); group('counter_cubit_counter_cubit_test_dart', counter_cubit_counter_cubit_test_dart.main); group('counter_view_counter_page_test_dart', counter_view_counter_page_test_dart.main); } ``` Then, the test execution consists of executing the single **.test\_runner.dart** file: ```dart final generator = await MasonGenerator.fromBundle(testRunnerBundle); ... await generator.generate( target, vars: vars, fileConflictResolution: FileConflictResolution.overwrite, ); ... flutterTest( workingDirectory: workingDirectory, arguments: [ ... if (optimizePerformance) p.join('test', '.test_runner.dart') ], ).listen((event) {...}); ``` ### Enforcing Coverage The new **test** command also provides an option to specify a minimum test coverage threshold. If the reported coverage drops below that threshold, the test run will exit with an error code. ```shell # Run tests, collect coverage, and enforce 100% coverage very_good test --coverage --min-coverage 100 ``` This is particularly useful when running tests as part of your continuous integration because you can fail a build if test coverage drops below the accepted threshold (we recommend 100% 💯). ### Excluding Coverage In some cases, it's handy to be able to exclude certain files from test coverage. For example, you may want to exclude code generated by [**build_runner**](https://pub.dev/packages/build_runner) from test coverage. The **\--exclude-coverage** option allows you to specify a [glob](https://pub.dev/packages/glob) which will be used to exclude files from coverage. ```shell # Run tests, collect coverage, exclude generated dart files from coverage, and enforce 100% coverage very_good test --coverage --exclude-coverage "*.g.dart" --min-coverage 100 ``` ### Run Recursively When working on a monorepo with many packages, it can be quite tedious to run tests in all sub-packages. For example, after a large refactor you may want to verify that no regressions were introduced. Previously, you'd have to manually run the tests in each affected package one at a time. With the new **test** command in Very Good CLI, it's easy to run tests in all nested packages via the **\--recursive** flag (**\-r** for short). ```shell # Run all tests in the current package as well as nested packages. very_good test --recursive ``` ❗️ Currently, the **very\_good test** command must be executed from the root directory of a package so be sure to keep that in mind when using the **\--recursive** flag. ## Summary Very Good CLI now ships with some new testing capabilities that we hope will make running tests in Flutter/Dart even more enjoyable. We're excited to continue to expand the capabilities of Very Good CLI and plan to incorporate the testing improvements into [very_good_workflows](https://github.com/VeryGoodOpenSource/very_good_workflows) shortly. Until next time, happy testing 🧪🦄! ### Flutter testing resources Source: https://verygood.ventures/blog/flutter-testing-resources/ Our team at VGV has a reputation for prioritizing automated testing in all of our projects, whether it’s an open source package or a production app for a client. We thought it might be helpful to gather our recommended testing resources — including blogs, courses, and packages — and put them all in one place here. Bookmark this page! **Table of Contents (click to skip to section):** - [Why should I write automated tests?](#why-should-i-write-automated-tests) - [Where can I read about automated testing?](#where-can-i-read-about-automated-testing) - [Where can I find videos or courses about automated testing in Flutter?](#where-can-i-find-videos-or-courses-about-automated-testing-in-flutter) - [What packages should I use for automated testing?](#what-packages-should-i-use-for-automated-testing) - [Does test coverage matter?](#does-test-coverage-matter) - [Are there examples of fully-tested apps?](#are-there-examples-of-fully-tested-apps) - [What about CI/CD?](#what-about-cicd) ## Why should I write automated tests? There are many benefits to writing automated tests. They can help you catch bugs, validate that your code works as intended, and give you confidence that every line of code you ship has been executed at least once (which is why we recommend 100% line coverage). Simply put: writing automated tests can ensure that your code is high-quality. [This tweet by Gergely Orosz](https://twitter.com/GergelyOrosz/status/1492579513615671303?s=20&t=EcnSOsGzLzjBoBO12MslNw) says it best: ![Tweet by Gergely Orosz on the value of writing automated tests](/assets/images/blog/flutter-testing-resources/body-0.png) Do your team (and future you) a favor and write automated tests! ## Where can I read about automated testing? One of the best places to learn about testing is the Flutter documentation itself. Here are a couple of pages we recommend to start: - [Flutter docs: Testing Flutter apps:](https://docs.flutter.dev/testing) The official documentation for automated testing with Flutter. Learn about unit, widget, and integration tests here. - Flutter docs: How to test a Flutter app codelab: Learn how to test a Flutter app with this interactive codelab. There are also many blogs that explore automated testing and provide examples of how to write tests. Here are articles that we suggest reading, including some from our blog: - [How can I start with Flutter Testing? by Ana Polo:](https://medium.com/flutter-community/how-can-i-start-with-flutter-testing-part-one-a51aaefdb6a2) Read about a Flutter developer's journey to learn more about Flutter testing. - [Flutter testing: A very good guide by VGV:](https://verygood.ventures/blog/guide-to-flutter-testing/) Learn about VGV's approach to the what, how, and *why* of Flutter testing. - [How to use Alchemist for Flutter golden tests by VGV:](https://verygood.ventures/blog/alchemist-golden-tests-tutorial/) Learn how to use the package Alchemist (created by Betterment x VGV) to write golden tests for your Flutter projects. - [Robot Testing by VGV:](https://verygood.ventures/blog/robot-testing-in-flutter/) Read about the Robot Testing method for end-to-end testing. ## Where can I find videos or courses about automated testing in Flutter? The below resources are great for both beginners wanting to learn the fundamentals and experienced devs looking to brush up on their testing skills. - [Testing Fundamentals of Flutter by Jorge Coca for VGV:](https://youtube.com/playlist?list=PLprI2satkVdFwpxo_bjFkCxXz5RluG8FY) In just over 30 minutes, you can learn the basics about widget, unit, and integration tests. - [Widget Testing by Jorge Coca for Google Developer Experts:](https://youtu.be/aReBbQgLjtk) This is a great standalone video that teaches how to use widget tests in your Flutter projects. - [Flutter Testing Guide for Beginners by ResoCoder:](https://www.youtube.com/playlist?list=PLB6lc7nQ1n4jN2u4rMmb-3tdJ_cQBs-YS) This new course will teach you how to make the most of automated testing in Flutter. As of the day this was published, Parts 1 and 2 are available; we anticipate that others will be published in the future. - [Unit Testing our AuthService by Vandad Nahavandipoor:](https://www.youtube.com/watch?v=nbUAZfJIQec) This video is Chapter 26 in a free course that aims to take students from beginners to more experienced Flutter developers. Watch to learn how to write unit tests for a sample app. ## What packages should I use for automated testing? These are some packages we frequently use to write unit, widget, and integration tests in our Flutter projects. Most of the tools listed were developed by our team to help make automated testing easy: - [Very Good Coverage:](https://github.com/verygoodopensource/very_good_coverage) Essential for enforcing a minimum code coverage threshold for your projects (we recommend 100%). - [Very Good Workflows:](https://github.com/VeryGoodOpenSource/very_good_workflows) Run your tests as part of these reusable GitHub workflows. Includes support for Flutter projects, Flutter packages, and Dart packages. - [Very Good CLI:](https://pub.dev/packages/very_good_cli) Generate a fully-tested scalable starter app, package, or plugin with our CLI tool. You can also use the [very_good test command](https://verygood.ventures/blog/flutter-tests-very-good-cli/) to run tests directly from the command line. - [Mocktail:](https://pub.dev/packages/mocktail) Inspired by mockito, this package helps you create mocks in Dart with null safety without the need for manual mocks or code generation. - [Bloc Test:](https://pub.dev/packages/bloc_test) Test your blocs easily with this package. It is especially useful if you’re using bloc and mocktail in your projects. - [Flame Test:](https://pub.dev/packages/flame_test) Test your applications using Flame with these classes. - [Mockingjay:](https://pub.dev/packages/mockingjay) Mock navigation in Flutter for testing purposes. - [Alchemist:](https://pub.dev/packages/alchemist) A package built by Betterment in collaboration with VGV that makes it easy to write golden tests, complete with useful functions, extensions, and documentation. ## Does test coverage matter? **Yes!** Our answer will always be to aim for 100% test coverage. We recognize that this is a high threshold and we hold ourselves to it whenever possible. It's important to note 100% test coverage does not guarantee the codebase is bug-free. We use 100% as our threshold because it ensures every line of code we ship has been exercised by at least one test. However, we also know that teams have their own requirements and sometimes 100% coverage is not possible to achieve. In these cases, we recommend two things: 1. Starting today, make it a requirement to write tests with every pull request. 2. Decide with your team on a coverage threshold and stick to it ([Very Good Coverage](https://github.com/VeryGoodOpenSource/very_good_coverage) can help you with this). The important thing is that you adhere to the coverage threshold you set and always aim to improve. If you set your threshold at 80%, make it a goal to never drop below. Instead, maintain that threshold or even better, gradually raise it over time until you can reach 100%. ## Are there examples of fully-tested apps? For examples of fully-tested Flutter apps, check out these open source examples: - [I/O Photo Booth](https://github.com/flutter/photobooth) - [Slide Puzzle](https://github.com/VGVentures/slide_puzzle) - [Todos Example App](https://github.com/felangel/bloc/tree/master/examples/flutter_todos) ## What about CI/CD? Automated testing is not complete without a robust CI/CD solution. When it comes to CI/CD we highly recommend the following tools: **GitHub Actions:** [GitHub Actions](https://docs.github.com/en/actions) are extremely useful for automating everything from checking your spelling to running your automated tests. Check out the [GitHub Actions Marketplace](https://github.com/marketplace?type=actions) to browse the available actions. *Note:* [*Very Good Workflows*](https://github.com/VeryGoodOpenSource/very_good_workflows) *is a GitHub Action that can automatically run all of your tests on each PR.* **CI/CD Solutions:** - [Codemagic:\*](https://codemagic.io/start/) This is our preferred continuous deployment solution for Flutter apps. Other solutions that work great with Flutter apps include AppWrite, Bitrise, Jenkins, Circle CI, and more. These are great because they are fully managed solutions, which allow you to run tests and other automated steps without having to manage your own servers. In addition, Codemagic makes it extremely easy to distribute your app via Google Play and TestFlight. *\*Note that VGV gets a portion of sales when we refer clients to Codemagic.* - [Firebase Test Lab:](https://firebase.google.com/docs/test-lab) Allows you to run tests on different hardware devices in a Google data center so that you can be sure your apps run as expected on all of the devices you're targeting. *Felix Angelov contributed to this article.* ### How to use Alchemist for Flutter golden tests Source: https://verygood.ventures/blog/alchemist-golden-tests-tutorial/ Golden testing is a method of testing the appearance of widgets using screenshot comparisons. It can be leveraged to help test purely visual components like charts or custom painters that might otherwise be really difficult to test. In this tutorial, you'll learn how to use both Flutter's built-in functionality for golden tests and the open source package [**Alchemist**](https://pub.dev/packages/alchemist/) (developed by [Betterment](https://www.betterment.com/) and [VGV](https://verygood.ventures/)) to improve your testing experience. The example we'll be using for our testing comes from [DartPad](https://dartpad.dev/?id=5c0e154dd50af4a9ac856908061291bc). It is a Flutter project that uses a **CustomPainter** to create a sunflower image with a variable number of seeds. This is a prime example for using golden testing because the **CustomPainter** is painting directly to the canvas. We could alternatively test it using a mock **Canvas** and verifying that the correct painting functions are called in the correct order; however, this would be very fragile to implementation changes and would also fail to serve as documentation for the code being tested. Golden tests will be a lot easier for other engineers to maintain because the tests can be easily read and understood without knowing how the painting logic works. ## Using Flutter's built-in golden tests Flutter already has built-in support for golden testing. The built-in golden test tooling is great because of how simple it is to start writing tests and how similar the syntax is to regular widget tests, but the downside is that they quickly become hard to read and maintain as you create more test variations. Additionally, there are issues with font rendering between platforms, so if you create your golden images on a Mac then the tests will fail on a Linux CI machine. Generally, golden tests work the same way as normal Flutter widget tests. Each test case is contained in a **testWidgets** function which uses **tester.pumpWidget** to load the widget under test. Any desired interactions, such as tapping a button, can be performed once the widget is loaded. Finally, the [**matchesGoldenFile**](https://api.flutter.dev/flutter/flutter_test/matchesGoldenFile.html) matcher within an **expectLater** call can be used to verify the rendered widget matches a previous render. For example, with our sunflower app it would look like this: ```dart void main() { const rootKey = Key('root_sizedBox'); Widget buildSubject(int seedCount) { return SizedBox( key: rootKey, width: 400, height: 400, child: CustomPaint( painter: SunflowerPainter(seedCount), ), ); } group('SunflowerPainter with matchesGoldenFile', () { testWidgets('matches expected output with 100 seeds', (tester) async { await tester.pumpWidget(buildSubject(100)); await expectLater( find.byKey(rootKey), matchesGoldenFile('sunflowerPainter_seedCount_100.png'), ); }); testWidgets('matches expected output with 20 seeds', (tester) async { await tester.pumpWidget(buildSubject(20)); await expectLater( find.byKey(rootKey), matchesGoldenFile('sunflowerPainter_seedCount_20.png'), ); }); testWidgets('matches expected output with 2000 seeds', (tester) async { await tester.pumpWidget(buildSubject(2000)); await expectLater( find.byKey(rootKey), matchesGoldenFile('sunflowerPainter_seedCount_2000.png'), ); }); }); } ``` When you run this test file the first time, you can pass the **\--update-goldens** flag to **flutter test** to generate screenshots for each of the test cases. After updating golden tests, each subsequent test run will compare the rendered widgets to the generated images (golden files), failing the test if they don't match. When the widget changes and you want to regenerate the golden files, then you can use **\--update-goldens** to generate new images and commit the updated golden files to your repository. ## Using Alchemist with golden tests Alchemist is a framework designed to assist with golden testing. It addresses pain points found while using existing golden testing solutions on a large codebase, such as fixing continuous integration test flakes and simplifying injecting a custom theme. It's also designed to have a more declarative API, which should help improve test readability. The golden tests using Alchemist have a different structure completely from typical flutter widget tests. They begin with a call to **goldenTest**. This function has a number of parameters you can use to customize the test. Every test needs a description, a filename, and a widget to test. Typically, the widget to test is a **GoldenTestGroup**. This is a special type of widget which organizes a number of labeled **GoldenTestScenario**s into a grid layout to keep the golden file organized. Here's what the completed **goldenTest** looks like: ```dart group('SunflowerPainter with alchemist', () { goldenTest( 'matches expected output', fileName: 'sunflowerPainter', widget: GoldenTestGroup( children: [ GoldenTestScenario( name: 'with 100 seeds', child: buildSubject(100), ), GoldenTestScenario( name: 'with 20 seeds', child: buildSubject(20), ), GoldenTestScenario( name: 'with 2000 seeds', child: buildSubject(2000), ), ], ), ); }); ``` Like before, running **flutter test --update-goldens** will make Alchemist generate the rendered widget images in the **goldens/** directory adjacent to the test file. Inside will be a **ci/** subdirectory, along with another subdirectory for each platform on which the tests run. Because **CI** golden files are not affected by font rendering inconsistencies, they are kept separate from the platform golden files (which can be omitted from source control, if desired). Golden tests running on CI can be generated on any platform and are guaranteed to pass on CI. The platform-specific goldens will not run on any other platform besides the one on which they were generated to avoid headaches caused by font-rendering inconsistencies. The golden tests shown have been pretty basic, although very useful for verifying a specific type of widget. ## What's next Golden tests can also be used with more advanced use cases, such as testing an app's UI library. Alchemist has some more built-in functionality to support this use case, like custom theming and gesture support. To get started with more advanced functionality, check out the [Alchemist docs](https://github.com/Betterment/alchemist#advanced-usage). *Joanna May contributed to this article.* ### Flutter games with Bloc and Flame Source: https://verygood.ventures/blog/flutter-games-with-bloc-and-flame/ Flutter is an incredible and versatile framework, focused on the creation of beautiful multi-platform applications. It can also be used to develop a special type of application: games! In this article, we are going to give you an introduction into how state can be managed in a game using [**bloc**](https://bloclibrary.dev/). ## A brief introduction to Flame [**Flame**](https://flame-engine.org/) is an open source game engine in Flutter which offers many tools to help you build games, like: - Images, sprites, and animations rendering - Receiving input (gestures, keyboard, mouse) - Camera and viewport - and more! It has recently achieved its first stable version and has a growing community with a few interesting games released with it. ## How a game works and how its state can be managed One of the biggest differences between a conventional application and a game is how the rendering occurs. In conventional apps, the app is re-rendered in response to changes. For example, a touch input from the user can triggers some logic that changes the UI, or even a background event, like a push notification. This is called passive rendering. In contrast, in games, many elements on the screen are updating constantly, even when there is no interaction from user. Since the rendering is constant, we call this active rendering. Good performance plays a critical role in games. Of course, good performance is important in any application, but bad performance is much more noticeable in game and could render the game unplayable. State can be managed a little bit differently when it comes to active rendering and performance in games. It could also make updating state easier. Since the game is always updating itself, we don't need reactivity update the screen. For example, we could just directly update a variable called **position** in our **Player** class and in the next frame of the game, our player would be rendered with the updated position. By storing the state directly within variables in our game classes, we can quickly retrieve it, which helps with game performance. That said, there are also many cases where state management packages can be more helpful than storing game data in class variables. We will explore this approach later in this article. ## The bloc package We're going to use the **flutter\_bloc** package within this article, as the state management library of choice here at Very Good Ventures and Flame provides some helper functions for it. However, a similar approach could easily be applied with any other state management packages. [**Bloc**](https://bloclibrary.dev/#/) is a predicable state management library which helps to implement the BLoC design pattern It helps you manage your state in a way that creates separation between your presentation and business logic. This makes your code easier to test and reuse. If you never used bloc before, it may be worth checking out this [introduction to bloc](https://medium.com/flutter-community/flutter-bloc-for-beginners-839e22adb9f5). ## A real life example To demonstrate how bloc can be used to help manage game state, we have built a game prototype called Very Good Adventures. It looks like this: In this example, Very Good Adventures is classic top-down adventure game, where the player navigates through landscapes, and collects items and equipment, which are used to solve mysteries and defeat enemies. The scope of our prototype will include the following features: - Player movement on the vertical and horizontal axis using the keyboard keys (W, A, S, D) - Collection of nearby items when the space bar is pressed - Display of the collected items in an inventory panel - Display of the equipped items in a player panel - Player appearance changes depending on the equipped gear We will go through how these features were implemented, but not all of the game code will be presented on the article. The complete source code can be found in [this repository](https://github.com/VGVentures/very_good_adventures) and [it can be played live here](https://vgventures.github.io/very_good_adventures). ### Player position and movement The position and movement of an in-game object is a very good example of a set of data which will be handled inside the game object itself. Since the object will change constantly over time, direct memory access plays an important role here. To represent our player, we are going to create a component based in one of Flame's many components, the **SpriteComponent**, which is a component which holds a position and renders a sprite in the game canvas. That position is represented by a **Vector2** class, which is nothing more than a point in space. To handle movement, we need two variables, **speed** which will be a constant of how many logical pixels our player moves per second and we also need a **direction** vector which will tell us what direction the player is moving, where x = 1, player is moving right, x = -1, moving left, x = 0, not moving, and the same directions for the y axis. Our player class will look like this: ```dart // KeyboardHandler is a Flame mixin which allows components to handle key events class Player extends SpriteComponent with KeyboardHandler { Player() : super(size: Vector2(20, 60)); Vector2 direction = Vector2.zero(); static const speed = 100.0; // Load method omitted @override void update(double dt) { super.update(dt); final newPosition = position + direction * speed * dt; position ..x = newPosition.x ..y = newPosition.y; } @override bool onKeyEvent( RawKeyEvent event, Set keysPressed, ) { final isDown = event is RawKeyDownEvent; if (event.logicalKey == LogicalKeyboardKey.keyA) { direction.x = isDown ? -1 : 0; return true; } if (event.logicalKey == LogicalKeyboardKey.keyD) { direction.x = isDown ? 1 : 0; return true; } // Other directions omitted } } ``` And then let's add our player into a **FlameGame**: ```dart class VeryGoodAdventuresGame extends FlameGame with HasKeyboardHandlerComponents { static final Vector2 resolution = Vector2(600, 400); late final Player player; @override Future? onLoad() async { await super.onLoad(); camera.viewport = FixedResolutionViewport( Vector2(resolution.x, resolution.y), ); await add(player = Player()..y = 40); camera.followComponent(player); } } ``` ### Collecting items in the game area Now that the player can roam around the map, we are going to implement the item collection. We will implement our inventory panel using Flutter widgets so that we can easily build our game UI. To manage our inventory and create a point of communication between our Flame game and our game UI, we are going to create an **InventoryBloc**, which will manage a list of collected items. Since a Flame Game is just a widget at the end of the day, we could just build this using **flame** and **flutter\_bloc** packages. To make things a little bit easier, we can use the [**flame_bloc**](https://pub.dev/packages/flame_bloc) package, which integrates nicely with the **bloc** package. So first things first, let's create our **InventoryBloc**, its state, and events: ```dart class InventoryState extends Equatable { const InventoryState({ required this.items, }); const InventoryState.initial() : this(items: const []); final List items; @override List get props => [items]; InventoryState copyWith({ List? items, }) { return InventoryState(items: items ?? this.items); } } abstract class InventoryEvent extends Equatable { const InventoryEvent(); } class GameItemPickedUp extends InventoryEvent { const GameItemPickedUp(this.gameItem); final GameItem gameItem; @override List get props => [gameItem]; } class InventoryBloc extends Bloc { InventoryBloc() : super(const InventoryState.initial()) { on(_onGameItemPickedUp); } void _onGameItemPickedUp( GameItemPickedUp event, Emitter emit, ) { emit( state.copyWith( items: [ ...state.items, event.gameItem, ], ), ); } } ``` Next we need to provide our new bloc to our game, we can do that by simply using **flutter\_bloc**'s **BlocProvider** widget: ```dart BlocProvider( create: (_) => InventoryBloc(), child: GameWidget(game: VeryGoodAdventuresGame()), ), ``` Next, we need to change our game to be a **FlameBlocGame** instead of just a **FlameGame**. **FlameBlocGame** is responsible for managing bloc subscriptions and also give us access read blocs from the context in order to trigger events. So lets do the change and add a few items on our game: ```dart class VeryGoodAdventuresGame extends FlameBlocGame with HasKeyboardHandlerComponents { // Omitted @override Future? onLoad() async { // Adding this to onLoad await add( Pickupable(item: GameItem.sword) ..x = -80 ..y = -40, ); } } ``` Finally, we add some logic on the Player class to collect items when pressing the space bar. ```dart // KeyboardHandler is a Flame mixin which allows components to handle key events class Player extends SpriteComponent with KeyboardHandler, // Adding HasGameRef so this component can easily access its game instance HasGameRef { // Previous code omitted @override bool onKeyEvent( RawKeyEvent event, Set keysPressed, ) { // Add this to onKeyEvent if (event.logicalKey == LogicalKeyboardKey.space) { // Find the items that are close enough final closePickups = gameRef.children.whereType().where((element) { final distance = position.distanceTo(element.position); return distance <= 50; }).toList(); if (closePickups.isNotEmpty) { // Select the first one of the items that are close enough and it to be removed // from the game since we are collecting it final pickup = closePickups.first..shouldRemove = true; // Trigger an event on our InventoryBloc that we have collected the item // Like we mentioned before, `FlameBlocGame` gives us access to reading blocs // from the context, so we use the `gameRef.read` method to add the event. gameRef.read().add( GameItemPickedUp( pickup.item, ), ); } return true; } } } ``` And that is it! Every time our player collects an item on the game, our inventory view will be automatically updated! ### Handling equipped gear To handle the equipped items, we will follow a very similar approach that we did for the **InventoryBloc**. Instead of using a list of all of the collected items, we will use a map, where the key is a value from a enum describing a gear slot (head, left arm, right arm), and the value will be the item, (or null if nothing is equipped). For the sake of article length, we will omit the **PlayerBloc** code below, but you can [view the full code here.](https://github.com/VGVentures/very_good_adventures/tree/main/lib/game/player/bloc) With our **PlayerBloc** ready, we now need to make our **Player** component aware of when an item is equipped or unequipped, so that we can reflect it visually in the UI. That is quite easy to do with **flame\_bloc**: ```dart class Player extends SpriteComponent with KeyboardHandler, HasGameRef, // By adding the BlocComponent mixin to our component, it will automatically // receive updates of the state BlocComponent { // Previous code omitted // With the BlocComponent mixin, we can now override // the onNewState method to receive live updates of the // bloc state @override void onNewState(PlayerState state) { // For the sake of simplicity, we just remove any gear component that // we currently have and add the currently ones on the new state for (final child in children) { child.shouldRemove = true; } for (final entry in state.gear.entries) { final item = entry.value; if (item != null) { add(PlayerGear(slot: entry.key, item: item)); } } } } ``` ## Summary By using **bloc** to handle parts of our game state, we get improved separation of game logic, as well as a simple way to communicate between our game components and our UI widgets. Furthermore, we get helpful tools to make it easy for us to test the code of our game, which will allow us to continue building to our game without unintentionally breaking parts of it. Even though we are not covering tests in this article, since that subject could be an entire article on this own, we suggest checking out two tools that can help us on that matter: - [**bloc_test**](https://pub.dev/packages/bloc_test): A testing library which provides helpers to test our blocs. - [**flame_test**](https://pub.dev/packages/flame_test): Similar to bloc\_test, but which instead focuses on helping to test Flame games and components. The [tests on the example app repository](https://github.com/VGVentures/very_good_adventures/tree/main/test) also can be used as reference. *Felix Angelov contributed to this article.* ### A Very Good Flutter Setup for Windows Source: https://verygood.ventures/blog/very-good-flutter-windows-setup/ ## Intro ✨ Many exciting updates came out of the Flutter Windows update from last week, most notably that there is now [stable support for Windows apps built with Flutter](https://medium.com/flutter/announcing-flutter-for-windows-6979d0d01fed). We're sure that this development will make many Flutter developers want to experiment with creating Windows-based versions of their current Flutter apps or target Windows in new projects. In order to do that, we need to configure a new Flutter environment setup. While Flutter development has been possible on Windows for quite some time, this may be new territory for Flutter developers who regularly use macOS (including our team here at VGV). This blog will guide you through the configuration process. ***Note that this is an opinionated guide to configuring your local environment according to our preferences — feel free to use whatever setup works best for you. This guide was written with the assumption of running on the latest Windows 11 release, but based on the version you're running, some changes may be required.*** ## Set Up ⚙️ Before getting started, be aware this process may be a lengthy one due to the size of some of the installation packages and downloads. For all commands we will be using the "Windows Terminal" which now comes standard with Windows 11 and defaults to "Windows PowerShell" mode. ### 1 - Chocolatey While the Mac has Homebrew, the Windows equivalent is [Chocolatey](https://chocolatey.org/). It's not technically required to get Flutter running on Windows, but it will make things much easier. You can follow the [install instructions](https://chocolatey.org/install) on their site. One thing to note is that you need to start the Terminal in "administrative mode". Do this by right clicking on Windows Terminal in the Start menu and select "Run as Administrator." ![Windows Terminal Run as Administrator option in the Start menu](/assets/images/blog/very-good-flutter-windows-setup/body-0.png) ### 2 - Install the pre-reqs There are a few things that all need to be downloaded and installed before we start running anything. The good news is that this can all be done with just a few Terminal commands via the **choco** command which was just installed. ```shell choco install vscodechoco install gitchoco install fvmchoco install visualstudio2022community ``` ### 3 - Flutter setup via fvm While you can get Flutter up and running without the use of fvm, it's a good idea to start using it right away. This will allow you to easily install, revert, and change what version of Flutter you are using. ```shell fvm install stablefvm global ``` ![FVM install stable and fvm global commands in Windows Terminal](/assets/images/blog/very-good-flutter-windows-setup/body-1.png) When setting the **global** version of Flutter to use, you will be shown the list of all Flutter versions you have installed so far. In this case we want to choose **stable**. By default, fvm is installed in your user directory. Once it is installed you can easily proxy **flutter** and **dart** commands through fvm. ![FVM listing installed Flutter versions with stable selected as global](/assets/images/blog/very-good-flutter-windows-setup/body-2.png) In order to cut down the number of keystrokes we need, we can just can add whatever fvm is pointing to as the **global** version to our environment. To do this we go to Settings -> System -> About, and then click on "Advanced system settings" in the Device specifications panel. In the window that pops up you can then select "Environment Variables". Find the **Path** variable in the top section of User variables. Double clicking on it will allow you to add a new item to the path. Here is where you add the path to your fvm install. Remember that this is typically installed in your user directory so we just need to point to the **default/bin** folder. As we update our version of Flutter via fvm, then the system will automatically update. After this is complete, restart Terminal and you should be able to run **flutter --version** directly. ![Windows Environment Variables dialog with FVM default/bin path added](/assets/images/blog/very-good-flutter-windows-setup/body-3.png) ### 4 - Visual Studio setup There are a couple of extra requirements we'll need to configure in Visual Studio in order to get a Flutter Windows app up in running. While we have the basic install completed back in step two, we need to open up the "Visual Studio Installer" and modify our installation. Select "Desktop development with C++" from the list and click "Modify". Note that this package is large and will most likely take a while to get downloaded and installed. ![Visual Studio Installer with Desktop development with C++ workload selected](/assets/images/blog/very-good-flutter-windows-setup/body-4.png) ### 5 - Enable Windows developer mode If your 're already in the Terminal, you can quickly jump to this by entering **start ms-settings:developers**. Otherwise, you can also get to it via the Settings app under Privacy & Security, and then For Developers. Go ahead and flip the "Developer Mode" switch to on. ![Windows Developer Mode toggle enabled in Settings](/assets/images/blog/very-good-flutter-windows-setup/body-5.png) ### 6 - VS Code setup With everything installed, we can finally setup VS Code for Flutter development. Search for and install the "Flutter" plugin, and the "Dart" plugin will automatically be installed as well. ![VS Code Extensions marketplace with Flutter plugin installed](/assets/images/blog/very-good-flutter-windows-setup/body-6.png) And with that our environment is setup and ready to go for Windows app development using Flutter! We now can either create a new Flutter app or try and add Windows support to an existing Flutter app. ## Create a New Flutter App With Windows support becoming stable, we no longer need to enable any extra flags or configuration. Just running a simple **flutter create app\_name\_here** command will get you the basic Counter app with iOS, Android, web, and Windows support. Simple run the command, open the new project in VS Code, and run the app with "Windows" as the selected device. ## Add to Existing Flutter App This is where things can get tricky. In order to add a Windows target to your current Flutter app, you just need to run a simple command, **flutter create --platform=windows**. This will do an update in place and create the new targets needed for the builds to happen. In order to demonstrate this we can easily take a Flutter project from the [Flutter Gallery](https://github.com/flutter/gallery), execute the create command to add the Windows support, and then run the app side by side with another platform like web via Edge. ![Flutter Gallery app running side by side on Windows desktop and Edge browser](/assets/images/blog/very-good-flutter-windows-setup/body-7.png) If you take the extra time to setup Android Studio, we can even get three platforms all running at the same time. Simply updating a few packages allows us to get the [**slide_puzzle**](https://github.com/VGVentures/slide_puzzle) running in Android, Windows, and Web via Edge. ![Slide puzzle app running simultaneously on Android, Windows, and Edge](/assets/images/blog/very-good-flutter-windows-setup/body-8.png) While the Flutter framework has had support for Windows in beta form for a while, the Flutter community may need to catch up. If you have a very robust Flutter based app today it's possible that not all of the third party packages that you use will be updated to fully support Windows yet. This is a similar thing as we saw last year when Flutter on the web moved to stable, the community needed time to catch up. We hope this guide helps you expand your apps to Windows, or create new experiences that target the desktop, built using Flutter's familiar tooling. We're excited to see what comes next from Flutter as the team continues to make notable progress on supporting multiple platforms! ### A very good Flutter setup Source: https://verygood.ventures/blog/very-good-flutter-setup/ If you're looking for a very good guide to help you install and configure your local environment to work on [Flutter](https://flutter.dev/) projects, you've come to the right place! This article will walk you through this process. You will find a setup section with step-by-step instructions for installation and configuration, which includes common errors, code snippets, screenshots, and (lots of) GIFs. ***Note that this is an opinionated guide to configuring your local environment according to our preferences — feel free to use whatever setup works best for you. As we primarily use macOS for Flutter development at VGV, this article is written for macOS users and assumes you have Google Chrome installed on your machine.*** ## Set Up ⚙️ Before getting started, be aware this process may be a lengthy one due to the size of some of the installation packages and downloads. ### 1 - Xcode You will need the [latest stable version of XCode](https://developer.apple.com/xcode/) on your Mac to develop Flutter apps for iOS. You can download it directly from the [App Store](https://apps.apple.com/us/app/xcode/id497799835?mt=12). This is by far the lengthiest step, and it may take a while to complete depending on your Internet connection and machine. So, in the meantime, you can check these fun resources on Flutter: - [The Boring Development Show](https://www.youtube.com/playlist?list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl) - [Flutter Widget of the Week](https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG) - [Flutter Package of the Week](https://www.youtube.com/playlist?list=PLjxrf2q8roU1quF6ny8oFHJ2gBdrYN_AK) - [Flutter Developer Stories](https://www.youtube.com/playlist?list=PLjxrf2q8roU33POuWi4bK0zvDpAHK6759) - [Decoding Flutter](https://www.youtube.com/playlist?list=PLjxrf2q8roU1fRV40Ec8200rX6OuQkmnl) - [Dart playlist](https://www.youtube.com/playlist?list=PLjxrf2q8roU0Net_g1NT5_vOO3s_FR02J) Once the download has been completed, you will find the Xcode icon showing the installation progress on your Launchpad. It's highly advisable not to continue with any of the following steps until this process has been fully completed to avoid potential issues. {% embed url="https://tenor.com/HjtT.gif" title="Movie One Eternity Later GIF - Movie One Eternity Later - Discover & Share GIFs" %} Are you still there? Good! You now need to open Xcode for the first time to **Agree** on the *Xcode and Apple SDKs Agreement*. Have your macOS user's password ready, as you'll be prompted to enter it to install all the required components. Et voilà! 🎉     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/1_Xcode_install.mp4" %} To check that everything has been completed successfully, press **Command ⌘** + **Space Bar**, type **Terminal**, and press **Enter ↩**. This will open a terminal window where you'll need to type: ```shell /usr/bin/xcodebuild -version ``` Hit **Enter ↩** again. You should see the Xcode version you've just installed as well as its build version id.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/2_Xcode_check_version.mp4" %} ### 2 - iTerm + Oh My Zsh Speaking of a **Terminal**: How about we get a nicer one? Simply go to the [iTerm website](https://iterm2.com/) and download it. Getting it installed on your Mac is as easy as unzipping the .zip file in your Downloads folder and then double-clicking on the unzipped iTerm.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/3_iTerm_install.mp4" %} To ensure you've installed it correctly, type the following into the opened iTerm window. ```shell zsh --version ``` Do you want to make it even neater? That's what [Oh My Zsh](https://github.com/ohmyzsh/ohmyzsh) is for. This open-source, community-driven framework "takes advantage of the hundreds of powerful plugins and beautiful themes" while managing your [Zsh](https://www.zsh.org/) configuration. To get it installed, just copy and paste this command into your iTerm window and hit **Enter ↩**. ```shell sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/4 _ohmyzsh_install.mp4" %} ### 3 - Homebrew This installation is not required to get Flutter working on your machine, but it is required if you want to streamline downloading and maintaining packages. As the people behind this handy project said, it is "[The Missing Package Manager for macOS (or Linux)](https://brew.sh/)." So, are you ready for another lengthy install? Let's download and install it all at once. Copy the following command into an iTerm window and press **Enter ↩**. ```shell /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/5_Homebrew_download_install.mp4" %} If it seems like it's stuck while *Downloading Command Line Tools*, you’re not alone. This part of the process may take a while. If it seems like progress is going slowly, you can check that the download is being completed successfully by clicking on the Apple icon found at the top left corner **→ About This Mac → Storage →** See how the available space on your Macintosh HD decreases. Once this download is complete, the process should continue and finish successfully.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/6_Homebrew_install_completed.mp4" %} As usual, you can check that everything went okay by checking the brew version you just installed. ```shell brew --version ``` ### 4 - Git The previous Xcode installation also included git, so there's no need to install it separately. However, if you'd like to have Homebrew manage git, you could simply execute **brew install git** in an iTerm window.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/7_Git_install.mp4" %} ### 5 - LCOV "[LCOV](http://ltp.sourceforge.net/coverage/lcov.php) is a graphical front-end for GCC's coverage testing tool [gcov](http://gcc.gnu.org/onlinedocs/gcc/Gcov.html)." Once again, this tool is not needed to get Flutter working on your machine, but it comes in handy when testing your code. What it lacks in appealing graphics it makes up for in simplicity and usefulness! Besides, it's the shortest installation, and it sure is worth the five seconds it takes to get it on your machine - Execute in an iTerm window, and you'll be done. ```shell brew install lcov ``` Feels good after installing Xcode, doesn't it?     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/8_lcov_install.mp4" %} ### 6 - Android Studio Back to things required for a proper and successful Flutter installation, it's time to address the green [Android](https://www.android.com/) in the room. You need to install Android Studio to get the Android SDK, but as you'll see in the following steps, we'll be using Visual Studio Code for Flutter Development as our preferred IDE. Again, this is an opinionated guide, so feel free to use whichever IDE works best for you. To download Android Studio, navigate to the [official website](https://developer.android.com/studio), click download, accept the terms and conditions, and select the kind of Mac you're using — I used **Mac with Intel chip** in this tutorial, but if you have a newer Mac from 2020 onward with an M1 (Apple Silicon) chip, choose "Mac with Apple chip" instead. Once the download is completed, open the .dmg file and drag Android Studio into the Applications folder. Finally, open Android Studio and complete the initial configuration steps as shown below.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/9_Android_start.mp4" %} ### 7 - Flutter If you've made it this far, you’re ready to finally install [Flutter](https://flutter.dev/)! This article won't go too in-depth into the whole installation process as the [official get-started installation guide](https://flutter.dev/docs/get-started/install/macos) does an outstanding job at walking you through each step. However, you can complement that guide with the steps presented below to avoid potential issues — or vice versa 😉. First, download the **flutter\_macos\_-stable.zip** archive from the official guide. This will place a .zip file in your Downloads folder. Moving on, open an iTerm window and check that your **p**resent **w**orking **d**irectory is /Users/. Otherwise, execute the following commands to navigate to that directory, create a new directory, and navigate into it. ```shell cd ~ mkdir development cd development ``` Notice we named it development as it is a common practice among developers. Nonetheless, feel free to name it however you'd like. Lastly, unzip the previously downloaded Flutter installation bundle containing the latest stable release of the Flutter SDK by typing unzip and then **dragging the .zip file into the iTerm window** — this will avoid potential path errors. Press **Enter ↩** and wait for the process to complete. Then, add the Flutter tool to your path by executing the following. Notice that this is a temporary path used to check if everything is fine for now. ```shell export PATH="$PATH:`pwd`/flutter/bin" ``` You should now be able to check the version you just installed. ```shell flutter --version ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/10_Flutter_install.mp4" %} If you now run: ```shell flutter doctor ``` you'll notice there are a few checks that are failing. So, let's take care of that right now.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/11_Flutter_doctor_issues.mp4" %} Let's permanently add Flutter to our path by navigating back to your user directory and opening the .zshrc file with the following commands: ```shell cd ~ nano .zshrc ``` This will open a file where you'll need to copy and paste the following configurations: ```shell # Flutter export FLUTTER_HOME=~/development/flutter PATH=$PATH:$FLUTTER_HOME/bin:$PATH # Dart (as part of Flutter) export DART_HOME=~/development/flutter/bin/cache/dart-sdk PATH=$PATH:$DART_HOME/bin:$PATH PATH=$PATH:~/.pub-cache/bin:$PATH ``` Press **Control ^** + **X** to close the file, then press **Y** to save it, and **Enter ↩** to go back to the command line.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/12_Flutter_paths.mp4" %} That being done, we can check that an iOS simulator can be opened executing: ```shell sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer ``` Enter your password when prompted and then hit **Enter ↩**. You should now be able to open an iOS simulator by executing: ```shell open -a Simulator ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/13_Flutter_Xcode_simulator.mp4" %} Next up, fixing the infamous [cocoapods error](https://stackoverflow.com/a/61658158). Running: ```shell sudo gem install cocoapods ``` may produce a very verbose error indicating that the process failed. Nonetheless, the solution is quite simple, and all you need to do is execute the following two commands. ```shell brew cleanup -d -v brew install cocoapods ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/14_Flutter_cocoapods.mp4" %} Let's now configure Android Studio so we can run and test Flutter on an Android Emulator. Open **Android Studio → More Actions → AVD Manager**. This will open the Android Virtual Device Manager, where you need to click on **\+ Create Virtual Device...** Among all the options listed under **Phone**, choose the newest device found at the very top - **Pixel XL**, when writing this article. Click **Next**, and **download** the latest system image - **R**, when writing this article. Accept the **Terms and Conditions** and wait for the installation to complete. Click **Next**, and in this screen, change the **Graphics** to **Hardware - GLES 2.0**. Click on **Finish...** and close Android Studio before your RAM *starts complaining*.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/15_Flutter_sdk_Command-line_Tools.mp4" %} You can now **Agree** to the necessary **Android Licenses**. So head on over to iTerm, run: ```shell flutter doctor --android-licenses ``` and accept them as they come.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/16_Flutter_Accept_licenses.mp4" %} An additional device you may want to run your Flutter projects on is your own Mac. To enable desktop support, execute: ```shell flutter config --enable-macos-desktop ``` Last but not least, you need to add a few additional commands to your .zshrc file. Execute: ```shell nano ~/.zshrc ``` to open the file, and copy the following configurations: ```shell # Android Configuration export ANDROID_HOME=~/Library/Android/sdk PATH=$PATH:$ANDROID_HOME/build-tools PATH=$PATH:$ANDROID_HOME/platform-tools PATH=$PATH:$ANDROID_HOME/tools PATH=$PATH:$ANDROID_HOME/tools/bin/ # Ruby - Need to make sure that use the Homebrew Ruby, not the system one export PATH="/usr/local/opt/ruby/bin:$PATH" # Python export PATH="/usr/local/opt/python/libexec/bin:$PATH" ``` Then press **control ^** + **x** to close the file, then press **y** to save it, and **Enter ↩** to go back to the command line.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/17_Flutter_zshrc_configs.mp4" %} And that's it! All the problems should now be gone, which you can check by seeing all the green check marks after running: ```shell flutter doctor ``` ### 8 - Node.js This step is optional. You might consider installing the latest [Node.js](https://nodejs.org/en/) LTS, which includes [npm](https://www.npmjs.com/). Thanks to Homebrew, all you need to do is execute: ```shell brew install node ``` and check that it's been successfully installed by executing: ```shell node --version ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/18_Node_download_install.mp4" %} ### 9 - Visual Studio Code There are plenty of IDEs to work on Flutter development, but we tend to prefer **VS Code**. This popular, open-source IDE is easy to use, offers powerful developer tooling, and supports *countless* extensions to enhance your coding experience. The good news is that [Flutter editor plugins are supported by Android Studio, IntelliJ, VS Code, or Emacs](https://docs.flutter.dev/get-started/editor), so feel free to use the one that best suits your needs. Having said that, let's [download](https://code.visualstudio.com/) it and install it by unzipping the downloaded .zip file. Then, simply move the unzipped VS Code application into your Applications directory. Finally, as you did in previous steps, add the following configurations to your .zshrc file located in your Home (~) directory: ```shell # VS Code export PATH="$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin" ``` Perhaps the most compelling and impressive feature about VS Code is the range of **extensions** that you can add to tune your programming experience to your liking. Here is a list of extensions you might consider adding: - [Dart](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code) - [Flutter](https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter) - [vscode-icons](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons) - [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - [bloc](https://marketplace.visualstudio.com/items?itemName=FelixAngelov.bloc) - [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) - [Path Intellisense](https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense) - [Dart Data Class Generator](https://marketplace.visualstudio.com/items?itemName=dotup.dart-data-class-generator) - [Awesome Flutter Snippets](https://marketplace.visualstudio.com/items?itemName=Nash.awesome-flutter-snippets) - [Dart Barrel Export File Generator](https://marketplace.visualstudio.com/items?itemName=orestesgaolin.dart-export-index) If you've never used VS Code and would like to familiarize yourself with it, check out [Flutter's official guide](https://flutter.dev/docs/development/tools/vs-code#creating-projects), which covers how to create, edit, run and debug projects, among other useful information. ### 10 - Flutter Version Management (fvm) Flutter is an amazing toolkit that enables developers to build exceptional applications. However, managing its different SDK versions, switching between channels, and using the correct configurations in separate projects can become cumbersome. One way to handle these potential complications is to use [fvm](https://fvm.app/). Let's download and install it. Start by running sequentially: ```shell brew tap leoafarias/fvm brew install fvm fvm --version ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/19_fvm_download_install.mp4" %} Let's now set **Flutter's stable SDK** globally by running: ```shell fvm global stable ``` which will prompt you to install it.     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/20_fvm_use_global_stable.mp4" %} Then, let's also install the **beta SDK** by executing: ```shell fvm install beta ``` Once it completes, run the following command to check you have two different SDKs available through fvm. ```shell fvm list ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/21_fvm_install_beta.mp4" %} Lastly, modify VS Code's settings.json file to have access to fvm by adding: ```json { "dart.flutterSdkPath": ".fvm/flutter_sdk", // Remove .fvm files from search "search.exclude": { "**/.fvm": true }, // Remove from file watching "files.watcherExclude": { "**/.fvm": true } } ```     {% video url="https://github.com/VGVentures/very_good_blog_assets/raw/main/very_good_setup/22_fvm_VSCode_configs.mp4" %} ### Congratulations! You've successfully completed this guide, and you now have a robust, convenient, and very good Flutter setup on your Mac 👏 *Óscar Martín, Jay Meijer, and Felix Angelov contributed to this article.* ### Configuring workflows for your Flutter projects Source: https://verygood.ventures/blog/configuring-workflows-for-your-flutter-projects/ At Very Good Ventures, automation plays a key role in our development process. We heavily rely on [GitHub Actions](https://github.com/features/actions) to perform automated checks that ensure a high level of quality and consistency over the course of a project's lifetime. We were very excited to hear about [GitHub reusable workflows](https://github.blog/2021-11-29-github-actions-reusable-workflows-is-generally-available/) and are pleased to announce [Very Good Workflows](https://github.com/VeryGoodOpenSource/very_good_workflows): a collection of reusable GitHub workflows used at Very Good Ventures 🎉 ## ✨ Intro to GitHub workflows If you're new to GitHub workflows, you can think of them as a way to configure steps that are run automatically for you at various points during the development process. A common use case is running a workflow when a pull request is created or updated to perform various code quality checks before allowing the changes to be merged. For more information about GitHub workflows, refer to the [official documentation](https://docs.github.com/en/actions/using-workflows). ## 🔬 Very Good Workflows: What's inside? Currently, Very Good Workflows includes two reusable workflows: - **dart\_package** - **flutter\_package** These workflows contain steps to ensure your Flutter and Dart packages stay in tip-top shape as they evolve. ![Very Good Workflows showing dart_package and flutter_package CI checks](/assets/images/blog/configuring-workflows-for-your-flutter-projects/body-0.jpeg) ## 📦 Very Good Workflows usage To add Very Good Workflows to a project, simply create a new GitHub workflow (we recommend naming the workflow after your package) and add the following contents: ### 🐦 Flutter package ```yaml # .github/workflows/my_flutter_package.yaml name: my_flutter_package on: pull_request jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 ``` ### 🎯 Dart package ```yaml # .github/workflows/my_dart_package.yaml name: my_dart_package on: pull_request jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 ``` That's it 🎉 With the above configuration, whenever a pull request is created or updated, the workflow will run the build job which uses the corresponding reusable workflow. ## 🛠️ Very Good Workflow customizations In some cases, you might need to customize aspects of Very Good Workflows. For example, you might want to specify a different Flutter or Dart version, a different working directory, or files to exclude from the code coverage check. Next, let's take a look at some examples of how we can apply these customizations. ### Dart workflow customizations In the following Dart workflow, we're able to: - Specify which files to exclude from code coverage via the **coverage\_excludes** input - Specify which Dart SDK version to use via the **dart\_sdk** input - Specify the directory in which the package is located via the **working\_directory** input ```yaml name: my_dart_package on: pull_request jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: coverage_excludes: "*.g.dart *.freezed.dart" # defaults to "" (none) dart_sdk: 2.12.0 # defaults to stable working_directory: packages/my_dart_package # defaults to "." (the top-level directory) ``` ### Flutter workflow customizations Similar to the Dart workflow, in a Flutter workflow, we're able to: - Specify which files to exclude from code coverage via the **coverage\_excludes** input - Specify which Flutter channel to use via the **flutter\_channel** input - Specify which Flutter version to use via the **flutter\_version** input - Specify the directory in which the package is located via the **working\_directory** input ```yaml name: my_flutter_package on: pull_request jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 with: coverage_excludes: "*.g.dart *.freezed.dart" # defaults to "" (none) flutter_channel: stable # defaults to (stable) flutter_version: 2.8.1 # defaults to latest version working_directory: packages/my_flutter_package # defaults to "." (the top-level directory) ``` ## 💡 Workflow tips Now that we've taken a look at how to integrate Very Good Workflows into our current projects, we'll look at some tips that can come in handy. ### Including paths Often times, we only want to run our checks when relevant code has been modified. For example, there's not much value is running the full workflows above when our project's README has been modified. We can specify which paths will trigger a specific workflow by adding [path includes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-including-paths). ```yaml name: my_flutter_package on: pull_request: paths: - "**.dart" jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 ``` The above workflow will now only run on a pull request if there are changes to Dart files. This can be super helpful in speeding up pull request checks and saving build minutes. ### Adding custom jobs: Visualizing dependencies In some cases, especially when it comes to a fully-fledged Flutter application, it can be super useful to visualize the application's dependency graph as part of the CI workflow. We can add another job to our workflow that generates and uploads the current project's dependency graph using the [pubviz package](https://pub.dev/packages/pubviz). ```yaml name: my_flutter_package on: pull_request jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 visualize: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v2 - name: Generate Dependency Graph env: FLUTTER_DART_HOME: ${{ env.FLUTTER_HOME }}/bin/cache/dart-sdk/bin FLUTTER_PUB_CACHE: ${{env.FLUTTER_HOME}}/.pub-cache/bin run: | flutter pub global activate pubviz export PATH=$PATH:$FLUTTER_DART_HOME export PATH=$PATH:$FLUTTER_PUB_CACHE pubviz print -d > pubviz.html - name: Upload Dependency Graph uses: actions/upload-artifact@v1 with: name: dependency-graph path: pubviz.html ``` With the above workflow, whenever the build job completes successfully, we'll also run a custom visualize job which uploads the dependency graph visualization to GitHub. ![GitHub Actions artifact upload showing the dependency graph visualization](/assets/images/blog/configuring-workflows-for-your-flutter-projects/body-1.png) ## What's next So far, we've learned a bit about reusable GitHub workflows, taken a look at Very Good Workflows and how to use them, and explored some tips for how to take workflows to the next level. We're very excited to continue to expand Very Good Workflows to include more functionality such as [pub.dev](https://pub.dev/) score verification, [build_runner](https://pub.dev/packages/build_runner) verification, and more! Happy Automating! 🤖 ### Flutter Revitalized Our Codebase and Transformed Our Engineering Team Source: https://verygood.ventures/blog/betterment-flutter-revitalized-our-codebase/ In late 2019, [Betterment](https://www.betterment.com/) committed to becoming a mobile-first company. Our customers were happy with our Android and iOS apps, but as an engineering team, we knew that our native codebases were not the best they could be. We had years of legacy code to maintain and were seeing a slowdown in our ability to get new features out to our customers. We were at a critical juncture that many engineering teams face. We could have done what a lot of companies tend to do and simply hire more developers to help overhaul the codebase. Instead, we made the bold decision to completely replatform our application using [Flutter](https://flutter.dev/), Google’s toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Switching to Flutter helped us achieve the monumental shift to become a mobile-first company and has vastly improved the productivity and happiness of our engineering team. ## Committing to cross-platform It no longer made sense to have two separate teams work on the same app for different devices. We needed a cross-platform solution so that our entire engineering team could work from one codebase and deploy to both iOS and Android. We began by researching the usual cross-platform solutions: [React Native](https://reactnative.dev/), [Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html), and [Flutter](https://flutter.dev/).  We were initially drawn to Flutter because we had heard that it could speed up the development process. Developers reported being able to build features faster and Flutter would allow us to control everything from UI to the business logic with one codebase. We even talked to one company that confirmed they saw a huge boost in productivity after choosing Flutter over React Native. Flutter was looking more and more appealing. We then conducted an objective assessment to measure Flutter against native iOS and Android respectively before making the commitment to switch over. We designed a list of specific criteria to compare, including customer experience, CI/CD integration, app architecture, UI design, available community resources, testing and test automation, accessibility, developer happiness, and migration/integration — all of the things we care about as an engineering team. We asked ourselves questions such as: *Does Flutter allow us to avoid writing platform-specific code in the majority of cases?*  It turns out that Flutter scored “as good as” or “better” than native iOS and Android for the majority of criteria we outlined. With this, we were confident in our decision to move ahead with Flutter.  ## Piloting Flutter We knew right away that we did not want to discard years of work and risk disrupting users by rebuilding our app from scratch with Flutter. We also did not want to waste any time. We needed to get this transition to Flutter right, so we sought out the help of an outside Flutter development consultancy [Very Good Ventures](https://verygood.ventures/). They had built some of the very first Flutter apps and could give us the expertise we needed to get up to speed on Flutter quickly. We came up with a plan to designate a small internal team to pilot Flutter while the rest of the team continued to work on the native apps. If we could get Flutter code to integrate with our existing native codebase, we could gradually migrate features over until our entire codebase was rewritten in Flutter.  Together our pilot team worked with a couple of Very Good Ventures engineers to build a checking flow allowing users to create a checking account directly in our app. With the [add-to-app](https://docs.flutter.dev/development/add-to-app) option, we integrated the Flutter feature into our existing native codebases. It worked! Our app had Flutter code coexisting with native code and users still got the same high-performing functionality. ## Getting our entire team on board Then came time for the arguably larger transition: convincing the entire Betterment mobile team, composed of developers who had been working with native iOS and Android for years, that a switch to Flutter was worth it. Adopting Flutter would not only be a huge shift for our codebase, but also for our culture, since the decision could have a huge impact on our teammates’ careers. *Would this mean developer titles would change? How would this affect each developer’s future at the company?* We wanted to ensure that all employees had an avenue to voice their concerns, so we kept them updated at every stage of our investigation into Flutter. We also developed an FAQ sheet tailored to specific questions they asked about Flutter as well as a newsletter regularly reporting on the internal pilot team’s progress. The most important thing was emphasizing that Betterment was committed to supporting each member on our team during our switch to Flutter. We would not ask our team to go through a coding bootcamp or individual training on their own. Everyone on the mobile team would receive the same training during work hours. ## Flutter training Flutter training was the final major inflection point in our progress as a team. Very Good Ventures facilitated a formal Flutter training program over the course of a week with a series of presentations and breakout sessions. The training helped each engineer on our team establish the same baseline understanding of the basics and best practices we would need to know to build a scalable app with Flutter.  After the first day of training, some developers were on the fence, but willing to give it a shot. By the second week of training, everyone was excited to be building with the new framework. Each member on our team was contributing meaningful updates to our codebase in *less than two weeks*. On top of that, many of our teammates said that the training was simply *fun*. An interesting side effect of the training was the positive impact on our culture. It’s not too often an entire team breaks from their day-to-day and learns something new as a team. The training helped establish bonds and built enthusiasm for our transition to Flutter. ## Future plans We piloted our first Flutter feature in early 2020 and we haven’t looked back. Over the course of six months, despite also grappling with the global pandemic, we were able to start adding Flutter features to our app and train our entire team to be Flutter experts. Today, just over a year since we fully adopted Flutter, 85% of our app runs in Flutter, with the remaining 15% attributed to a handful of large features that our product team wants to reimagine as part of migrating them. We expect to migrate the last few features and convert from add-to-app to the standard Flutter app template in the first half of 2022. It can be daunting to replatform an entire app, but our experience shows that it can be done efficiently without much risk. Switching to Flutter was an investment for the future of our mobile app and our engineering team. Now that we are in the process of replatforming with Flutter, we can develop faster, more efficiently, with higher quality features, and ensure the future success of our mobile app. For Betterment, Flutter is an investment in the future, and we’re already seeing returns on that investment. *Sam Moore is a Principal Engineer at Betterment. Betterment is an online investment company with over $32 billion in assets under management.* ### A Very Good Brand Refresh Source: https://verygood.ventures/blog/very-good-brand-refresh/ We’ve come a long way since our first day as Very Good Ventures. What began as an ongoing stewardship of [Hamilton](https://verygood.ventures/case-studies/hamilton/), the first commercial app built with Flutter outside of Google, has now grown into a consultancy that has advised more than 25 companies about how to build scalable Flutter applications. Our team is now approaching 40 teammates with [offices](https://verygood.ventures/contact-us/) in Chicago, Stamford, Poland, Madrid, and Rotterdam. We’ve written over 60 [blogs](https://verygood.ventures/blog/) and created open [source packages](https://github.com/VeryGoodOpenSource) that have reached thousands of stars on GitHub. Over time, our team and methods have evolved and the scale and sophistication of our services has grown tremendously. As we grow and mature, we want our brand to evolve too. So we embarked on a brand refresh — not to overwrite what we’ve done so far or establish a new identity, but to better reflect who we are today and support us into the future. This blog details the updated Very Good Ventures brand marks and the thinking behind the changes. ### Updated unicorn mascot You may know us best for our mascot: a unicorn wearing sunglasses. It was obvious to us the icon resonated with our community, so we did not want to lose the familiar unicorn mark. However, we looked for ways to update and modernize it without losing the spirit of the original. The original logo was drawn by [Daviher Laredo](https://www.instagram.com/davihero/?hl=en) and we fell in love with it at first sight. However, the breaks in lines of the circle and the mixing of strokes and fills made the logo difficult to use in some settings. We couldn’t add a fill to the background or change the color of the lines without changing the color of the glasses. ![Original VGV unicorn logo with broken circle lines and mixed strokes](/assets/images/blog/very-good-brand-refresh/body-0.png) The new unicorn mark evokes the same positive and playful feeling , but is simpler and better constructed as an asset. We’ve significantly reduced the visual complexity which helps the logo to maintain fidelity at smaller resolutions, which is important on digital platforms. Additionally, its tighter composition allows us to modify it in ways that aren’t possible with the old logo. For instance, we can add background colors or textures to the circle without breaking the form. The new unicorn also has more of an upward tilt and forward lean, to further emphasize the positivity and ambition of our team, as it’s leaning forward more than falling back. Another subtle change is that we’ve removed the company name from being wrapped around the circular path. From now on, we’ll only lock up the word mark separately. This gave our designers a lot more freedom as they didn’t need to maintain a visual spacing that supported the words. ![Updated VGV unicorn mark with simpler construction and upward tilt](/assets/images/blog/very-good-brand-refresh/body-1.png) Perhaps the thing we’re most excited about is the fact that our logo can now exist in a variety of treatments to best suit the context. For instance, we found that a “stroke only” version tended to lose its punch on dark backgrounds. So now we also have “fill only” and “full color” variants that allow us to adjust the stroke and fills to create cool effects and make our brand really pop. In the end, the new version also feels like it could be a fun app icon, which feels right at home with our mobile development work. ![Unicorn logo variants: stroke only, fill only, and full color treatments](/assets/images/blog/very-good-brand-refresh/body-2.png) ### Responsive wordmark The original Very Good Ventures wordmark was a fun hand lettering element, but it is something that we’ve had a hard time using in practice. It didn’t really pair in an obvious way with the unicorn mark, and we needed a better solution. Since “Very Good Ventures” is three words and a bit lengthy, it also presents some challenges fitting it into certain spaces. As Flutter developers, we are familiar with building responsive apps that look very good on any screen. So, we had the idea for our new wordmark to be flexible to scale through three variants that are responsive and can work for various constraints. As the wordmark is collapsed down to the three-line variant, the shorthand “VGV” is revealed. ![Responsive wordmark scaling from full name to three-line VGV abbreviation](/assets/images/blog/very-good-brand-refresh/body-3.png) ### “VGV” mark Increasingly we’ve been using the shorthand “VGV”, both internally and externally, so we knew we needed to address it in some way. While our primary brand will remain “Very Good Ventures”, our new wordmark style supports the abbreviation. In informal and known contexts, we’ll use this wordmark as another way to connect with our brand. ![VGV shorthand wordmark for informal and known contexts](/assets/images/blog/very-good-brand-refresh/body-4.png) ### “Very Good” stamp As our team grows, we’re finding we’re leaving our mark on many things and in many ways. Since we can’t always put our brand on something, we wanted to find a way to show affiliation without ownership. So, we also added some stamps into the mix for projects, partnerships, and other ventures that meet our Very Good standards. While we don’t anticipate that this will become a main logo for us, we like how this gives us the ability to apply our “stamp of approval” beyond VGV — and we think it’ll make some pretty sweet merch. ![Very Good stamp of approval badge for affiliated projects and partnerships](/assets/images/blog/very-good-brand-refresh/body-5.png) ## History of VGV brand Back in 2018 we set out to create a brand that made people feel good. We wanted a company name and logo that you could help but smile when you see it, but that felt inspired and professional. We called the company Very Good Ventures because, well, we feel we are “very good.” But you can always be better — so being the “best” is not a state but a never-ending goal. When considering an icon, our founder (who has two daughters) found inspiration in the juxtaposition of the many plush unicorn stuffies in his house and the idea that a unicorn is a billion-dollar company. What better icon for a serious company with big ambitions that doesn’t take itself too seriously. We’re happy that over the past few years, the VGV brand has caught on and resonated well with our team, our communities, and our clients. The updated brand is an exciting next step in our evolution. ## What’s next You’ll start seeing our new branding everywhere Very Good Ventures can be found, including our website, open source packages, social media, and more.  Stay tuned for some Very Good Merch, coming soon to the web near you. ### VGV 2021 Retrospective Source: https://verygood.ventures/blog/vgv-2021-retrospective/ This year has been a big one for us at Very Good Ventures. We've worked on many Flutter apps, created high-quality open source software, and even opened a couple of international offices. Here is a look back at the top five VGV moments from 2021. ## Betterment x VGV Working with Betterment has been a major highlight of 2021. Earlier this year we posted a [case study of our work together](https://verygood.ventures/success-stories/betterment/) which details how we helped Betterment integrate Flutter into their existing native app. We’ve continued to work together closely with their engineering team on expanding Flutter within the Betterment application, which continues to deliver top-notch investment tools to its users. Hear more about our work with Betterment in this podcast with Sam Moore or watch [this session from Droidcon London 2021](https://www.droidcon.com/2021/11/17/is-flutter-the-coke-zero-of-the-mobile-world/) where Jesse Harrelson and Florian Harr discuss their engineering team’s transition to Flutter and how we helped support them. ## Very Good CLI release We released [Very Good CLI](https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/) in February which enables users to generate a VGV-approved Flutter starter application called Very Good Core with one command. It includes a bunch of scalable features out of the box, including web support, build flavors, internationalization, sound null safety, layered architecture with bloc, 100% test coverage, logging, our preferred lint rules with [Very Good Analysis](https://pub.dev/packages/very_good_analysis), and continuous integration with GitHub Actions.  Since the initial launch, we’ve [released many new features](https://github.com/VeryGoodOpenSource/very_good_cli/blob/main/CHANGELOG.md), including templates for packages and plugins, and reached over 1,100 stars on GitHub. Check out the [Very Good CLI repo here](https://github.com/VeryGoodOpenSource/very_good_cli). ## Very Good Start Release Shortly after the launch of Very Good CLI, we released [Very Good Start](https://verygood.ventures/solution/very-good-start/), our proprietary Flutter project generator for our consulting clients. This tool is specifically designed for teams or developers looking for a solid foundation for a production-quality Flutter application. With Very Good Start, we can generate about one month’s work in one day, so that our clients can begin building new features right away.  Very Good Start has been a helpful tool for our internal teams. It helps our developers spin up a new codebase with a built-in scalable foundation in one day. It also helps our teams start working on features right away and they can always use the codebase as a good example for implementing a scalable architecture. As of now, Very Good Start is only offered to VGV clients as part of our ongoing consulting arrangements, but we’re looking for ways to make it available to more users. Stay tuned! ## I/O Photo Booth Teaming up with Google to build a web application for Google I/O was definitely a highlight from 2021. We combined Flutter + Firebase to build I/O Photo Booth, a performant web app where users could take photos with their favorite Google mascots and props. The app was even highlighted in the interactive virtual world I/O Adventure! The codebase is open source and showcases the scalable approach to building apps that we adhere to at VGV. You can still [take a photo in the I/O Photo Booth here](https://github.com/flutter/photobooth) or read the deep dive into [how it’s made](https://medium.com/flutter/how-its-made-i-o-photo-booth-3b8355d35883). ## VGV Expanded to the EU We officially opened our [first VGV EU office in Warsaw, Poland](https://verygood.ventures/contact-us/) in July. Led by Principal Engineer Dominik Roszkowski, our Poland team has already grown to four members (and counting). In keeping with our central hub office model, we also [opened offices in Madrid and Rotterdam](https://verygood.ventures/contact-us/) to support our teammates there. In 2022, we look forward to expanding our team in the U.S., Europe, and beyond! 🚀 ### Remembering our friend and teammate, Kevin Gray Source: https://verygood.ventures/blog/remembering-our-friend-and-teammate-kevin-gray/ It is with the deepest regret and sorrow that we have to say our dear friend and colleague, Kevin Gray, passed away suddenly from a recently diagnosed heart condition on Tuesday November 30, 2021 at the age of 33. We want everyone in the Flutter community to know and remember his name for what he did for us all. Many have seen or experienced his work — Kevin played a vital role in making Flutter what it is today. But Kevin was not a self-promoter, he was the ultimate team player, and as a result not many know the scope of his accomplishments. Kevin was one of the primary developers of the [Hamilton app](https://youtu.be/kfd-oLypqFI) —  the first commercial app built outside of Google to demonstrate Flutter to the world. Kevin was the developer of the “[Flutter Slides](https://youtu.be/5SZZfpkVhwk?t=121)” app, which was used at the Flutter Live event in 2018 to first demonstrate Flutter’s ability to run as a native macOS app. He reprised that work a year later for Flutter Interact in Brooklyn. Kevin worked on the [New York Times KENKEN](https://www.youtube.com/watch?v=LoLqSbV1ELU&t=3292s) app that demonstrated the capabilities of Flutter on the Web to the Google I/O audience in 2019. Kevin was also one of the original members of [Very Good Ventures](https://verygood.ventures/). Without his commitment to the company, there would likely have never been a VGV. Over the last 3.5 years, he contributed to many Flutter projects for at least 14 different VGV clients. While we got to know Kevin and understand his impact at VGV, he is an unsung hero of the Flutter community. Always kind, modest, humble, and unwavering in his commitment, Kevin’s brilliance and hard work was done mostly from the shadows. But, Kevin had a hand in many of the earliest influential Flutter apps and many of the Flutter community’s key moments. Most importantly, Kevin was the definition of a good person and a great teammate. You’d be hard pressed to find someone more kind, more friendly, and more willing to help. He was up for anything and never asked for anything. You could count on him like you could count on the sun rising. Kevin is irreplaceable. He’ll always be a member of our team. We will miss him dearly. Please take a moment to honor him and remember him. Kevin’s obituary can be found here: [link](https://www.hawthornefuneralhome.com/obituary/Kevin-Gray). If you would like to support a cause that was important to Kevin, Plan International, you may do so at: [https://donate.planusa.org](https://donate.planusa.org) Please select the “Dedicate this donation to someone” checkbox, add “Kevin Gray” as the Honoree name, and mail the card to the following address: “D Gray, PO Box 861 White Plains, NY 10602”. My personal relationship with Kevin goes back to January 2015 when we hired him as an intern at Posse. I remember his first interview. He was clearly nervous and there were frequent pauses of silence as he collected his thoughts. Many companies would likely have passed based on the interview. But we gave him a shot. After all, we weren’t interested in how he interviewed — we were interested in who he was. Kevin is from Westchester in New York, and he studied game development in college. He always had a passion for games. He was an extremely dedicated Pokemon Go player — I bet he was still playing on his last day. In his professional career, he shifted his talents to mobile app development. First, he learned to be a great iOS engineer, then he learned Android. He emerged as a prolific member of our team. He was involved in the development of more native apps than I am able to count (or at least remember). In 2017, he jumped into Flutter development for the Hamilton app. Flutter was in its infancy and there were no answers on Stack Overflow. It was unclear if Flutter was going to be a good move professionally. None of us had any idea what we were doing. Kevin never batted an eye. He jumped in and never asked why we were doing this. When he hit a challenge or a roadblock he worked the problem until he found a solution. I don’t ever remember Kevin complaining. I only remember the confidence that if you gave Kevin a task, you knew it’d get done and get done well. Later, along with myself and Albert Lardizabal, Kevin was one of the founding members of Very Good Ventures. It was a difficult moment for all of us as our future was very uncertain. It would have been perfectly reasonable for Kevin to look for more stability and better opportunities elsewhere. But he stuck with us. Without Kevin there would be no VGV. All of the things that VGV has gone on to become would not have happened if Kevin hadn’t taken the leap of faith to enable the new company to get started. From there, Kevin did much of the early work as we ultimately decided to take on more Flutter work and become a consultancy. He continued to maintain and improve the Hamilton app, but he also took on any new project — no matter how perfect, no matter how poorly scoped, no matter how interesting, and no matter how stressful. And he loved his job. In a recent review when asked “What would make the next 6 months successful for you?”, he responded: *To be honest I can't say that I ever feel unhappy with my job. I feel really fortunate that I like programming as much as I do and that this is the career path I've chosen.* At VGV, Kevin found confidence and a role that really suited him and that he loved. I and his other VGV teammates trusted him immensely. I often say that trust in each other is the most important thing a team needs in order to continue to be successful. We all trusted Kevin fully. Ultimately, Kevin was the perfect teammate and a terrific friend. He was talented, reliable, quick with a smile, funny, curious and explorative, humble and self-effacing, always willing to lend a hand, and serious without taking himself too seriously. Over the years, Kevin has been a constant. It’s hard to accept he’s not going to respond on slack, review a pull request, or meet us for a beer. It’s hard to accept that we’ve lost a friend we know will be there for us. It’s hard to accept that he isn’t going to get to see what this great company he helped start will become. It’s just hard to accept. All we can do now is honor and remember Kevin. His impact and mark is clear and powerful. He is going to live on as a part of VGV forever.  We’ll miss you Kevin. ### Bloc v8.0.0 is out! Source: https://verygood.ventures/blog/bloc-v8-release/ ## Overview That latest stable version of the Bloc library is out: Bloc v8.0.0! Most notably, v8.0.0 removes the deprecated **mapEventToState** API in favor of **on**, which was introduced in v7.2.0. This is a breaking change, so while things were backwards compatible with ^7.2.0, upgrading to v8.0.0 will require that all blocs are refactored to use the APIs. If you haven’t already, we highly recommend waiting to upgrade to v8.0.0 until all blocs have been refactored to use the new APIs. You can do this at your own pace using ^7.2.0. ## Additional changes In addition, v8.0.0 introduces a new **BlocOverrides** API which enables specifying **BlocObserver**, **EventTransformer**, and **HydratedStorage** overrides that are scoped to a particular zone. With the previous implementation, you were constrained to having a single, global observer/event transformer/storage. The new API enables those overrides to be scoped and for multiple implementations of these overrides to exist within a single application. **Before v8.0.0** ```dart void main() { Bloc.observer = CustomBlocObserver(); Bloc.transformer = customEventTransformer(); // ...} ``` **After v8.0.0** ```dart void main() { BlocOverrides.runZoned( () { // ... }, blocObserver: CustomBlocObserver(), eventTransformer: customEventTransformer(), );} ``` For more context regarding the **BlocOverrides** API refer to [this pull request](https://github.com/felangel/bloc/pull/2932). This upgrade also brings a number of fixes and improvements, including better error handling and reporting via the **addError** API. The full list of changes can be [found here](https://github.com/felangel/bloc/pull/2798). ## What’s next We’re experimenting with Flutter APIs to expose the **BlocOverrides** API within the context of the widget tree. The goal of this exploration is to make it even easier to scope instances of **BlocObservers**, **EventTransformers**, and **Storage** to different parts of the Flutter application. A common use case is using an encrypted storage implementation when the user is authenticated and an unencrypted implementation when there is no user authenticated within the context of [**hydrated_bloc**](https://pub.dev/packages/hydrated_bloc). Look out for a proposal soon regarding what these changes could look like. ## [**View the full migration guide for bloc v8.0.0**](https://bloclibrary.dev/#/migration) ## For more on migrating your blocs, check out our other blogs: - [**How to Use Bloc with Streams and Concurrency**](https://verygood.ventures/blog/how-to-use-bloc-with-streams-and-concurrency/)**: How to migrate your blocs and cubits to Bloc >=7.2.0** - [**What's New in Bloc v7.2.0**](https://verygood.ventures/blog/whats-new-in-bloc-v7-2-0/)**: An overview of the changes introduced in the Bloc v7.2.0 release** *Scarlett Wardrop contributed to this article.* ### Flutter Bloc with Streams: Concurrency Patterns & Examples Source: https://verygood.ventures/blog/how-to-use-bloc-with-streams-and-concurrency/ [Bloc 7.2.0 and Bloc 8.0.0](https://verygood.ventures/blog/whats-new-in-bloc-v7-2-0/) introduce a new way to register [event handlers](https://github.com/felangel/bloc/issues/2526). With this change comes several benefits, including reduced boilerplate, better consistency with cubit, and, most of all, concurrent event processing — by default! Let's take a look at the classic Counter bloc, updated for Bloc 7.2.0. ```dart class CounterBloc extends Bloc { CounterBloc() : super(0) { // The future is here. on((event, emit) => emit(state + 1)); on((event, emit) => emit(state - 1)); } } ``` Prior to Bloc 7.2.0, events added all at once would be processed sequentially. Bloc >=7.2.0 now processes event handlers at the same time. Let's see what that means in practice. ## The new reality ```dart BlocProvider.of(context)..add(EventA())..add(EventB()); ``` In the example above, both **EventA** and **EventB** will be processed concurrently. If they yield conflicting states, a [race condition](https://en.wikipedia.org/wiki/Race_condition) results. In Bloc >=7.2.0, whichever event finishes processing first in that particular moment will have its state overwritten by the event which took the longest. If you haven't designed your blocs with this in mind, don't worry! The rest of this article will demonstrate several examples using the old bloc syntax and show how they can be migrated to safely and effectively use Bloc 7.2.0. In this article, we'll build each scenario the old way, using **mapEventToState**, before migrating it to Bloc 7.2.0's new event registration system. In total, we will demonstrate three different scenarios, each taking advantage of bloc's [new concurrency features](https://bloclibrary.dev/#/migration?id=v720). To avoid making this article too long, we're showing only the relevant snippets of code which explain the concept. You can view the complete source code for all three scenarios in [the accompanying code repository](https://github.com/VGVentures/bloc_concurrency_demos), which was created with [Very Good CLI](https://pub.dev/packages/very_good_cli). The code for each bloc is included twice: one uses the old **mapEventToState** approach, and the other uses the new event registration system. In addition, if you run the app, you can examine the code for both the old and new blocs and see how they work with the same exact widgets. ### Sequential events Prior to Bloc 7.2.0, events were processed sequentially, one after the other. If an event called asynchronous methods and took a long time to finish, the other events just queued up, patiently waiting for their turn. ```dart class MyBloc extends Bloc { MyBloc() : super(MyState()); Stream mapEventToState(MyEvent event) async* { if (event is DoSomething) { // If fetchSomething() takes a while, it will block the // internal event queue, preventing other events from firing. final results = await repo.fetchSomething(); yield MyState(results: results); } } } ``` If you needed to process events concurrently prior to Bloc 7.2.0, you could have used a cubit. ```dart class MyCubit extends Cubit { Future doSomething() async { final results = await repo.fetchSomething(); emit(MyState(results: results, otherThings: state.otherThings)); } Future doSomethingElse() async { final otherThings = await repo.doSomethingElse(); emit(MyState(results: results, otherThings: state.otherThings)); } } ``` Because cubits emit states directly from methods, you can call as many methods on a cubit as you want from a widget (or anything that's using a cubit). *Bloc 7.2.0 aligns bloc and cubit more closely since bloc now processes events concurrently. While cubit itself doesn't use events, you can* ***await*** *multiple methods on it at once, which is essentially the same as processing multiple events at once in a bloc.* ```dart class MyWidget extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { return TextButton( onPressed: () { final cubit = BlocProvider.of(context); // We'll pretend the cubit methods return futures that // we are deliberately not awaiting, allowing us to // fire them both at the same time. cubit.doSomething(); cubit.doSomethingElse(); }, child: Text(state.results), ); }, ); } } ``` ### A structured approach to concurrency Bloc 7.2.0 has deprecated **mapEventToState** in preparation for Bloc 8.0.0, which will require event handlers to use the **on()** registration API. Below is a demonstration of the new event registration API. Note that we're passing in one of the supported event transformers from the new [bloc_concurrency](https://pub.dev/packages/bloc_concurrency) package to demonstrate how concurrency can be *handled per event class*. ```dart import 'package:bloc/bloc.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; class MyBloc extends Bloc { MyBloc() : super(MyState()) { on( (event, emit) async { final results = await repo.fetchSomething(); // Use emit instead of yield. emit(MyState(results: results)); }, transformer: droppable(), ); } } ``` Different types of events registered with **on()** are processed concurrently, meaning that blocs and cubits are finally on equal footing. ### What about mapEventToState? While you're migrating old blocs, you may be tempted to just slap the **sequential()** transformer on all your events to mimic the previous behavior. You could even specify it as the default for your entire app globally. ```dart // Change the default event transformer for all blocs everywhere. Bloc.transformer = sequential(); ``` **However**, that statement alone will not make Bloc behave like it used to! Events fall into buckets, based on how you register them. Instead of processing all events sequentially in the order they were added, each event is processed sequentially within its own bucket, and multiple buckets can be processing concurrently. *If you choose to override the global event transformer, as shown above,* ***you must also remember to do it in your tests****.* Overriding the default event transformer is equivalent to registering event buckets like this: ```dart on((event, emit) { ... }, transformer: sequential()); on((event, emit) { ... }, transformer: sequential()); // Only one EventA can be running at once. // Only one EventB can be running at once. // An EventA and an EventB can both be running at the same time. ``` If you need your blocs to behave purely sequentially, you'll have to register a single bucket to catch all of the events. You'll also have to ensure your event bucket transformer is **sequential** (or change the global transformer). The resulting behavior is equivalent to the way **mapEventToState** behaved. ```dart abstract class MyEvent {} class EventA extends MyEvent {} class EventB extends MyEvent {} class MyBloc extends Bloc { MyBloc() : super(MyState()) { // To catch every event, we just need to register // the parent class of all the event subclasses. on( // If you squint, it's just like looking at mapEventToState (event, emit) { // Don't forget to pass emit to your handlers, though! if (event is EventA) _handleEventA(event, emit); else if (event is EventB) _handleEventB(event, emit); }, transformer: sequential(), ); } void _handleEventA(EventA event, Emitter emit) { ... } void _handleEventB(EventB event, Emitter emit) { ... } } ``` For many migration scenarios, you might not need anything else, but, if you want to take advantage of the new features, read on! ## Blocs and streams Sometimes, we have blocs that listen to a stream. Often, this stream bubbles up from a repository in our [domain layer](https://verygood.ventures/blog/boring-code-part-1/), where it can be listened to by multiple blocs. The blocs listening in use the data stream to make their business logic decisions. It's not uncommon to find streams in large Flutter apps when dealing with authentication, video streaming, image uploads/downloads, etc. *Often, using a repository that exposes a stream of data — i.e.,* ***a reactive repository*** *— is one way to prevent blocs from depending on other blocs. Instead of creating a sibling dependency between blocs, which is extremely hard to manage as a project grows, moving the data to the domain layer and "listening in" from blocs above is often an elegant solution to what would otherwise be a dependency nightmare. There are other solutions to prevent direct bloc-to-bloc coupling, too, such as putting bloc listeners higher in the widget tree and adding events to other blocs when another bloc's state changes.* ### Video streaming example We can demonstrate Bloc's improved support for working with streams by creating a video streaming app! We'll use a fake video streaming repository that exposes a stream of images. Each image represents a video frame received from over the network. Before Bloc 7.2.0, this is what our bloc might have looked like: ```dart class VideoStreamBlocOld extends Bloc { VideoStreamBlocOld({ required this.videoStreamingRepo, required VideoData frame, }) : super(VideoStreamState.initial(frame)); final VideoStreamingRepo videoStreamingRepo; StreamSubscription? _subscription; @override Stream mapEventToState(VideoStreamEvent event) async* { if (event is VideoStreamPlayPauseEvent) { if (event.play) { _subscription ??= videoStreamingRepo.videoDataStream.listen(_onVideoData); yield VideoStreamState( currentFrame: state.currentFrame, isPlaying: true, ); } else { await _subscription?.cancel(); _subscription = null; yield VideoStreamState( currentFrame: state.currentFrame, isPlaying: false, ); } } else if (event is VideoStreamUpdatedEventOld) { if (state.isPlaying) { yield VideoStreamState(currentFrame: event.frame, isPlaying: true); } } } @override Future close() async { await _subscription?.cancel(); await super.close(); } void _onVideoData(VideoData data) { add(VideoStreamUpdatedEventOld(frame: data)); } } ``` Unfortunately, we have to go through the usual hassle of subscribing to a stream via **listen()** and canceling the subscription (if it is active) during bloc **close()**. [We can't use await for](https://github.com/dart-lang/sdk/issues/42717) to easily listen to the stream. If we did, the event handler would run for as long as the stream was alive, [blocking any other events](https://github.com/felangel/bloc/issues/232#issuecomment-485146815) in that bloc from executing. *Technically, you can* ***break*** *out of an* ***await for,*** *but Bloc doesn't provide a way for a specific invocation of an event handler to know when it has outlived its usefulness.* ```dart // DON'T DO THIS IN A BLOC. // THIS PREVENTS YOUR EVENT HANDLERS FROM FINISHING. await for (final frame in videoDataStream) { // Beautiful, but not meant to be. yield VideoStreamState(currentFrame: event.frame, isPlaying: true); } ``` Because we have to manage our stream manually from **mapEventToState**, it's best to fire another event whenever we receive stream data so that we can yield out states correctly. If we don't, we risk running into the [nested async generator bug](https://github.com/dart-lang/sdk/issues/44616). Now, imagine we've upgraded to Bloc 7.2.0 in our app. Here's what our video streaming bloc might look like now. ```dart class VideoStreamBlocNew extends Bloc { VideoStreamBlocNew({ required this.videoStreamingRepo, required VideoData frame, }) : super(VideoStreamState.initial(frame)) { on( (event, emit) async { if (event.play) { await emit.forEach( videoStreamingRepo.videoDataStream, onData: (VideoData videoStreamData) => VideoStreamState( currentFrame: videoStreamData, isPlaying: true, ), ); } else { emit( VideoStreamState( currentFrame: state.currentFrame, isPlaying: false, ), ); } }, // Allow only one of these events to ever be active at once, canceling // any active `emit.forEach` above. transformer: restartable(), ); } final VideoStreamingRepo videoStreamingRepo; } ``` Wow! That cleaned up nicely. When working with streams in Bloc >=7.2.0, we can use the methods provided on the **emit** object to make working with streams easier. The **forEach** method allows us to receive a callback when a stream has data. When our event handler is canceled or finished, it cleans up after us, ending the stream subscription. Notice how we were able to eliminate the **VideoStreamUpdatedEventOld** event entirely. Because of the way **emit** is given to us in our event handler, we can emit states directly from inside the stream callback without any issues, drastically reducing the amount of boilerplate needed when working with streams and circumventing the synchronization errors that would have resulted from the [nested async generator bug](https://github.com/dart-lang/sdk/issues/44616). *While you might think* ***emit*** *is just a function used to spit out states, like it is on cubit, it's actually a* [*callable class*](https://dart.dev/guides/language/language-tour#callable-classes)*. If you look at the type information, you'll see that* ***emit*** *is actually an* ***Emitter****. When you invoke* ***emit*** *as a function, it's actually calling the* ***call*** *method of the* ***Emitter class****.* We also used the **restartable()** transformer for the **VideoStreamPlayPauseEvent**. The **restartable()** event transformer will cancel any previous **VideoStreamPlayPauseEvent** event still running when a new one is added, ensuring only one event handler is ever active at once (for those particular types of events). Using **restartable()** is a great way to keep your state up to date with events that might still be processing when another event of the same type is added. Don't use it, however, if the value of previous results is important to your bloc! ## Using Bloc concurrency transformers Because Bloc now allows us to specify [concurrency](https://pub.dev/packages/bloc_concurrency) per event type, we can more easily influence how events are processed than we did before Bloc 7.2.0. We can even allow some events to be processed concurrently, while others are processed sequentially. As a rule of thumb, events which "read" data can often be concurrent and restartable, since the UI typically only cares about the most recent state from a bloc. Events which write (or mutate) data will need to be handled on a case-by-case basis. If a mutation event updates or replaces multiple pieces of state, that event might actually be an [atomic operation](https://en.wikipedia.org/wiki/Atomicity_\(database_systems\)). Generally speaking, atomic operations should be processed sequentially. That is, **if the order of mutation events influences the final state, those events should be processed sequentially**. If, however, **the order of mutation events does *not* influence the final state, the events may be processed concurrently**. Such an example is given below. ### Deleting files concurrently in the cloud For our second example, we'll build a stripped-down version of a cloud file storage app, such as OneDrive or Dropbox, that allows the user to manage the files they've uploaded (except ours will be much simpler). Instead of uploading files, we can only delete files and pull to refresh. We'll use a fake repository implementation which starts off with some placeholder files. If the fake repository receives a request to load more files after all the files have been deleted, it will start over with a fresh set of placeholder files. Now you can fulfill that fantasy of deleting files forever! To demonstrate the old way of doing things, we'll use a cubit to manage the file logic. Before Bloc 7.2.0, using a cubit was the only practical way to process "events" concurrently without holding futures in the bloc state. First, we need to define the state we'll be using for our file cubit. ```dart class FileState extends Equatable { final Map fileView; late final List files = fileView.values.toList(); final Set pendingDeletions; final bool isLoading; final Object? error; } ``` Later on, we'll use the exact same state for the updated bloc. Of importance is the **fileView** map, which contains our **File** model objects, keyed by their IDs. We've used a typedef to define **FileId** as an **int**, and we've omitted the **File** model here for brevity. If you want to check out the full implementation, see the [accompanying repository](https://github.com/VGVentures/bloc_concurrency_demos). The state also exposes the list **files** to make it easier for widgets to consume in a **ListView.builder**. We're using the **late final** modifier on this property to avoid converting the map to a list more than once per state. In a real app, we might have sorted the list based upon filename, timestamp, or a sorting field specified by the user. *When you create a* ***Map*** *in Dart, you're actually creating a* ***LinkedHashMap*** *under the hood, which is sorted by key insertion order. Calling* ***fileView.values.toList()*** *will reflect that ordering in the resulting list.* We'll define two methods on our cubit: **loadFiles()** and **deleteFile(FileId)**. Both will emit states using the cubit's **emit** method, which is equivalent to **yield**ing a state from a bloc. Because **fileView** is a map and **pendingDeletions** is a set, we can easily update our files and pending deletions without disturbing the rest of the state. ```dart class FileCubitOld extends Cubit with FileCubitCommon implements FileCubit { FileCubitOld({required this.fileRepo}) : super( const FileState(fileView: {}, pendingDeletions: {}, isLoading: false), ); @override final FileRepo fileRepo; Future loadFiles() async { Map? files; Object? error; try { emit( FileState( fileView: state.fileView, pendingDeletions: state.pendingDeletions, isLoading: true, ), ); files = await fileRepo.loadFiles(); } catch (e) { error = e; } finally { emit( FileState( fileView: files ?? state.fileView, pendingDeletions: state.pendingDeletions, isLoading: false, error: error, ), ); } } Future deleteFile(FileId fileId) async { if (state.pendingDeletions.contains(fileId)) { return; } emit( FileState( fileView: state.fileView, isLoading: state.isLoading, pendingDeletions: { ...state.pendingDeletions, fileId, }, ), ); try { await fileRepo.deleteFile(id: fileId); emit( FileState( isLoading: state.isLoading, fileView: {...state.fileView}..remove(fileId), pendingDeletions: { ...state.pendingDeletions, }..remove(fileId), ), ); } catch (e) { emit( FileState( isLoading: state.isLoading, fileView: state.fileView, pendingDeletions: { ...state.pendingDeletions, }..remove(fileId), error: e, ), ); } } } ``` The **deleteFile** method indicates that a deletion is pending while it waits on the deletion result from the file repository by emitting a state which adds the **fileId** to the **pendingDeletions** set. If the deletion succeeds, **deleteFile** emits a state that removes the file from the **fileView** map and the **fileId** from the set **pendingDeletions**. Likewise, if the deletion fails, **deleteFile** emits a state with the error object containing the exception that occurred. All that's left to do is migrate it to the new event system! Fortunately, migrating a cubit to use the new event registration is trivial. The resulting bloc logic is essentially identical. In fact, the [actual example](https://github.com/VGVentures/bloc_concurrency_demos) uses a **mixin** to share the **loadFiles** and **deleteFiles** method between the old cubit and the new bloc. ```dart class FileBlocNew extends Bloc with FileCubitCommon { FileBlocNew({required this.fileRepo}) : super( FileState( fileView: const {}, pendingDeletions: const {}, isLoading: false, ), ) { on( (event, emit) => loadFiles(event, emit.call), transformer: restartable(), ); on( (event, emit) => deleteFile(event, emit.call), transformer: concurrent(), ); } @override final FileRepo fileRepo; } ``` We're using **restartable()** on the **LoadFiles** event so that only the latest loading event will ever emit states, keeping our state nice and clean. If we instead let multiple loading events run at once, an older **LoadFiles** event, which took longer to complete than a newer one, might emit states which overwrite the ones from the newer event and show older files that may not exist anymore. Whenever that happens, the UI gets out of sync. Further, if the bloc or widgets try to perform operations on data that's not there (or out of date), state errors can creep up out of nowhere, wrecking the application. Making the **LoadFiles** event restartable ensures our UI is synchronized with our server, even in the most trying conditions. Such hangups are extremely unlikely, but the whole point of bloc is to provide [predictable state management](https://bloclibrary.dev/). Because deleting a file doesn't affect the other files, we're able to process those events concurrently, which keeps the UI unblocked and allows the user to keep doing what they do best. ### Checking username availability Back in the old days, transforming bloc events quickly became non-trivial. In this third (and final) scenario, we'll demonstrate how event transformation has improved by creating a user registration form. When the user types, the form will send username-changed events to the bloc, which will then check to see if the given username is available. The bloc will debounce username availability check requests and restart any pending events when a duplicate event is received. *By* ***debouncing****, we mean that the bloc will only actually fire an event if an event of the same type hasn't fired within a given duration. We also want the username availability check events to be restartable, since we only care about the latest result.* For the sake of simplicity, we've omitted some of the details surrounding the form validation (we're using [formz](https://pub.dev/packages/formz)), but you can find all of it in the [accompanying code repository](https://github.com/VGVentures/bloc_concurrency_demos). Here's what the old bloc might have looked like. ```dart class RegistrationBlocOld extends Bloc { RegistrationBlocOld({required this.registrationRepo}) : super(RegistrationState.initial); @override final RegistrationRepo registrationRepo; @override Stream mapEventToState(RegistrationEvent event) async* { if (event is UsernameChanged) { // ... } else if (event is Register) { // ... } } @override Stream> transformEvents( Stream events, Stream> Function( RegistrationEvent) transitionFn, ) { final deferredEvents = events .where((e) => e is UsernameChanged) .debounceTime(RegistrationBloc.debounceUsernameDuration) .distinct() .switchMap(transitionFn); final forwardedEvents = events.where((e) => e is! UsernameChanged).asyncExpand(transitionFn); return forwardedEvents.mergeWith([deferredEvents]); } } ``` The **transformEvents** method above uses Stream extension methods from [rxdart](https://pub.dev/packages/rxdart) to debounce only the **UsernameChanged** events and make them restartable. If that's not immediately apparent to you, don't worry. You are not alone. Thankfully, the new way of doing things makes it much easier to transform bloc event streams. In fact, our new bloc might look something like this: ```dart class RegistrationBlocNew extends Bloc { RegistrationBlocNew({required this.registrationRepo}) : super(RegistrationState.initial) { on( (event, emit) async { // ... }, // We will use our own debounce/restartable transformer for this event. // Other events can use their own appropriate transformers. transformer: debounceRestartable(RegistrationBloc.debounceUsernameDuration), ); on( (event, emit) async { // ... }, transformer: sequential(), ); } @override final RegistrationRepo registrationRepo; EventTransformer debounceRestartable( Duration duration, ) { // This feeds the debounced event stream to restartable() and returns that // as a transformer. return (events, mapper) => restartable() .call(events.debounceTime(duration), mapper); } } ``` Because the new bloc API applies transformers to event buckets, we don't have to worry about merging the streams back together to only selectively transform certain events, like we did in the old bloc. ### Migrating messy blocs Before we finish, let's discuss one more pitfall you might encounter while migrating a bloc. You've probably seen a bloc that looks something like this before, or maybe you've even written one like it: ```dart class BadBloc extends Bloc { BadBloc() : super(MyState()); @override Stream mapEventToState(MyEvent event) async* { if (event is EventA) { yield* _handleEventA(); } } Stream _handleEventA() async* { yield* _helperMethod(); ... } Stream _helperMethod() async* { yield MyState( ... ); ... } } ``` The bloc above yields states from helper methods — an indicator that the bloc code can be cleaned up. Besides the fact it triggers the [nested async generator bug](https://github.com/dart-lang/sdk/issues/44616), the code above is also harder to migrate and harder to follow. ```dart class BadBloc extends Bloc { BadBloc() : super(MyState()) { on((event, emit) => _eventA(event, emit)); } void _eventA(EventA event, Emitter emit) async { _helperMethod(emit); ... } void _helperMethod(Emitter emit) async { emit(MyState( ... )); ... } } ``` Notice how we have to pass the emit all the way from the **on** handler through **\_eventA** and into **\_helperMethod**? If **\_helperMethod** had relied on yet another helper method to emit states for it, it would also have to keep passing **emit** down through the call stack. Not only is this a lot of extra typing, it also makes the code harder to follow. If we only emit or yield states from primary event handlers, we can avoid the aforementioned headache. Let's refactor the original bloc. The helper methods can return any information needed by the calling event handler, but they shouldn't emit states. ```dart @override Stream mapEventToState(MyEvent event) async* { if (event is EventA) { yield* _handleEventA(); } } Stream _handleEventA() async* { final nextState = _helperMethod(); yield nextState; ... } MyState _helperMethod() { ... return MyState( ... ); } ``` Here, we're returning a state from **\_helperMethod**, rather than using it to yield states directly. This avoids the nested async generator bug and helps us keep all of our state changes in the actual event handler. When we migrate the new and improved bloc, we can see that emit only needs to be passed to the handler method. Helper methods will return other types of data or states, but they should never emit states directly. ```dart class GoodBloc extends Bloc { GoodBloc() : super(MyState()) { on((event, emit) => _eventA(event, emit)); } void _eventA(EventA event, Emitter emit) async { final nextState = _helperMethod(); emit(nextState); ... } MyState _helperMethod() { return MyState( ... ); } } ``` While cleaning up the method signatures might seem like a subtle change, we've greatly improved the readability and maintainability of our bloc. All of the state changes for a given event happen in one place, so it's easy to see when something goes wrong. ## Wrapping up Bloc >=7.2.0 allows us to easily take advantage of concurrent event processing, while still being predictable, easy to test, and indicative of clear and concise business logic contracts. By designing our blocs thoughtfully, we can take advantage of everything Bloc has to offer to solve business logic problems more elegantly than ever before. **If you haven't already, be sure to check out the** [**complete code example**](https://github.com/VGVentures/bloc_concurrency_demos)**.** *Felix Angelov and Kirpal Demian contributed to this article.* ### Top 5 Things We Look For in Flutter Code Assessments Source: https://verygood.ventures/blog/top-5-things-in-flutter-code-assessments/ A typical part of our client onboarding process involves conducting a code assessment. Depending on client needs, a code assessment can be quite thorough, or simply provide an overview of the main characteristics that we should focus on as a team. With each assessment, we provide our recommendations for fixing any issues we spot in order of priority, which helps us plan the best way to get started together. Digging into the code also helps us become familiar with the app’s structure so that we can begin to provide value right away — whether it’s building out a new feature or refactoring a part of the codebase to solve a pain point. We can also get an overview of code quality and if there is anything that needs to be fixed to ensure that the codebase is scalable. As consultants, we see and work in a lot of codebases. We have a [proven approach to building scalable codebases](https://verygood.ventures/blog/scalable-best-practices/), so we can quickly determine which codebases are solid, and which ones may need some work to get them up to our standards. Here are the top five things we look for when assessing Flutter codebases and why they matter: ## 1. Tests ✅ The number one thing we look for are tests. In order of priority, we look for unit tests, then widget tests, and finally, integration tests. If there are no tests present, this is a huge red flag. No tests means that the code may not be properly functioning, adding new features or performing refactors may break existing code, bugs may be hard to track down, and there may be other unsavory side effects. Flutter comes with a [testing suite built into the SDK](https://flutter.dev/docs/testing), so there really is no excuse to forego writing tests. [We strive for 100% code coverage in all of our projects](https://verygood.ventures/blog/guide-to-flutter-testing/), but when assessing codebases, **the presence of some tests is better than none.** It’s much easier to raise the code coverage threshold gradually than start writing tests from scratch. Oftentimes, the absence of tests is accompanied by code that is not testable. Comprehensive tests are a great sign of a well-structured, healthy codebase. ## 2. Latest Version of Flutter & Dependencies ✅ This may seem obvious, but using the [latest stable version of Flutter](https://flutter.dev/docs/development/tools/sdk/releases) is crucial. It’s a simple way to ensure you’re taking full advantage of the latest Flutter features and bug fixes. It’s also very important to take time to upgrade when a new version is released to avoid falling far behind and accumulating tech-debt. In contrast, using an old version of Flutter can have a few undesired outcomes. Dependencies can become outdated, and you could be missing out performance improvements or new features in the latest versions of Flutter and Dart. We recommend using the latest stable version of Flutter whenever possible, as well as keeping all dependencies up to date. ## 3. Well-Organized Project Structure ✅ A well-organized codebase can be a good indicator of code quality. It can also help separate business logic from the UI, which is something we recommend when building a scalable code base. 🛑 **A not-so-great codebase may look like this:** - A single file that contains the entire app. - Presence of a “common” or “shared” folder in a codebase that contains lots of miscellaneous code. - Codebase organized by component, rather than feature (e.g. folder called “screens” that contains all of the app screens). ✅ **A very good codebase may look like this:** - One folder per feature, with each folder containing only the code for that feature. You should be able to look at the project structure and know exactly where to find each part of the app. For an example of a codebase with this structure, check out the [GitHub repo for I/O Photo Booth](https://github.com/flutter/photobooth). - Layered architecture and reusable code is split into internal packages. ## 4. CI/CD Solution ✅ Not using a CI/CD solution in a codebase is a red flag. This means that critical things like tests, code formatting, and linting are not automatically enforced through the CI/CD. There may as well be no coding standards, as you are relying on manual PR review to enforce standards, which is just not a scalable solution. It’s much more effective to offload these tasks to a CI/CD solution so that you can ensure tests, lint rules, and whatever else you want to enforce is done so automatically on every pull request. The good news is that there are multiple CI/CD solutions available for Flutter — our favorites are [GitHub Actions](https://github.com/features/actions) and [Codemagic](https://flutterci.com/).\* We prefer maintaining our CI/CD configuration within the codebase (rather than in an external tool), in order to track and review changes just like any other code. ## 5. Analysis Options ✅ Linting rules are a great way to enforce best practices across a codebase. [Mike Rydstrom has a great comparison](https://rydmike.com/blog_flutter_linting) of the most popular Flutter lint rule packages from pub.dev (check out our preferred lint rules in [very_good_analysis](https://pub.dev/packages/very_good_analysis)). The most important thing is that you choose the lint rules that make the most sense for your project, so that you can enforce consistency across your codebase. If you’re currently not using lint rules in your project, adding them and addressing the recommendations from the output can provide a helpful roadmap for improving your codebase. ## Conclusion Maintaining a scalable codebase may be the single most important thing you can do for your app. Implementing the above characteristics will go a long way in ensuring that your code quality remains high. ### Building a Very Good Location Sharing Chat App with Flutter Source: https://verygood.ventures/blog/very-good-location-sharing-chat-app/ Ever imagined building a chat application like Facebook Messenger or WhatsApp? Typically, you would need to build a whole real-time infrastructure as a backend first, and then build your chat client for your users to start communicating. This could lead to a lot of time spent on development. Building a Flutter chat application is surprisingly fast and easy thanks to our friends at [Stream](https://getstream.io/) and their [Stream Chat SDK](https://getstream.io/chat/sdk/flutter/), which provides us with all the functionality we need to hook up to a powerful chat API and start developing our app right away. ![Stream Chat SDK interface for Flutter chat integration](/assets/images/blog/very-good-location-sharing-chat-app/body-0.png) *Stream Chat SDK* We'll learn how to build a location-sharing chat app with Stream's real-time infrastructure and our opinionated set of tools (like [flutter_bloc](https://verygood.ventures/blog/why-we-use-flutter-bloc/)) and best practices. So, let's build a very good chat app! ## ChatRepository First, we'll create a **ChatRepository** that will manage the chat domain. This repository will be created as a standalone package and will have the following dependencies: ```yaml location: ^4.3.0 stream_chat_flutter: ^2.0.0 ``` To use the Stream Chat SDK, we will provide our **ChatRepository** with a **StreamChatClient** that will act as our API in charge of interacting with the Stream backend. Additionally, we'll have a dependency on **Location** to have access to the device's location services, which will allow us to handle permissions and retrieve the current location data. ```dart class ChatRepository { ChatRepository({ StreamChatClient? chatClient, Location? location, }) : _chatClient = chatClient ?? StreamChatClient(''), _location = location ?? Location(); final StreamChatClient _chatClient; final Location _location; } ``` ### StreamChatClient We'll use our **StreamChatClient** to handle the connection and joining channels to chat. The connect method must be the first to be called as it's the entry point for our user to be authenticated. It accepts extra information like the user's avatar, which can be provided as a **Uri**. ```dart Future connect({ required String userId, required String token, Uri? avatarUri, }) { final extraData = {}; if (avatarUri != null) { extraData['image'] = '$avatarUri'; } return _chatClient.connectUser( User(id: userId, extraData: extraData), token, ); } ``` Now that our client has authenticated the user, we can access the **currentUser** via the client's state property. We will return the user id from this **getUserId** method for it to authenticate the user on the Stream Chat SDK widgets (you will see it in action later). ```dart String getUserId() { final user = _chatClient.state.currentUser; if (user == null) { throw StateError( 'could not retrieve user. did you forget to call connect()?', ); } return user.id; } ``` ### Location To share the user's location, we need to first retrieve it through our **Location** API. This is achieved by the **getCurrentLocation** method, which already handles asking for the location permission. The location is returned in the shape of a custom class called **CoordinatePair**. ```dart Future getCurrentLocation() async { final serviceEnabled = await _location.serviceEnabled(); if (!serviceEnabled) { final isEnabled = await _location.requestService(); if (!isEnabled) throw CurrentLocationFailure(); } final permissionStatus = await _location.hasPermission(); if (permissionStatus == PermissionStatus.denied) { final status = await _location.requestPermission(); if (status != PermissionStatus.granted) throw CurrentLocationFailure(); } late final LocationData locationData; try { locationData = await _location.getLocation(); } catch (_) { throw CurrentLocationFailure(); } final latitude = locationData.latitude; final longitude = locationData.longitude; if (latitude == null || longitude == null) throw CurrentLocationFailure(); return CoordinatePair(latitude: latitude, longitude: longitude); } ``` ## Chat UI Now that we have our **ChatRepository**, it's time to create our second package: **chat\_ui**. This package will contain our widgets that will be abstracting the Stream Chat SDK widgets for our customization. ### ChannelAppBar First, we will create a **ChannelAppBar** that will use **ChannelHeader** from Stream Chat under the hood. It looks like this: ```dart class ChannelAppBar extends StatelessWidget with PreferredSizeWidget { const ChannelAppBar({ Key? key, required stream_chat_flutter.Channel channel, }) : _channel = channel, super(key: key); final stream_chat_flutter.Channel _channel; @override Widget build(BuildContext context) { final channel = context .findAncestorStateOfType(); if (channel != null) { return const stream_chat_flutter.ChannelHeader(); } return stream_chat_flutter.StreamChannel( channel: _channel, child: const stream_chat_flutter.ChannelHeader(), ); } @override Size get preferredSize => const Size.fromHeight(kToolbarHeight); } ``` ### ChannelListView To build our channel list, we will abstract the Stream Chat's **ChannelListView** with its own **ChannelsBloc**. That way, we can provide the **ChannelsBloc** to any channel we create. ```dart class ChannelListView extends StatelessWidget { const ChannelListView({ Key? key, required this.userId, required this.channelBuilder, }) : super(key: key); final String userId; final Widget Function( BuildContext context, stream_chat_flutter.Channel channel, ) channelBuilder; @override Widget build(BuildContext context) { return stream_chat_flutter.ChannelsBloc( child: stream_chat_flutter.ChannelListView( filter: stream_chat_flutter.Filter.in_('members', [userId]), onChannelTap: (channel, child) { Navigator.of(context).push( MaterialPageRoute( builder: (_) => channelBuilder(context, channel), ), ); }, ), ); } } ``` We achieve this by specifying a function that we call **channelBuilder**, which is simply a callback with the context and the channel provided by Stream Chat's **ChannelListView**. This is also how we provide the channel that will be used on the next page to display the messages. ### MessageListView Now we want to display the channel's messages. So we will do some wrapping of Stream Chat's **StreamChannel** and **MessageListView** widgets. Stay with me on this one, it's kind of tricky. ```dart class MessageListView extends StatelessWidget { const MessageListView({ Key? key, required stream_chat_flutter.Channel channel, OnGenerateAttachements? onGenerateAttachements, }) : _channel = channel, _onGenerateAttachements = onGenerateAttachements ?? const {}, super(key: key); final stream_chat_flutter.Channel _channel; final OnGenerateAttachements _onGenerateAttachements; @override Widget build(BuildContext context) { final channel = context .findAncestorStateOfType(); final customAttachmentBuilder = {}; for (final entry in _onGenerateAttachements.entries) { customAttachmentBuilder[entry.key] = (context, details, attachments) { return entry.value(context, details); }; } final messageListView = stream_chat_flutter.MessageListView( messageBuilder: (context, details, messages, widget) { return widget.copyWith( customAttachmentBuilders: customAttachmentBuilder, ); }, ); if (channel != null) return messageListView; return stream_chat_flutter.StreamChannel( channel: _channel, child: messageListView, ); } } ``` First, we receive the corresponding channel from the **ChannelListView** we made earlier, which will help us to provide a channel for the **StreamChannel** widget to populate the messages list. Since we want to add attachments such as location, we created a **typedef** called **OnGenerateAttachements** that is just a **Map** with a builder function. We populate the attachments of the messages based on what we get from the constructor. At last, we create a **MessageListView** from the Stream Chat SDK and copy the attachments to its widget. We can build our **StreamChannel** widget with the channel and our recently created message list. ### MessageInput To finish our UI package with the attachment and channel lookup logic, we need the last widget: a message input to type and send a new message to the channel. ```dart class MessageInput extends StatefulWidget { const MessageInput({ Key? key, required stream_chat_flutter.Channel channel, OnGenerateAttachementThumbnails? onGenerateAttachementThumbnails, List? actions, MessageInputController? controller, }) : _channel = channel, _onGenerateAttachementThumbnails = onGenerateAttachementThumbnails ?? const {}, _actions = actions ?? const [], _controller = controller, super(key: key); final stream_chat_flutter.Channel _channel; final OnGenerateAttachementThumbnails _onGenerateAttachementThumbnails; final List _actions; final MessageInputController? _controller; @override _MessageInputState createState() => _MessageInputState(); } class _MessageInputState extends State { final _messageInputKey = GlobalKey(); @override void initState() { super.initState(); widget._controller?._key = _messageInputKey; } @override Widget build(BuildContext context) { final channel = context .findAncestorStateOfType(); final messageInput = stream_chat_flutter.MessageInput( key: _messageInputKey, actions: widget._actions, attachmentThumbnailBuilders: widget._onGenerateAttachementThumbnails, ); if (channel != null) return messageInput; return stream_chat_flutter.StreamChannel( channel: widget._channel, child: messageInput, ); } } ``` This widget is similar to **MessageListView**. It will wrap a **MessageInput** from the Stream Chat SDK. We will also add new functionality for actions, such as a share location button and the attachment thumbnail. With that, we've finished our **chat\_ui** package. 🎉 ## Building the App ### Bootstrapping As we mentioned at the beginning, this app is created using our [Very Good CLI](https://pub.dev/packages/very_good_cli), which creates a project using the Very Good Core template. In addition to what our CLI generates, we are going to create a new file called **bootstrap.dart**. This file will be in charge of initializing all of our dependencies and inject them into the app. This helps set up specific configurations before you run the app and allows us to initialize asynchronous dependencies. We set up the Stream Chat client by providing a **chatApiKey**. We then inject the Stream Chat SDK into the **ChatRepository**. ```dart typedef AppBuilder = FutureOr Function( Widget Function(Widget) builder, ChatRepository chatRepository, ); Future bootstrap({required AppBuilder builder}) async { Bloc.observer = AppBlocObserver(); FlutterError.onError = (details) { log(details.exceptionAsString(), stackTrace: details.stack); }; const chatApiKey = String.fromEnvironment('CHAT_API_KEY'); if (chatApiKey.isEmpty) { throw StateError('missing environment variable '); } final chatClient = StreamChatClient(chatApiKey, logLevel: Level.OFF); final chatRepository = ChatRepository(chatClient: chatClient); const chatToken = String.fromEnvironment('CHAT_TOKEN'); if (chatToken.isEmpty) { throw StateError('missing environment variable '); } const userId = ''; final avatarUri = Uri.https( 'getstream.imgix.net', 'images/random_svg/FS.png', ); await chatRepository.connect( userId: userId, token: chatToken, avatarUri: avatarUri, ); await runZonedGuarded( () async => runApp(await builder( (child) => chat_ui.StreamChat( client: chatClient, child: child, ), chatRepository, )), (error, stackTrace) => log(error.toString(), stackTrace: stackTrace), ); } ``` ### App Widget ```dart class App extends StatelessWidget { const App({ Key? key, required Widget Function(Widget) builder, required ChatRepository chatRepository, }) : _builder = builder, _chatRepository = chatRepository, super(key: key); final Widget Function(Widget) _builder; final ChatRepository _chatRepository; @override Widget build(BuildContext context) { return RepositoryProvider.value( value: _chatRepository, child: MaterialApp( localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, ], supportedLocales: AppLocalizations.supportedLocales, home: const ChannelListPage(), builder: (_, child) => _builder(child!), ), ); } } ``` The **App** widget requires a widget builder and the **ChatRepository** that was provided during bootstrapping. We wrap the **MaterialApp** with a **RepositoryProvider** that injects the **ChatRepository** so we can access it from the subtree. Lastly, we'll add our home page and the **ChannelListPage**. ### Channel List At Very Good Ventures, we separate our project by feature. Our first feature will be the channel list. Here, we will obtain the **userId** from the **ChatRepository** by using a [**Cubit**](https://bloclibrary.dev/#/coreconcepts?id=cubit) from the [**bloc** package](https://pub.dev/packages/bloc). #### ChannelListState ```dart class ChannelListState extends Equatable { const ChannelListState({required this.userId}); final String userId; @override List get props => [userId]; } ``` #### ChannelListCubit ```dart class ChannelListCubit extends Cubit { ChannelListCubit({ required ChatRepository chatRepository, }) : super(ChannelListState(userId: chatRepository.getUserId())); } ``` This **ChannelListCubit** will maintain the **userId** as part of its state, which we can provide to other widgets from the **chat\_ui** package. #### ChannelListPage **ChannelListPage** is composed of two widgets: - **ChannelListPage** handles the page's general structure and injects the **ChannelListCubit** into the widget tree. ```dart class ChannelListPage extends StatelessWidget { const ChannelListPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final l10n = context.l10n; return Scaffold( appBar: AppBar(title: Text(l10n.channelListAppBarTitle)), body: BlocProvider( create: (context) => ChannelListCubit( chatRepository: context.read(), ), child: const ChannelListView(), ), ); } } ``` - **ChannelListView** accesses the **userId** from the **ChannelListCubit** and provides it to the custom **ChannelListView** widget. ```dart class ChannelListView extends StatelessWidget { const ChannelListView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final userId = context.select( (ChannelListCubit cubit) => cubit.state.userId, ); return chat_ui.ChannelListView( userId: userId, channelBuilder: (_, channel) => MessageListPage(channel: channel), ); } } ``` As you can see, on the **ChannelListView** from **chat\_ui**, we use the **channelBuilder** property to provide the channel to the **MessageListPage**. That new page will be responsible for displaying the list of messages from the specified channel. ### Message List #### MessageListState The **MessageListCubit** will manage the selected channel, as well as the user's current location. We will create the **CurrentLocation** class first: ```dart enum CurrentLocationStatus { unavailable, available, pending } class CurrentLocation extends Equatable { const CurrentLocation({ this.latitude = 0.0, this.longtitude = 0.0, this.status = CurrentLocationStatus.pending, }); final double latitude; final double longtitude; final CurrentLocationStatus status; @override List get props => [latitude, longtitude, status]; CurrentLocation copyWith({ double? latitude, double? longtitude, CurrentLocationStatus? status, }) { return CurrentLocation( latitude: latitude ?? this.latitude, longtitude: longtitude ?? this.longtitude, status: status ?? this.status, ); } } ``` Then, we can add it to the state: ```dart class MessageListState extends Equatable { const MessageListState({ required this.channel, this.location = const CurrentLocation(), }); final Channel channel; final CurrentLocation location; @override List get props => [channel, location]; MessageListState copyWith({Channel? channel, CurrentLocation? location}) { return MessageListState( channel: channel ?? this.channel, location: location ?? this.location, ); } } ``` #### MessageListCubit Our new **Cubit** will have a dependency on the channel from the UI layer and the **ChatRepository**. We will expose a **locationRequested** method to get the current location of the device. ```dart class MessageListCubit extends Cubit { MessageListCubit({ required Channel channel, required ChatRepository chatRepository, }) : _chatRepository = chatRepository, super(MessageListState(channel: channel)); final ChatRepository _chatRepository; void locationRequested() async { emit( state.copyWith( location: state.location.copyWith( status: CurrentLocationStatus.pending, ), ), ); try { final location = await _chatRepository.getCurrentLocation(); emit( state.copyWith( location: state.location.copyWith( status: CurrentLocationStatus.available, latitude: location.latitude, longtitude: location.longitude, ), ), ); } catch (_) { emit( state.copyWith( location: state.location.copyWith( status: CurrentLocationStatus.unavailable, ), ), ); } } } ``` #### MessageListPage The **ChannelListPage** is composed of a few widgets: - **MessageListPage** handles the page's general structure and injects the **MessageListCubit** into the widget tree. ```dart class MessageListPage extends StatelessWidget { const MessageListPage({Key? key, required Channel channel}) : _channel = channel, super(key: key); final Channel _channel; @override Widget build(BuildContext context) { return Scaffold( appBar: ChannelAppBar(channel: _channel), body: BlocProvider( create: (context) => MessageListCubit( channel: _channel, chatRepository: context.read(), ), child: const MessageListView(), ), ); } } ``` - **MessageListView** consumes the **MessageListCubit** by selecting the **channel** it contains and providing it to the custom **MessageListView** widget. This widget also uses a **BlocListener** and reacts to the **MessageListCubit** by either showing a **SnackBar** when location is not available or adding a location attachment when location is shared. - We use the **MessageInputController** from the **chat\_ui** package for the send functionality. ```dart class MessageListView extends StatefulWidget { const MessageListView({Key? key}) : super(key: key); @override _MessageListViewState createState() => _MessageListViewState(); } class _MessageListViewState extends State { late final chat_ui.MessageInputController _controller; @override void initState() { super.initState(); _controller = chat_ui.MessageInputController(); } @override Widget build(BuildContext context) { final channel = context.select( (MessageListCubit cubit) => cubit.state.channel, ); return BlocListener( listenWhen: (prev, curr) => prev.location.status != curr.location.status, listener: (context, state) { if (state.location.status == CurrentLocationStatus.unavailable) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( '''We can't access your location at this time. Did you allow location access?''', ), ), ); } if (state.location.status == CurrentLocationStatus.available) { _controller.addAttachment( Attachment( type: 'location', uploadState: const UploadState.success(), extraData: { 'latitude': state.location.latitude, 'longitude': state.location.longtitude, }, ), ); } }, child: Column( children: [ Expanded(child: chat_ui.MessageListView(channel: channel)), chat_ui.MessageInput( controller: _controller, channel: channel, onGenerateAttachementThumbnails: { 'location': (context, attachment) { return MapThumbnailImage( latitude: attachment.extraData['latitude'] as double, longitude: attachment.extraData['longitude'] as double, ); }, }, actions: [ IconButton( icon: const Icon(Icons.location_history), onPressed: () { context.read().locationRequested(); }, ), ], ), ], ), ); } } ``` - **MapThumbnailImage** extracts the map thumbnail image for the shared location message. This will be added to the channel as an attachment message each time the user shares their location. ```dart class MapThumbnailImage extends StatelessWidget { const MapThumbnailImage({ Key? key, required this.latitude, required this.longitude, }) : super(key: key); final double latitude; final double longitude; Uri get _thumbnailUri { return Uri( scheme: 'https', host: 'maps.googleapis.com', port: 443, path: '/maps/api/staticmap', queryParameters: { 'center': '$latitude,$longitude', 'zoom': '18', 'size': '700x500', 'maptype': 'roadmap', 'key': '', 'markers': 'color:red|$latitude,$longitude' }, ); } @override Widget build(BuildContext context) { return Image.network( '$_thumbnailUri', height: 300.0, width: 600.0, fit: BoxFit.fill, ); } } ``` With these widgets, we can build a whole chat user interface to manage conversations (also called channels). ## Testing [Testing](https://verygood.ventures/blog/guide-to-flutter-testing/) is an essential part of a successful and well-built app for us at VGV. Tests make it easy to know which parts of our code work properly. When it comes to changing existing functionality, tests can tell us if everything is still working as expected. We also have a very good playlist about [testing fundamentals](https://verygood.ventures/blog/testing-fundamentals-of-flutter-course/) by our teammate Jorge, so go check it out after reading this! The chat application we just built has 100% code coverage, which means every part of it has at least one test written for it. We will go through the most notable ones so that you can take a look at how to write them. There are three types of tests in our application: unit (isolated components), widget (UI components), and bloc (business logic components). We will go through an example of each one. ### Unit Testing ChatRepository Unit testing aims to test the smallest pieces of code. Luckily Dart makes it a breeze to test these components with its [**test**](https://pub.dev/packages/test) package. When we write a test, we first need to mock the dependencies of the component we are testing, if it has any. In this case, we're testing if the **ChatRepository** connects the user correctly. Our repository has a dependency on a **StreamChatClient** for this method, so we need to mock it. ```dart import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; class MockStreamChatClient extends Mock implements StreamChatClient {} ``` We create a Mock class using the [**mocktail**](https://pub.dev/packages/mocktail) package by extending the Mock class and implementing the class we want to mock (in this case, **StreamChatClient**). We can instantiate our chat client as a **MockStreamChatClient** and pass it to the **ChatRepository**. ```dart group('ChatRepository', () { late StreamChatClient chatClient; late ChatRepository chatRepository; setUpAll(() { registerFallbackValue(FakeUser()); }); setUp(() { chatClient = MockStreamChatClient(); chatRepository = ChatRepository(chatClient: chatClient); }); } ``` Now, we can stub the behavior we want for this dependency. For the connection functionality, we want to return a fake value that won't be stubbed, so we can create a **FakeOwnUser** class to return when the connect user method is called: ```dart class FakeOwnUser extends Fake implements OwnUser {} ``` We use the **when** method to stub a fake user when the method is called with any kind of argument. Then, we ensure the method completes normally by using the **expect** method with the **completes** matcher, as it is a void function and won't return anything. Lastly, we use the **verify** method to make sure our dependency was called only once with the correct arguments. ```dart group('connect', () { const userId = 'test-user-id'; const token = 'test-token'; final avatarUri = Uri.https('test.com', 'profile/pic.png'); test('connects with the provided userId, token, and avatarUri', () { when( () => chatClient.connectUser(any(), any()), ).thenAnswer((_) async => FakeOwnUser()); expect( chatRepository.connect( userId: userId, token: token, avatarUri: avatarUri, ), completes, ); verify( () => chatClient.connectUser( User(id: userId, extraData: {'image': '$avatarUri'}), token, ), ).called(1); }); }); ``` ### Widget Testing ChannelListView Our presentation, or graphics components, should be tested too! Luckily, Flutter provides a complete framework to test this components easy with [**flutter_test**](https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html). Just as before, we must mock the dependencies of our widgets. For a **ChannelListView**, the dependencies are the **StreamChatClient** and the current **Channel**. ```dart import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart' as stream_chat_flutter; class MockStreamChatClient extends Mock implements stream_chat_flutter.StreamChatClient {} class MockChannel extends Mock implements stream_chat_flutter.Channel {} void main() { group('ChannelListView', () { late stream_chat_flutter.StreamChatClient client; setUp(() { client = MockStreamChatClient(); when( () => client.on(any(), any(), any(), any()), ).thenAnswer((_) => const Stream.empty()); when( () => client.wsConnectionStatus, ).thenReturn(stream_chat_flutter.ConnectionStatus.connected); }); }); } ``` After we mocked and stubbed our dependencies, we can test our widgets. ```dart testWidgets( 'renders ChannelListView with correct configuration', (tester) async { const userId = 'test-user-id'; await tester.pumpWidget( MaterialApp( home: stream_chat_flutter.StreamChat( client: client, child: Material( child: ChannelListView( userId: userId, channelBuilder: (context, channel) => const SizedBox(), ), ), ), ), ); expect(find.byType(stream_chat_flutter.ChannelsBloc), findsOneWidget); final channelsListViewFinder = find.byType(stream_chat_flutter.ChannelListView); final channelsListView = tester .widget(channelsListViewFinder); final expectedFilter = stream_chat_flutter.Filter.in_( 'members', const [userId], ); expect(channelsListView.filter, equals(expectedFilter)); }, ); ``` A couple of things to note here: - We first use the **WidgetTester** provided by the **testWidgets** function, which allows us to build and interact with widgets by rendering them using the **pumpWidget** method. - After we render the widget we're testing, we use the **expect** method with a finder provided by the **flutter\_test** package. This is called **find.byType** which checks if one or more widgets of the specified type are found. We expect only one **ChannelsBloc**, so we use the **findsOneWidget** matcher. - We create a type finder for **ChannelListView** and use it to get the mentioned widget. - We create the expected filter for our **ChannelListView** and ensure it is rendered correctly. ### Bloc Testing MessageListCubit We didn't forget about testing the business logic of the application. These components usually are harder to test, but don't worry: *there's a package for that*. [**bloc_test**](https://pub.dev/packages/bloc_test) is part of the [bloc library](https://bloclibrary.dev/), and helps us a lot to write clear, simple bloc tests to get faster to 100% code coverage. As always, we start with dependencies: ```dart class MockChannel extends Mock implements Channel {} class MockChatRepository extends Mock implements ChatRepository {} void main() { group('MessageListCubit', () { late Channel channel; late ChatRepository chatRepository; setUp(() { channel = MockChannel(); chatRepository = MockChatRepository(); }); }); } ``` After that, we can write our bloc test. We will test the "happy path" of the **locationRequested** method. A bloc test helps us **build** our bloc with its dependencies, **act** by adding an event (bloc) or calling a method (cubit) that would emit some state, and **expect** a list of states that are going to be emitted. *To learn more about* ***bloc\_test****, check the package documentation.* ```dart const latitude = 42.0; const longitude = 13.37; const coordinatePair = CoordinatePair( latitude: latitude, longitude: longitude, ); blocTest( 'emits [pending, available] when location is available', build: () { when( () => chatRepository.getCurrentLocation(), ).thenAnswer((_) async => coordinatePair); return MessageListCubit( channel: channel, chatRepository: chatRepository, ); }, act: (cubit) => cubit.locationRequested(), expect: () => [ MessageListState(channel: channel), MessageListState( channel: channel, location: const CurrentLocation( latitude: latitude, longtitude: longitude, status: CurrentLocationStatus.available, ), ), ], ); ``` Here, we stub our dependency and build the cubit with it. Then, call the method that will cause a new list of states to be emitted. And at last, specify the states we expect with their properties. And there you go, a test for each layer of our app! 🧪 ## Wrap Up We finished building our chat application with location-sharing functionality! 🎉 This example was created using the [Stream Chat SDK](https://getstream.io/chat/sdk/flutter/) and [Very Good CLI](https://pub.dev/packages/very_good_cli) with our opinionated practices. You can take a look at the [full source code here](https://github.com/VGVentures/chat_location). Hope you enjoyed going through this tutorial and we can't wait to see what you build next with these two packages together. *Felix Angelov contributed to this article.* ### What's New in Bloc v7.2.0 Source: https://verygood.ventures/blog/whats-new-in-bloc-v7-2-0/ Bloc v7.2.0 has been released! It brings an API update and a handful of new features to simplify event transformations. Here’s what’s new in the bloc library, how to migrate, and what’s coming next. ## Why Bloc v7.2.0? We had been wrestling with an [issue in Dart](https://github.com/dart-lang/sdk/issues/44616) for a while that impacted the predictability of state changes when using bloc. We were waiting to see if this would be addressed in the Dart SDK, but because it would require changes to asynchronous constructs including **yield\*** (**yield-each**), it would be risky and unlikely to be addressed in the near future. Because predictability is at the core of the bloc library, we started to experiment with alternative approaches. We knew that in order to address the predictability issue, we had to move away from async generators and introduce an API which gave us granular control over when state changes occur. In the current version, **mapEventToState** returns a **Stream** which lends itself to relying on async generators (**async\***) in order to react to events and emit states. The new **on** API introduced in v7.2.0 makes it easier and safer to implement complex state changes while eliminating uncertainty. Before implementing any major updates, we wanted to get input from the bloc community. The [initial proposal](https://github.com/felangel/bloc/issues/2526) surfaced some interesting conversations. For example, multiple people expressed concerns about introducing a breaking change without having an easy way to migrate. Instead of making a migration tool, we decided it would be best if the changes were backwards compatible. This way people could gradually migrate one bloc at a time without a looming deadline on the horizon. Shoutout to [Marcus Twichel](https://github.com/felangel/bloc/issues/2526#issuecomment-861231909), [Dominik Roszkowski](https://github.com/felangel/bloc/issues/2526#issuecomment-861430393), [Sam Moore](https://github.com/felangel/bloc/issues/2526#issuecomment-862572745), [Christopher Casey](https://github.com/felangel/bloc/issues/2526#issuecomment-861582470), and many others for their feedback. As a byproduct of the new API, we were able to simplify the public API, align more closely with **Cubit**, and introduce a simpler way to manage concurrency when using bloc. ## Bloc v7.2.0 updates ### ✨ Introduce new on API The most significant change in v7.2.0 is **mapEventToState** is deprecated and **on** should be used instead. In v7.1.0: ```dart abstract class CounterEvent {} class Increment extends CounterEvent {} class CounterBloc extends Bloc { CounterBloc() : super(0); @override Stream mapEventToState(CounterEvent event) async* { if (event is Increment) { yield state + 1; } } } ``` In v7.2.0: ```dart abstract class CounterEvent {} class Increment extends CounterEvent {} class CounterBloc extends Bloc { CounterBloc() : super(0) { on((event, emit) => emit(state + 1)); } } ``` In practice your existing blocs might look something like: ```dart @override Stream mapEventToState(Event event) async* { if (event is EventA) { yield* _mapEventAToState(event); } else if (event is EventB) { yield* _mapEventBToState(event); } } Stream _mapEventAToState(EventA event) async* {...} Stream _mapEventBToState(EventB event) async* {...} ``` Most of the important logic usually lives inside other functions (e.g. **\_mapEventAToState** and **\_mapEventBToState**), leaving **mapEventToState** to handle which mapper to call based on the event type. Furthermore, using async generators (**async\***, **yield**, **yield\***) can introduce complexity and undesired behavior in some cases. In v7.2.0 the above bloc would look something like: ```dart class SomeBloc extends Bloc { SomeBloc() : super(0) { on(_onEventA); on(_onEventB); } void _onEventA(EventA event, Emitter emit) {...} void _onEventB(EventB event, Emitter emit) {...} } ``` You can register event handlers by calling **on** where **Event** is the type of event being handled. As seen above, the **on** API provides a callback **(Event event, Emitter) {...}** which is invoked when the particular event is added to the bloc. You are then able to **emit** zero or more states using the **Emitter** provided by the API. Calling **emit(...)** updates the bloc's state synchronously. ### ✨ Introduce new EventTransformer API In addition to **on**, **transformEvents** is deprecated and the new **EventTransformer** API should be used instead. Previously, if you wanted to provide a custom event transformer per event you would need to introduce further complexity within the **transformEvents** function. The **on** API allows you to provide a custom event transformer per event handler in a more streamlined and developer-friendly way. In v7.1.0: ```dart @override Stream> transformEvents(events, transitionFn) { return events .debounceTime(const Duration(milliseconds: 300)) .flatMap(transitionFn); } ``` In v7.2.0: ```dart EventTransformer debounce(Duration duration) { return (events, mapper) => events.debounceTime(duration).flatMap(mapper); } MyBloc() : super(MyState()) { on(_onEvent, transformer: debounce(const Duration(milliseconds: 300))); } ``` As seen above, a custom **EventTransformer** is responsible for taking the incoming stream of events along with an **EventMapper** (your event handler) and returning a new stream of events. ### 📦 New package: bloc\_concurrency The new package [**bloc_concurrency**](https://pub.dev/packages/bloc_concurrency) comes with an opinionated set of custom event transformers. Out of the box, it includes: - **concurrent** - process events concurrently - **sequential** - process events sequentially - **droppable** - ignore any events added while an event is processing - **restartable** - process only the latest event and cancel previous event handlers ![bloc_concurrency event transformers: concurrent, sequential, droppable, restartable](/assets/images/blog/whats-new-in-bloc-v7-2-0/body-0.png) Using the custom event transformers is as easy as: ```dart import 'package:bloc/bloc.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; abstract class CounterEvent {} class Increment extends CounterEvent {} class CounterBloc extends Bloc { CounterBloc() : super(0) { on( (event, emit) async { await Future.delayed(Duration(seconds: 1)); emit(state + 1); }, /// Specify a custom event transformer from `package:bloc_concurrency` transformer: sequential(), ); } } ``` ### ⚠️ Deprecate transformTransitions API The **transformTransitions** API is deprecated and the state **stream** should be used instead. In v7.1.0: ```dart @overrideStream> transformTransitions( Stream> transitions,) { return transitions.debounceTime(const Duration(milliseconds: 42));} ``` In v7.2.0: ```dart @overrideStream get stream => super.stream.debounceTime(const Duration(milliseconds: 42)); ``` ## Migration strategy Migrating to the latest version of bloc is quite simple since version v7.2.0 is backwards compatible. Using the new **on** API, you can migrate one bloc at a time. Changes to the bloc will not impact UI components or existing tests. Once the new API changes are complete, you can simply rerun your test suite to ensure your bloc migration was successful. Additionally, the backwards compatibility of bloc v7.2.0 means you can run your application even when some blocs are still using the previous **mapEventToState** API and others are using the updated **on** API. This feature allows for incremental development while maintaining a fully functioning application during your migration to the new bloc API. Once all of the blocs in your application are transitioned to the new **on** API, you are ready to upgrade your **pubspec.yaml** file to bloc v8.0.0 (coming soon). In v8.0.0, deprecated APIs, like **mapEventToState**, will no longer be supported. Consequently, it is important to make sure your project is fully migrated before moving to bloc v8.0.0. For further information on bloc migration, refer to our [migration guide](https://bloclibrary.dev/#/migration). ## Coming soon: bloc v8.0.0 [All bloc tutorials](https://bloclibrary.dev/) and [example applications](https://github.com/felangel/bloc/tree/master/examples) have already been migrated to the new APIs and new releases for developer tooling (VSCode and IntelliJ extensions) are available so you can get ahead of the upcoming major release. Speaking of v8.0.0, we're planning to release a developer preview in the coming weeks with a stable v8.0.0 release sometime in October 🤞. Development for v8.0.0 is already well underway but as it is a major release, we want to make sure we're covering all our bases across the entire bloc ecosystem. If you have any feature requests, questions, or feedback now is a great time to [open an issue on GitHub](https://github.com/felangel/bloc/issues/new/choose). ### [**Read more about why we use flutter_bloc for state management.**](https://verygood.ventures/blog/why-we-use-flutter-bloc/) *Felix Angelov contributed to this article.* ### My internship at Very Good Ventures: Dennis Source: https://verygood.ventures/blog/dennis-internship-experience/ This past summer I was given the opportunity to work at Very Good Ventures as an Engineering Intern where I was able to dive headfirst into Flutter app development. During my three months at VGV, I refined my programming skills working on open source tooling used both internally by VGV and by the Dart and Flutter community. I learned a lot about writing flexible, easily testable code and the importance of well organized project architecture. Here are some highlights from my internship. From day one, it became evident that at Very Good Ventures, quality work was a priority. Rather than being thrown headfirst into a project to figure things out on my own, I was given a very thorough week of onboarding where I learned all about the fundamentals of Flutter and the company standards for app architecture. This onboarding experience involved working through several well-documented tutorials as well as meeting with various team members. This extra effort put into onboarding made transitioning into open source work fluid and less stressful. Once I finished onboarding, I started off my internship working on various open source projects with other VGV interns. From there, my focus transitioned to [Very Good CLI](https://pub.dev/packages/very_good_cli), where I worked on expanding the functionality of the create command. The CLI uses Mason internally to generate projects from various “brick” templates offered ([read about Mason here](https://github.com/felangel/mason)). My contributions to the CLI involved adding support for a [custom organization name](https://github.com/VeryGoodOpenSource/very_good_cli/pull/148), expanding the [templates supported by the CLI](https://github.com/VeryGoodOpenSource/very_good_cli/pull/154), and adding an [auto-update feature](https://github.com/VeryGoodOpenSource/very_good_cli/pull/181) whenever the CLI gets run. In addition to working on the Very Good CLI, I also contributed to a new feature on [Very Good Start](https://verygood.ventures/whitepapers/ebook-introducing-very-good-start/), VGV’s enterprise-grade Flutter starter app. ![very_good_cli v0 4 0](/assets/images/blog/dennis-internship-experience/body-0.png) *One of the updates to Very Good CLI to support plugin template creation* Prior to interning for Very Good Ventures, I interned at BMW where I worked on automating cloud resource deployment using tools like Terraform and Kubernetes. Coming from this background in cloud infrastructure, I felt the transition to using Flutter and having more of a focus on app development was refreshing. I personally enjoyed the extra room for creativity and the satisfaction of being able to see my contributions directly in open source libraries. All in all, my three months at VGV gave me a strong foundation in Flutter app development and helped me hone my overall programming skills. I’m glad I was given the opportunity to be a part of the amazing team at VGV and plan on continuing to work in the Flutter and Dart ecosystem. ## [**Read about what it's like to work at VGV from other interns.**](https://verygood.ventures/blog/intern-experiences-summer-2021/) ### Generate Flutter plugins with Very Good CLI Source: https://verygood.ventures/blog/generate-flutter-plugins-with-very-good-cli/ [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli) is an open source Command-Line Interface for Dart which simplifies starting new projects. Very Good CLI offers starter templates for various project formats including generic Flutter apps, Flutter packages, Dart packages, and now, Flutter plugins. The generated Flutter plugin comes with comprehensive platform support (Windows, Android, iOS, Linux, macOS, web), and each platform has its own dedicated package, unlike the [plugin template offered by the Flutter CLI](https://github.com/flutter/flutter/issues/43284), which wraps all of the platforms under one package. Since a [federated plugin architecture](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#federated-plugins) is used for Very Good CLI rather than a single-package approach, each package can be developed, maintained, and tested separately. This means that developers don't need familiarity with all of the supported platforms to contribute to the plugin. ## Getting Started To generate a new Flutter plugin with Very Good CLI, just activate the [very_good_cli package from pub.dev](https://pub.dev/packages/very_good_cli). ```shell dart pub global activate very_good_cli ``` Then, run the create command in a new project directory with the **\-t flutter\_plugin** flag. ```shell very_good create my_plugin -t flutter_plugin✓ Generated 105 file(s) (0.3ms)✓ Running "flutter packages get" in my_plugin (8.1ms)Created a Very Good Flutter plugin! 🦄 ``` Now that you have created your plugin, you're ready to start developing! Happy coding! ### [**Check out Very Good Core, our open source Flutter starter application with VGV-opinionated architecture.**](https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/) ### Mocking Flutter routes with Mockingjay Source: https://verygood.ventures/blog/mocking-flutter-routes-with-mockingjay/ Testing your application's code is important. At [Very Good Ventures](https://verygood.ventures/), this sentiment has been with us from the very beginning, and we have explained our [approach to testing multiple times](https://verygood.ventures/blog/guide-to-flutter-testing/). We use a myriad of metrics and methods to give us a level of confidence that the code we write works the way we want to -- not just now, but especially when we need to go back and change bits of a particular feature in the future. We want to be sure that we catch bugs that we could have prevented well in advance, before they hit the app stores or, better yet, before they are even merged into our codebase. Throughout the years, we have established and battle-tested methods of testing multiple aspects of Flutter development; we use [bloc_test](https://pub.dev/packages/bloc_test) to assert the logic in our blocs works as expected, and the [mocktail](https://pub.dev/packages/mocktail) package helps us mock anything related to APIs, repositories and data models. One facet that has been proving more difficult to test than others for a long time, however, has been navigation routes. We want to be sure that the buttons and logic in our app route to the correct screens and dialogs -- in fact, our 100% code coverage requirement forces us to test every executable line in our codebase, including navigation calls. However, when testing navigation routes, some interesting issues can occur. **Great news! We have built the newly released 🕊** [**Mockingjay**](https://pub.dev/packages/mockingjay) **package to help with exactly that. In this article, we go over the issues we've found when testing routes in Flutter apps, and how Mockingjay helps us more easily test and verify any routing logic.** Note, this article assumes you already know the basics of the bloc state management patterns and Flutter testing. If you want to catch up on these topics, we recommend having a look at these articles: [Flutter Testing: A Very Good Guide](https://verygood.ventures/blog/guide-to-flutter-testing/) and [Why We Use flutter_bloc for State Management](https://verygood.ventures/blog/why-we-use-flutter-bloc/). ## Testing routes today For us, testing UI in a Flutter app comes with multiple goals in mind. Among other things, a widget test should be; - **atomic**; it should only test the part of the application the test is focussed on, and mock any external dependencies. - **exhaustive**; we should easily be able to test all parts of the UI, including which widgets are rendered for a given state and what actions occur when pressing any button. - **repeatable**; we want to have a consistent approach for how we build both the UI itself as well as the tests we write for it. ### An example As mentioned before, most aspects of widget tests already have well-established patterns we follow. For example, take a chat application that has a page with a list of friends that you can chat with. This chat overview page might have a button at the top right that will take you to your profile settings. Using the blocs and repositories pattern, as we do for every project, the code for this ChatOverviewPage might look something like this. ```dart class ChatOverviewPage extends StatelessWidget { const ChatOverviewPage({Key? key}) : super(key: key); static Route route() { return MaterialPageRoute( builder: (context) { return BlocProvider( create: (context) => ChatBloc( chatRepository: context.read(), ), child: const ChatOverviewPage(), ); }, ); } @override Widget build(BuildContext context) { final state = context.watch().state; return Scaffold( appBar: AppBar( title: const Text('Chats'), actions: [ IconButton( onPressed: () { Navigator.of(context).push(UserProfileSettingsPage.route()); }, icon: const Icon(Icons.settings), ), ], ), body: ListView( children: [ for (final conversation in state.conversations) ChatConversationListTile( conversation: conversation, ), ], ), ); } } ``` There are a few things to note about this screen. As you can tell, the ChatOverviewPage has a single dependency on a ChatBloc, which in turn depends on a ChatRepository. This is a common pattern we see throughout our codebases and helps create applications that can scale to virtually any size and complexity. This means that, in our tests, we can simply mock the ChatRepository when testing the route() method. For the rest of the UI, all we have to do is provide a mock instance of a ChatBloc. With that, all dependencies on this screen are mocked, and every aspect of the screen can be tested! Additionally, a button is displayed in the AppBar that routes to a UserProfileSettingsPage. Let's have a look at what its source code might look like. ```dart class UserProfileSettingsPage extends StatelessWidget { const UserProfileSettingsPage({Key? key}) : super(key: key); static Route route() { return MaterialPageRoute( builder: (context) { return BlocProvider( create: (context) => UserSettingsBloc( userSettingsRepository: context.read(), ), child: const UserProfileSettingsPage(), ); }, ); } @override Widget build(BuildContext context) { final state = context.watch().state; return Scaffold( appBar: AppBar( title: const Text('Profile Settings'), ), body: ListView( children: [ ListTile( title: const Text('Status'), subtitle: Text(state.settings.isOnline ? 'Online' : 'Offline'), ), ListTile( title: const Text('Phone number'), subtitle: Text(state.settings.phoneNumber), ), ListTile( title: const Text('Email address'), subtitle: Text(state.settings.emailAddress), ), ], ), ); } } ``` Similar to the ChatOverviewPage, this UserProfileSettingsPage has a single dependency on a UserSettingsBloc, which in turn depends on a UserSettingsRepository. The testing setup for this screen will be almost identical to the one for ChatOverviewPage; a mock can be made for both the repository and the bloc, and we can test the route() and build() methods respectively. ### Let's get testing Let's have a look at what our test file for the ChatOverviewPage would look like, starting with some general setup. We first declare some mocks for our repository and bloc, create some fake data to drive the UI, and make sure this data is visible to the UI whenever it tries to fetch the nearest bloc. ```dart // import '...'; import 'package:mocktail/mocktail.dart'; class MockChatRepository extends Mock implements ChatRepository {} class MockChatBloc extends MockBloc implements ChatBloc {} void main() { group('ChatOverviewPage', () { late ChatRepository chatRepository; late ChatBloc chatBloc; const conversations = [ Conversation( chatName: 'mock-conversation-1', unreadMessages: 0, ), Conversation( chatName: 'mock-conversation-2', unreadMessages: 5, ), Conversation( chatName: 'mock-conversation-3', unreadMessages: 10, ), ]; setUpAll(() { registerFallbackValue(const ChatState()); registerFallbackValue(const ChatEvent()); }); setUp(() { chatRepository = MockChatRepository(); when(() => chatRepository.getConversations()).thenAnswer((_) async => []); chatBloc = MockChatBloc(); when(() => chatBloc.state).thenReturn(const ChatState( conversations: conversations, )); }); // ... }); // ... } ``` Now that we're all set up, let's create a simple test for the route() method. ```dart testWidgets('route method creates route normally', (tester) async { await tester.pumpWidget( RepositoryProvider.value( value: chatRepository, child: MaterialApp( onGenerateRoute: (_) => ChatOverviewPage.route(), ), ), ); expect(find.byType(ChatOverviewPage), findsOneWidget); expect(tester.takeException(), isNull); }); ``` Looks great! A mock instance of the ChatRepository is provided, and some clever usage of the MaterialApp's internal Navigator allows us to call the route() method to make sure it functions as intended. A ChatOverviewPage is rendered, and no exceptions occurred during the creation of the ChatBloc. Next up is the list of ChatConversationListTiles present in the actual view, one of which should be rendered for each conversation. Since we've already made sure that the ChatBloc can be successfully constructed by testing the route() method, we can simply provide a mock ChatBloc for all our remaining tests. ```dart testWidgets( 'renders a ChatConversationListTile for each conversation', (tester) async { await tester.pumpWidget( BlocProvider.value( value: chatBloc, child: const MaterialApp( home: ChatOverviewPage(), ), ), ); expect(find.byType(ChatConversationListTile), findsNWidgets(3)); }, ); ``` Simple, yet effective. In our setup, we made sure the chatBloc would return a ChatState with three Conversation objects, so that is what we expect to find in the UI when counting the number ChatConversationListTiles rendered. Now, let's test the profile settings button to make sure it routes correctly. ```dart testWidgets( 'routes to UserProfileSettingsPage when settings button is pressed', (tester) async { await tester.pumpWidget( BlocProvider.value( value: chatBloc, child: const MaterialApp( home: ChatOverviewPage(), ), ), ); await tester.tap(find.byIcon(Icons.settings)); await tester.pumpAndSettle(); expect(find.byType(UserProfileSettingsPage), findsOneWidget); }, ); ``` Nice! Let's give it a spin and... ```bash ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ The following ProviderNotFoundException was thrown building UserProfileSettingsPage(dirty, dependencies: [_InheritedProviderScope]): Error: Could not find the correct Provider=> above this _InheritedProviderScope Widget This happens because you used a `BuildContext` that does not include the provider of your choice. ``` Oh boy, that's a problem. ### Why this approach fails Perhaps you've already figured out what the problem is in our last test. As mentioned before, our ChatOverviewPage depends on a ChatBloc and ChatRepository, which we've successfully mocked as shown in our previous tests. However, in writing our tests, we forgot the fact that the UserProfileSettingsPage also has two dependencies, those being the UserSettingsBloc and UserSettingsRepository. In our last test, we press the button that executes the UserProfileSettingsPage.route() method. This method requires a UserSettingsRepository to construct the UserSettingsBloc. This means that, **only for this specific test**, we would need to provide a mock UserSettingsRepository in addition to the chat-related dependencies. That would make the test look something like this: ```dart // ... class MockUserSettingsRepository extends Mock implements UserSettingsRepository {} void main() { group('ChatOverviewPage', () { // ... late UserSettingsRepository userSettingsRepository; // ... setUp(() { // ... userSettingsRepository = MockUserSettingsRepository(); when(() => userSettingsRepository.getSettings()) .thenAnswer((_) async => const Settings()); }); // ... testWidgets( 'routes to UserProfileSettingsPage when settings button is pressed', (tester) async { await tester.pumpWidget( BlocProvider.value( value: chatBloc, child: RepositoryProvider.value( value: userSettingsRepository, child: const MaterialApp( home: ChatOverviewPage(), ), ), ), ); await tester.tap(find.byIcon(Icons.settings)); await tester.pumpAndSettle(); expect(find.byType(UserProfileSettingsPage), findsOneWidget); }, ); }); } ``` ### Why this is a bad solution Though the test above works properly, I think it goes without saying it's not the ideal approach for multiple reasons. 1. The test dedicated to verifying the functionality of the ChatOverviewPage is no longer atomic since we have brought dependencies related to the UserProfileSettingsPage over to this file. This increases the overall *noise* in the file, increases the line count, and gives the testing logic new responsibilities it shouldn't receive. 2. Though this testing approach is repeatable in some sense, the simplicity has decreased significantly since it forces us to add all the aforementioned boilerplate to **every test** related to UI that can route to the UserProfileSettingsPage. In other words, if three pages can route to that screen, we need to duplicate this logic in three different tests in addition to the file for the UserProfileSettingsPage test itself, increasing the amount of noise in our tests every time. If only there were a way to prevent all this... ## Mockingjay to the rescue! 🎉 Introducing 🕊 [Mockingjay](https://pub.dev/packages/mockingjay), a package from [Very Good Ventures](https://verygood.ventures/) that helps you test any kind of routing in your Flutter app. This newly released and fully null-safe package (which is completely [open source](https://github.com/VeryGoodOpenSource/mockingjay)) uses [mocktail](https://pub.dev/packages/mocktail) to allow you to easily mock a Navigator in the same way any model or bloc can be mocked, making for a significantly cleaner and simpler testing approach. One of the best parts is that nothing about your UI code needs to change; it can be used as a dev dependency and is only relevant to your testing files. ### Let's try it out Out of the box, Mockingjay gives you a MockNavigator class. This is a subclass of NavigatorState that gives you the ability to mock and verify any method you would normally call using Navigator.of(context), such as push, pop, and replace. That allows us to completely omit adding extra setup and logic to mock dependencies that aren't related to the bit of UI we're testing. Let's take our previous tests and make a few additions to the setup. Mockingjay includes the mocktail package, so we can simply replace the import statement, making our tests a little bit smaller. Additionally, we can create a mock navigator instance and give it some default behavior. ```dart // import '...'; import 'package:mockingjay/mockingjay.dart'; // ... void main() { group('ChatOverviewPage', () { late ChatRepository chatRepository; late ChatBloc chatBloc; late MockNavigator navigator; // ... setUp(() { // ... navigator = MockNavigator(); // When calling `Navigator.of(context).push(...)`, return void by default. when(() => navigator.push(any())).thenAnswer((_) async {}); }); // ... }); // ... } ``` Now that we're all set up, we can use the MockNavigatorProvider in our tests to provide this mock navigator to any part of the widget tree, allowing us to mock any navigation call. **Make sure your MockNavigatorProvider is declared below any MaterialApp or Navigator widget** to make sure the UI finds the MockNavigator instead of a real NavigatorState. ```dart testWidgets( 'routes to UserProfileSettingsPage when settings button is pressed', (tester) async { await tester.pumpWidget( BlocProvider.value( value: chatBloc, child: MaterialApp( // Provide the mock navigator to any widget below // this point in the widget tree. home: MockNavigatorProvider( navigator: navigator, child: const ChatOverviewPage(), ), ), ), ); await tester.tap(find.byIcon(Icons.settings)); // Verify a new route was pushed once. verify(() => navigator.push(any())).called(1); }, ); ``` Beautiful! Our test is a lot more concise and no longer contains dependencies that are unrelated to the ChatOverviewPage. ### But wait, there's more! As you might have figured out already, we now have full control over mocking and verifying the exact types of navigation calls within our app. This means we can be very specific about what we want to return when any method is called on the mock navigator. This gives us a lot more advantages than might appear at face value. Mockingjay comes with a matcher called isRoute that makes it easy to verify what kind of route was performed, including the return type and even the name it should match. Say your app contains a dialog box that asks your user if they prefer pizza or hamburgers. A widget like that might look something like this: ```dart enum QuizOption { pizza, hamburger, } class QuizDialog extends StatelessWidget { const QuizDialog({Key? key}) : super(key: key); static Future show(BuildContext context) { return showCupertinoDialog( context: context, // Important for compatibility with MockNavigator. useRootNavigator: false, builder: (context) => const QuizDialog(), ); } @override Widget build(BuildContext context) { return CupertinoAlertDialog( content: const Text('Which food is the best?'), actions: [ CupertinoDialogAction( onPressed: () => Navigator.of(context).pop(QuizOption.pizza), child: const Text('🍕'), ), CupertinoDialogAction( onPressed: () => Navigator.of(context).pop(QuizOption.hamburger), child: const Text('🍔'), ), ], ); } } ``` Without Mockingjay, every typical widget test for a page showing this dialog would need to press a button to open the dialog, then find the specific widget needed to provide a return value of either pizza or hamburger, and then verify the rest of the logic. However, using our newly acquired superpowers, a test like this becomes trivial to write. ```dart testWidgets('shows quiz dialog when pressed', (tester) async { await tester.pumpWidget( MockNavigatorProvider( navigator: navigator, child: const QuizPage(), ); ); await tester.tap(find.byKey(showQuizDialogTextButtonKey)); // Verify a new route of type `QuizOption?` was pushed // when the button was pressed. verify(() => navigator.push(any(that: isRoute()))) .called(1); }); ``` Need a specific return value from this route? Easy peasy widget treesy. 😎 ```dart testWidgets('displays snackbar when pizza was selected', (tester) async { // Whenever a route of type `QuizOption?` is pushed, // return `QuizOption.pizza`. when(() => navigator.push(any(that: isRoute()))) .thenAnswer((_) async => QuizOption.pizza); await tester.pumpWidget( MockNavigatorProvider( navigator: navigator, child: const QuizPage(), ); ); await tester.tap(find.byKey(showQuizDialogTextButtonKey)); await tester.pumpAndSettle(); expect( find.widgetWithText(SnackBar, 'Pizza all the way! 🍕'), findsOneWidget, ); }); ``` Testing proper popping behavior becomes a breeze as well. ```dart void main() { group('QuizDialog', () { // ... testWidgets( 'pops route with pizza option when pizza button is pressed', (tester) async { await tester.pumpTest( builder: (context) { return MockNavigatorProvider( navigator: navigator, child: const QuizDialog(), ); }, ); await tester.tap(find.text('🍕')); // Verify the current route is popped with the correct // return value. verify(() => navigator.pop(QuizOption.pizza)).called(1); }, ); }); } ``` ## Try it out! We're very excited about using this package in our tests. It helps to keep tests simple and to the point, and takes away multiple headaches, while making it easier to reach our goal of 100% code coverage. We'd love to hear what you think about our package. If you're curious how the magic happens, feel free to take a look at the [repository](https://github.com/VeryGoodOpenSource/mockingjay). If you like it, be sure to leave a ⭐️! If you have any feature requests or other feedback, feel free to open an issue or pull request. Thank you for reading, and happy testing! 👋🏻 🧪 🧑‍🔬 *Felix Angelov contributed to this article.* ### My internship at Very Good Ventures: Summer 2021 Source: https://verygood.ventures/blog/intern-experiences-summer-2021/ This summer, we had the privilege of working with a number of interns at Very Good Ventures. Through our internship program, we strive to provide opportunities to learn about and build production-quality Flutter applications, scalable architecture, testing, and everything else that goes into app development. Our interns have completed an impressive amount of work over the summer — from contributing to open source packages to building on real world apps with clients. We asked the interns to reflect upon their experiences and write about some of the things they’ve learned over the course of the summer. Here is what they said (responses edited for length and clarity). ## Daniel Duan Interning at VGV greatly improved upon my previous experiences working with native iOS development. I learned how to build scalable applications by layering applications, automate workflow with CI/CD, write unit and widget tests, and much more.  I spent the first week familiarizing myself with Flutter and [**bloc**](https://bloclibrary.dev/#/). I soon joined a client team, where I added new features to their app and wrote unit and widget tests. It was incredibly rewarding, allowing me to make contributions to a significant product. I have my mentor, ​​Bartosz Selwesiuk, to thank for the learning experience.  I wrapped up my internship working with Felix Angelov on the [Very Good Ventures Open Source](https://github.com/VeryGoodOpenSource) team. There, I addressed GitHub issues for open source packages, fully tested a VGV product, and even had the opportunity to write a [blog about top open source packages](https://verygood.ventures/blog/top-flutter-dart-packages/). I’m grateful for all that VGV has taught me this summer.  ## Jonathan Kao My internship at VGV was a game-changing experience for me. At VGV, I learned what it meant to write truly scalable and maintainable software and how to effectively collaborate in teams. I also learned the importance of layering applications (in general, not just Flutter applications) and decoupling business logic from other application concerns.  I spent my entire internship working on the open source team with Felix Angelov. I was able to work on developing **bloc** tooling, working on [Very Good Start](https://verygood.ventures/solution/very-good-start/), resolving issues/creating tests for Felix’s open source packages, and strengthening Very Good Start functionality. Overall, working and learning from Felix was a great experience and I feel that this internship has helped me grow to become a better software developer and designer. ## Patrick Li One of my favorite parts about my internship at VGV was being able to try on multiple hats at the company. I began the first week learning the basics of Flutter and **bloc**, running through several tutorials and updating some of our blogs. Next, I had the opportunity to hop on some prospective client calls, do some business outreach, and upgrade some internal management software. Meanwhile, I was working with the open source team to improve the Very Good Start codebase, working on a variety of tasks.  A month into the internship, I joined a client project to help rebuild their mobile app. I learned how to work in a professional development environment, write production-level code, and work with scalable architecture.  In the last leg of my time at VGV, I focused a lot of my time working on the **bloc** library with Felix Angelov. Along with addressing GitHub issues, I also updated the documentation and added a few tutorials for new **bloc** and Flutter updates.  I am very happy to have had such a diverse internship experience, and I would like to thank my amazing mentors — Felix, Oscar, Matt, and Megan for their patience and support.  ## Allison Ryan One of the first and most important things I learned at VGV was the importance of layered architecture. I spent my first few weeks refactoring  a personal project that I had created prior to the internship. My original code did not separate the app logic from the UI components so bugs were much harder to track down and the code was difficult for others to understand. I worked closely with my mentor to untangle the app’s logic and UI dependencies by creating a business logic layer that used **blocs** instead of **notifiers**. With my code separated into several layers, I was able to easily add a new feature to change my app’s color theme by building it incrementally up through the layers. I was able to implement this feature in a day whereas in my original project, it would have likely taken me several days and been a lot more frustrating. A good foundation and consistent architecture practices can really make a world of a difference in a Flutter project. ![Allison's refactored to-do app with bloc-based color theme switching](/assets/images/blog/intern-experiences-summer-2021/body-0.png) *Screenshots from Allison's personal project: a to-do app* I spent the remainder of the internship working on a client project. For me, this was the most valuable work because it exposed me to a production-scale project and team. I primarily worked on adding integration tests for existing features and migrating the codebase over to null-safe Dart. I had written widget tests in the past, so getting to write integration tests that followed a feature/flow from its first screen to completion expanded my testing skill set. Migrating to null safety required  me to make logical decisions about whether a value should be nullable or not which deepened my knowledge of the codebase and Dart’s null safety.  ## Peter Simpson I was fortunate to join an afternoon retreat during my first few weeks at VGV. There, I was treated to a spirited discussion of VGV's rationale for extensive test coverage. A few explanations were offered up: the importance of testing in demonstrating the efficacy of software for clients, or the utility of tests in helping software engineers think about the requirements and complexities of the systems they create. Then, another engineer challenged and expanded the role I saw for testing: "Tests are also a gift to every future developer for the project, whether that be you or someone else who's picking up the project. Tests serve as documentation, and as a confidence-building measure for developers, helping them evaluate the relationship of changes they're making to the project as a whole." That moment encapsulates what I have found to be my greatest reward from working at VGV — looking beyond just a project's immediate requirements and learning how to lay out the best ecosystem for us and for our clients. Coming from the world of one-off computer science homework assignments and personal projects, my mentors at VGV opened my eyes to the importance of creating maintainable, scalable, high-quality projects — not just as abstract values but in specific practices which I have been taught to analyze and implement by some of the best in the field. ## Ria Sharma Like most of my fellow interns, my main goal for this summer was to  learn as much as possible about Flutter, mobile development, and working with a professional team, and I’m so happy to say that this summer has taught me more than my first year at Penn. This summer, I worked on two separate client projects. My first client project had a large existing code base so my main responsibilities included adding smaller features or fixes including facilitating part of the login process, making new error pages, and designing new buttons and widgets for call screens (while of course testing every feature). It was a valuable learning experience in terms of understanding the architecture of a large-scale application and adapting to a curated workflow.  My next client project was just beginning. Most of the features were not yet implemented and the team was much smaller, so it was a completely different experience than the other client project. With the project being in a newer state, I had more responsibilities in terms of writing new features from scratch including new profile pages, adding and selecting markers based on device locations, and more. I took more care in the way I approached different issues and sometimes tried out multiple solutions for a feature before submitting my code.  However, none of these features would have been possible without all of the help from mentors in both projects. The VGV engineers on my team were always happy to help or jump on a quick Google Meet to get through blockers. Overall, through this internship, I am much more confident in my coding abilities and value the process of writing scalable code and testing every aspect of it.  ### To learn more about previous intern experiences, check out these blogs: ### [**My Internship at Very Good Ventures: Sahit**](https://verygood.ventures/blog/sahit-intern-experience/) ### Why We Use flutter_bloc for State Management Source: https://verygood.ventures/blog/why-we-use-flutter-bloc/ State management is the debate that just won’t go away. It seems like every week, there is a new discussion attempting to determine which pattern or library is the best approach for state management in Flutter. We get it — selecting a state management solution could determine your entire application architecture, which is no small decision. And, there are many [popular state management solutions](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options) to choose from such as [**redux**](https://pub.dev/packages/redux), [**mobX**](https://pub.dev/packages/mobx), [**flutter_bloc**](https://pub.dev/packages/flutter_bloc), and even vanilla [**setState**](https://api.flutter.dev/flutter/widgets/State/setState.html).  **Our perspective on the state management debate is: *The best state management solution is the one that works the best for you.*** **When it comes to our projects at VGV, we know exactly what state management solution works best for our team:** [**flutter_bloc**](https://bloclibrary.dev/#/)**.**  This blog is not intended to be the last word in conversations about state management, nor is it an attempt to prove that there is a “winner” once and for all. Instead, we will explain why we use **flutter\_bloc**, the benefits it brings to our team, and how we address common concerns about it.  ## Building Expertise on a Solid Foundation We work with clients across various industries — including automotive, entertainment, healthcare, and financial services. To date, we’ve provided Flutter expertise to over 20 companies, which means we’ve seen a lot of Flutter apps, as well as a mix of approaches to scalable Flutter development. Our team continually pools together our knowledge and refines the way we approach building scalable apps — that is, apps optimized for growth over the long run ([read what we mean by scalable here](https://verygood.ventures/blog/scalable-best-practices/)).  Building a robust product requires our team to pick and define a set of tools and practices that makes us feel comfortable and confident in our ability to deliver and maintain that product successfully over time. One of those tools you need first is *your state management solution.* A while ago we had to ask ourselves as a team: *How do we provide more value to our customers?* We had two options: - Learn and continually improve on a single, well-known and popular solution - Start each project with a different solution, hoping for the benefits and accepting risks it might bring  ### At Very Good Ventures, we are in the business of providing confidence to our clients: we deliver our best when we avoid surprises and are predictable and [**boring**](https://verygood.ventures/blog/boring-code-part-1/). We came to the conclusion that it would be easier and more impactful in the long-run to commit to a single tool that we have used before and know works well, rather than spending our time testing out other solutions. The tool we have selected is **flutter\_bloc**. Before we explain the specific benefits that it brings to our team, we should note that some of our clients come to us with codebases that are already using a state management solution other than **flutter\_bloc**. In these cases, we will work with them to deliver the best code with their preferred tools. However, our job as consultants also entails that we provide recommendations, especially when clients are looking for a solid architecture to start building from, or an effective way to refactor existing code. In these cases, we recommend **flutter\_bloc** as our preferred solution for building predictable, scalable Flutter applications.  ## Predictable, simple, and highly testable One of the cultural values we have defined at Very Good Ventures is to **“scale with standards.”**  We define it like this: - We create scale through solid, repeatable practices - We grow client capabilities in addition to creating software - We are principled with our work and avoid shortcuts - We seek out and build solutions to repeating needs - We embrace automation and long-run optimizations Once our team defined a set of values and principles that we wanted to follow, it was much easier to pick the tool that most accurately represents and embraces those values. And, well, **flutter\_bloc** is the perfect match for this:  ![Screenshot of bloclibrary.dev website](/assets/images/blog/why-we-use-flutter-bloc/body-0.jpeg) *bloclibrary.dev* ### Predictable One of the biggest challenges of product development is knowing, at any point in time, the real state of your application. Flutter helps developers to answer this question through the [widget tree](https://flutter.dev/docs/development/ui/layout). But in order to make your product interesting and interactive, you will need to modify the widget tree; that is, you need to decide how you will manage the structure of your tree (or, in other words, state management). **flutter\_bloc** allows you to decompose your app’s state into smaller, well-defined state machines that transform events into zero, one, or multiple states. For example, when you open your favorite food delivery app, something like this might happen: *When I open my favorite food delivery app, I get a loading indicator that indicates that we are fetching data from the network. If the connection was successful, I will be presented with a list of my favorite restaurants. Otherwise, an error message will be displayed.* Say no more! You could code that requirement in a predictable way using an approximation of plain English: ```dart @override Stream mapEventsToState(FavoriteRestaurantEvent event) async* { if (event is AppOpened) { yield FavoriteRestaurantsLoadInProgress(); try { final restaurants = await restaurantsRepository.fetchFavoriteRestaurants(); yield FavoriteRestaurantsSuccess(restaurants); } catch (_) { yield FavoriteRestaurantsFailure(); } } } ``` ### Simple Every UI application is reactive by nature: your UI is idle most of the time waiting for your customer’s interaction (or a system event) to then provide and transform data. But *you never know* when that interaction will happen, or even if it will happen at all. That’s why your application needs to *react* to those events.  Traditionally, the APIs that helped developers to capture that *reactiveness* were powerful, but incredibly complex to use properly (callbacks, Rx, Streams, etc.). With **flutter\_bloc**, we still honor the fact that every application is reactive by nature, while abstracting the complexity of streams from developers. Forget about maintaining complex subscriptions and lifecycles, and instead, focus on the real, predictable interactions of your product. Only focus on what matters: for the received event, the state that we need produced.  ### Highly testable One of the key qualities that VGV developers have to feel confident about when selecting tools is that they should be testable. Along with the production code, our team [values automated tests](https://verygood.ventures/blog/guide-to-flutter-testing/) that can validate the behavior of the product  at any point in time. [**bloc_test**](https://pub.dev/packages/bloc_test) is a utility library that removes all the complexity of testing reactive code, and allows you to unit test your code with almost no setup required. ```dart blocTest( 'emits [1] when CounterEvent.increment is added', build: () => counterBloc, act: (bloc) => bloc.add(CounterEvent.increment), expect: () => [1], ); blocTest( 'emits [-1] when CounterEvent.decrement is added', build: () => counterBloc, act: (bloc) => bloc.add(CounterEvent.decrement), expect: () => [-1], ); ``` ### Onboarding and documentation Documentation, tutorials, examples, guides, and migration guides are all key to ensure a healthy and productive community. **flutter\_bloc** is known in the community for its [detailed documentation](https://bloclibrary.dev/#/gettingstarted) (which currently provides translations for 10 languages), selection of [high-quality tutorials](https://bloclibrary.dev/#/fluttercountertutorial) (from a simple counter project to complex integrations with Firebase), as well as support for [Angular Dart](https://pub.dev/packages/angular_bloc). We’ve been able to successfully onboard entire engineering organizations by just going through the **flutter\_bloc** documentation. Even our internal onboarding, especially these days when remote work is the norm, heavily relies on the **flutter\_bloc** documentation.  ### Used by thousands **flutter\_bloc** started as a solution for an internal need that some of our teammates faced when working at BMW: *how can we help our teammates all around the world commit their features to Flutter in an efficient, productive, and safe way?* **flutter\_bloc** quickly scaled from being used internally by a few dozen engineers to being open-sourced with more than 7,000 stars on Github! To put it in perspective, [dagger](https://github.com/square/dagger) has 7,300 stars on GitHub. That’s a huge accomplishment for a Flutter package! In addition, **flutter\_bloc** has helped companies of all sizes to scale and release their products successfully, and we are incredibly proud and excited to keep investing and supporting this amazing library! **flutter\_bloc** has even been named a [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites) by the Flutter Ecosystem Committee, who consider metrics like overall package score, feature completeness, usability, and more.  ## Addressing some bloc concerns ### *“Bloc is too complex.”* A quote that is very present in our work is [**“complex problems, easy solutions.”**](https://speakerdeck.com/jorgecoca/flutter-at-scale?slide=4) Flutter already introduces a very powerful solution to manage state: **setState** on **StatefulWidgets***.* So, why do we need a state management solution at all? **setState** is ultimately what every state management solution ends up calling to modify the widget tree, but it can be easy to mismanage when your application gets complex, especially when it comes to managing multiple variables, touch events, navigation, push notifications, analytics. At scale, problems can arise, such as passing state around the widget tree ([prop-drilling](https://kentcdodds.com/blog/prop-drilling)), unnecessary widget tree rebuilds, and code that is generally hard to read and test due to logic injected throughout the UI. With **flutter\_bloc**, we introduce a way to adapt to the reactive nature of every application without having to be proficient in tools and APIs like [**Streams**](https://dart.dev/tutorials/language/streams) or [**RxDart**](https://pub.dev/packages/rxdart)*.* You will still need to learn a few [core concepts](https://bloclibrary.dev/#/coreconcepts) such as the **Bloc API** itself and the difference between a **BlocBuilder** and a **BlocListener**. The good news is these are all things that are [well-documented](https://bloclibrary.dev/#/gettingstarted) and covered in [multiple](https://bloclibrary.dev/#/fluttercountertutorial) [tutorials](https://bloclibrary.dev/#/flutterweathertutorial). **flutter\_bloc** might not be the first tool you need to learn when getting started in programming, or even during your first attempts with Flutter (after all, you need to learn first how to walk before you can run). But in our experience, **flutter\_bloc** can boost your productivity and efficiency as a developer due to its simplicity and relatively few concepts to learn. ### *“Bloc has too much boilerplate.”*  **flutter\_bloc** requires you to model your events and states with different classes. This, compared to the simplicity of simply declaring variables if you were using **setState***,* could be considered a step back since it might require you to write more lines of code to achieve the same functionality. However, is the sheer number of lines of code a fitting metric for considering a piece of software *good software?* A beautiful thing about modeling your events and states with separate classes is that: - You can truly make them immutable - Your events, states, and their properties are completely independent of each other If your problem is complex, what at the beginning might seem like a burden (*“Ugh, do I have to write a single class per state?”*) will turn into a benefit! (*“Yes! I am so glad that my success logic is completely separate from my error handling!”*). Alternatively, if you truly consider your event information boilerplate, you can default to using [**cubit**](https://bloclibrary.dev/#/coreconcepts?id=cubit) instead of **bloc**: after all, **cubit** is mostly a bloc without events as a class, but as a public method. You'll lose functionality, like automating logs and analytic events, but you can then write less code without sacrificing legibility. Tooling can also make it easier to write this code quickly and consistently — such as the bloc [VS Code](https://marketplace.visualstudio.com/items?itemName=FelixAngelov.bloc) and [IntelliJ](https://plugins.jetbrains.com/plugin/12129-bloc) extensions.  Finally, we are excited and hopeful for the changes that the Dart team is considering with the [**static metaprogramming**](https://github.com/dart-lang/language/issues/1482) issue; they could help even further to simplify the **bloc API**, be more strict and safe, and do *more with less.* ## We have the highest level of confidence in flutter\_bloc In our business, providing confidence and meeting expectations is key, which means our team must be flexible and adapt to changing client needs. As a highly experienced team of Flutter developers, clients ask us to recommend tools and approaches that will provide the best results. When it comes to state management, **flutter\_bloc** is the fastest and safest path to engineering excellence for our team.  *Scarlett Wardrop contributed to this article.* ### Code generation with Mason Source: https://verygood.ventures/blog/code-generation-with-mason/ [**Mason**](https://pub.dev/packages/mason) is an open source tool that generates code from custom templates called bricks. It can be helpful for developers who find themselves writing the same code over and over again, or those looking for a way to customize reusable templates. **Mason** can increase the efficiency of development teams by enabling greater day-to-day automation. For example, if there is a widget or method that your team will be using throughout an entire project, you can simply write that piece of code once as a brick template. You can then use **mason** at any time to generate that piece of code from the template with dynamic variables. Brick templates can be maintained locally as part of a specific project or remotely via a GitHub repository. As an individual developer, **mason** can be extremely helpful for creating and generating your own templates to start new projects. In fact, that’s exactly what we did with [**Very Good CLI**](https://github.com/VeryGoodOpenSource/very_good_cli), which uses mason under the hood to generate Very Good Core, our VGV opinionated Flutter starter app template. Another example is [**groovin_cli**](https://github.com/GroovinChip/groovin_cli), an app generator created by [**GroovinChip**](https://twitter.com/GroovinChip) using **mason** and inspired by **Very Good CLI**. Mason is written in Dart but can be used to generate code in any programming language. It can also generate code from templates of any size — ranging from a few lines to a complete application consisting of hundreds of files and directories. ## How it Works **Mason** uses the [**mustache templating syntax**](https://mustache.github.io/mustache.5.html) to enable developers to create and maintain complex templates called bricks without having to write any generator code. Internally, **mason** maintains references to all installed brick templates and surfaces them to the developer via the **mason make** command. Brick templates consist of a **brick.yaml** file and a **\_\_brick\_\_** directory which contains the template code. When you want to generate code from a brick, **mason** gathers the input variables needed for the brick, either via prompts or as command line arguments, and injects them into the brick before writing the newly generated code to disk. Since **mason** is a CLI tool, you can run it straight from your terminal. ## Getting Started **Mason** can be installed either via [**pub.dev**](https://pub.dev/packages/mason) or [**homebrew**](https://github.com/felangel/homebrew-mason). ```shell # Install from pub.dev $ dart pub global activate mason_cli # Install from homebrew $ brew tap felangel/mason $ brew install mason ``` At this point, **mason** should be available as a command. You can verify by running **mason** in your terminal. ```shell $ mason ⛏️ mason • lay the foundation! Usage: mason [arguments] Global options: -h, --help Print this usage information. --version Print the current version. Available commands: bundle Generates a bundle from a brick template cache Interact with mason cache get Gets all bricks. init Initialize mason in the current directory. make Generate code using an existing brick template. new Creates a new brick template. Run "mason help " for more information about a command. ``` ## Hello World Example The first example we’ll take a look at is the hello world example which comes out of the box when you initialize **mason**. ### Initializing Mason Start by running **mason init** in your directory of choice. It should generate a **mason.yaml** along with a **bricks** directory. ```shell $ mason init ✓ Initialized (0.1ms) ✓ Generated 3 file(s): mason.yaml (new) bricks/hello/brick.yaml (new) bricks/hello/__brick__/HELLO.md (new) ``` The **mason.yaml** is where bricks can be registered similar to the **pubspec.yaml**. Bricks can be referenced either via path or git URL as seen below. ```yaml # Register bricks which can be consumed via the Mason CLI. # https://pub.dev/packages/mason bricks: # Sample Brick # Run `mason make hello` to try it out. hello: path: bricks/hello # Bricks can also be imported via git url. # Uncomment the following lines to import # a brick from a remote git url. # todos: # git: # url: git@github.com:felangel/mason.git # path: bricks/todos ``` The bricks directory contains the brick templates. In this case, there is only a single brick called, **hello**. The **brick.yaml** contains important metadata about the template such as the name, description, and required variables. ```yaml name: hello description: An example hello brick. vars: - name ``` Each brick also has a **\_\_brick\_\_** directory which contains the template itself. Any files or directories within **\_\_brick\_\_** will be part of the generated code when the brick is run. ```shell ├── __brick__ │ └── HELLO.md └── brick.yaml ``` If we take a quick look at the **HELLO.md** template file, we can see that we're able to access the **name** variable via **{{name}}**. ```md Hello {{name}}! ``` Now that we've taken a closer look at the parts of a brick template, let's install one. ### Installing Brick Templates We can install all bricks declared in the **mason.yaml** via the **mason get** command. ```shell $ mason get ✓ getting bricks (0.0ms) ``` This will generate a **.mason** which contains relevant metadata needed to determine where the templates are installed. We recommend adding the **.mason** directory to your **.gitignore**. ```shell # .gitignore .mason/ ``` Now we're ready to generate some code! ### Generating Code Once the bricks have been installed, we can run **mason make hello** to use the **hello** template. ```shell $ mason make hello --name Felix ✓ Made brick hello (0.0ms) ✓ Generated 1 file(s): /HELLO.md (new) ``` **HELLO.md** will be generated in the current directory. The contents of the file should look something like: ```md Hello Felix! ``` **Mason** will prompt you for any variables that haven't been provided via command line arguments: ```shell $ mason make hello name: Dash ✓ Made brick hello (7.7ms) ✓ Generated 1 file(s): /HELLO.md (new) ``` **HELLO.md** should now have been overwritten to include the updated name: **Dash**. ```md Hello Dash! ``` Now let's take a look at how to create our own brick for a custom widget. ## Custom Widget Example We can use the **mason new** command to create a new brick. ```shell mason new widget -d "A custom Flutter widget" ✓ Created new brick: widget (0.1ms) ✓ Generated 2 file(s): bricks/widget/brick.yaml (new) bricks/widget/__brick__/hello.md (new) ``` Note that **mason** automatically declared the new **widget** brick in the **mason.yaml**. ```yaml bricks: hello: path: bricks/hello widget: path: bricks/widget ``` Here is our new widget brick's **brick.yaml** file: ```yaml name: widget description: A custom Flutter widget vars: - name ``` Let's add a variable called **routable** which will determine whether or not the new widget should expose a route function. ```yaml name: widget description: A custom Flutter widget vars: - name - routable ``` Now the **brick.yaml** is complete. Let's start writing the template. We can delete the default **hello.md** file within the **\_\_brick\_\_** directory and create a new file called: **{{#snakeCase}}{{name}}{{/snakeCase}}.dart** The **snakeCase** lambda is provided by **mason**. Currently **mason** provides many lambdas out of the box such as: - **camelCase** - **constantCase** - **dotCase** - **headerCase** - **lowerCase** - **pascalCase** - **paramCase** - **pathCase** - **sentenceCase** - **snakeCase** - **titleCase** - **upperCase** This will tell **mason** to create a filename, **.dart**, and ensure the filename is snake case. Next, let's modify the content of our widget template file to include the widget code. ```dart import 'package:flutter/material.dart'; class {{#pascalCase}}{{name}}{{/pascalCase}} extends StatelessWidget { const {{#pascalCase}}{{name}}{{/pascalCase}}({Key? key}) : super(key: key); {{#routable}} static PageRoute route() { return MaterialPageRoute(builder: (context) => const {{#pascalCase}}{{name}}{{/pascalCase}}()); } {{/routable}} @override Widget build(BuildContext context) { // TODO: implement build return const SizedBox(); } } ``` Here we're defining a **StatelessWidget** called **name** and using the **pascalCase** lambda to ensure it is a valid class name. We're also using mustache sections to define a static route function only if the variable **routable** is **true**. That's it, now anytime we want to generate a new widget, we can use **mason make widget**. ```shell $ mason make widget name: dash container routable: true ✓ Made brick widget (12.5ms) ✓ Generated 1 file(s): dash_container.dart (new) ``` Then we can take a look at the generated code in **dash\_container.dart**: ```dart import 'package:flutter/material.dart'; class DashContainer extends StatelessWidget { const DashContainer({Key? key}) : super(key: key); static PageRoute route() { return MaterialPageRoute(builder: (context) => const DashContainer()); } @override Widget build(BuildContext context) { // TODO: implement build return const SizedBox(); } } ``` If we want a new widget that is not routable, we can set **routable** to **false**: ```shell $ mason make widget name: flutter box routable: false ✓ Made brick widget (16.8ms) ✓ Generated 1 file(s): flutter_box.dart (new) ``` And if we take a look at the newly generated **flutter\_box.dart**, we should see there is no **route** method defined: ```dart import 'package:flutter/material.dart'; class FlutterBox extends StatelessWidget { const FlutterBox({Key? key}) : super(key: key); @override Widget build(BuildContext context) { // TODO: implement build return const SizedBox(); } } ``` ## What's Next We're actively working on several new features including support for global templates and template extensions. You can head over to the [**mason repo**](https://github.com/felangel/mason) to check on the progress, provide feedback, and explore other examples. Happy templating! ### Readable Robot Tests: Bridge the Tech Gap Source: https://verygood.ventures/blog/robot-testing-in-flutter/ Robot Testing is an end-to-end (E2E) technique that mimics a human interacting with software who can see and do anything a human can in an automated way. Its main advantage is that it focuses on the WHAT and not the HOW we are testing it. When a requirement changes or as our project or application evolves, we can continue to use the robot tests and its components. Developers can build a Robot Testing scenario by grouping multiple robot tests that each correspond to one screen of the application. Non-technical stakeholders can simply glance over the different testing scenarios to obtain the necessary confidence to release the product, knowing that the project is tested E2E in an automated way.  ## Benefits for Stakeholders With Robot Testing, we gain confidence in the product because the engineers have control of the tests and can guarantee that they are written correctly. In addition, the tests are readable and understandable by non-technical stakeholders and teammates outside of the engineering team. Another big benefit of robot tests is that since there are specific segmented “robots” or “tests” for specific components, the tests themselves can be reused and maintained as the project and codebase grows. ## Separating the “What” from the “How” Often for Program Managers, one of the first questions we ask ourselves is: *How can I make sure that what we are building behaves as expected?* And when it comes to testing, the most important factor for us is “what” we are testing, which should match with the requirements for that feature or project. Here’s one example of a PM caring about the “what.” The things we care about testing are: - If a user navigates from the home page to the login screen and enters only email and no password, they should receive an error message. - If a user enters email and password and they match, the user should receive a welcome message.  The first thing we're looking for is that we are testing all the steps/requirements in my story. Thanks to the Robot Pattern, we can look at an isolated section of the code, confirm that the engineers are testing what we need tested, and verify that the result is as expected. When using robot tests, we can also benefit from visual assertion in the form of a video or screen recording that shows the code being run and a simulator showing what it looks like in a specific device (engineers can provide this). When we separate the “what” from the “how,” we segment the test and are able to expose the composition listing what we are testing in a readable way.  ## How to Use Robot Testing in a Flutter App  We’ve talked quite a bit already about how the Robot Testing pattern is all about separating the “what” from the “how.” In other words: in one file, the requirements of a certain feature must be presented in semi-natural language, and must not contain any implementation details of the testing tool we might be using (these testing scenarios should not have any code related to the *WidgetTester* nor from the *FlutterDriver)*. This represents “what” we are testing. In a different file, the “robot” code contains actions that could be executed on a given screen, and includes details about the different testing frameworks we might be using.  Let’s see a sample of a testing scenario with the Robot Pattern used to test a login screen: ```dart group('Login', () { test('shows error message when login information is missing', () async { await homeRobot.navigateToLoginPage(); await loginRobot.enterEmail('notvalid'); await loginRobot.tapLoginButton(); await loginRobot.checkInvalidCredentialsMessageIsShown(); }); test('authenticates a user with an email and password', () async { await loginRobot.enterEmail('email@email.com'); await loginRobot.enterPassword('s3cr3etP4ssw0rd'); await loginRobot.tapLoginButton(); await loginRobot.checkWelcomeMessageIsShown('email@email.com'); }); }); ``` As you can see, this testing scenario does not contain any implementation details about the testing framework used, and instead focuses on the feature requirements and the actions we need to execute to assert the behavior of our feature. **This is the part we refer to as “what” we are testing, and it is highly valuable for stakeholders, as well as for engineers looking for ways to self-document their features.** Each one of the robots used will hide the implementation details of the testing framework. **In our case, we use *FlutterDriver* to test our application — this it the “how” of Robot Testing:** ```dart import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; class LoginRobot { const LoginRobot(this.driver); final FlutterDriver driver; Future enterEmail(String email) async { await driver.tap(find.byValueKey('emailTextField')); await driver.enterText(email); } Future enterPassword(String password) async { await driver.tap(find.byValueKey('passwordTextField')); await driver.enterText(password); } Future tapLoginButton() async { await driver.tap(find.text('Login')); } Future checkInvalidCredentialsMessageIsShown() async { final errorMessageFinder = find.byValueKey('snackbar'); await driver.waitFor(errorMessageFinder); } Future checkWelcomeMessageIsShown(String email) async { final welcomeMessageFinder = find.text('Welcome $email'); await driver.waitFor(welcomeMessageFinder); expect(await driver.getText(welcomeMessageFinder), 'Welcome $email'); } } ``` **For more samples of the Robot testing pattern, you can visit our** [**sample project**](https://github.com/VGVentures/robot_testing)**.** ## Robot Testing in the Real World At VGV we aim for 100% test unit and widget test coverage, so these are baked into our Flutter development process. We can incorporate Robot Testing as an E2E testing strategy when it fits the project. We are happy to use it since it can help both our engineering team *and* our PM teams collaborate and communicate efficiently. Robot testing is an additional step that can increase the confidence in your ability to release your product to the market, as well as decrease the cost and amount of manual testing needed, especially when there are regular releases that need to be tested. Robot tests are just one of many other E2E tests that can be used in a project. At VGV, we love to let Robots test our Flutter Apps! ### For more about testing strategies, read our [**Very Good Guide to Flutter Testing**](https://verygood.ventures/blog/guide-to-flutter-testing/). ### VGV Turns 3: What We’ve Learned About Flutter Since Day 1 Source: https://verygood.ventures/blog/vgv-turns-3-what-weve-learned-about-flutter/ ## Today we’re celebrating our third birthday!  It’s hard to believe that just three years ago we started business as a brand new consultancy dedicated exclusively to Flutter app development. As we worked on the [Hamilton app](https://verygood.ventures/case-studies/hamilton/), the first commercial app built in Flutter (outside of Google), we were amazed at the speed by which we were able to build and honestly how fun it was to use Flutter! We were so excited that we built our entire business around it.  Three years later and we’re still going strong. We are especially excited to partner with some well-known enterprise companies to build Flutter apps — while we can’t reveal any names just yet, stay tuned for developments! We’re also ramping up our efforts to give back to the Flutter Community by building some awesome open source libraries under our GitHub organization [Very Good Open Source](https://github.com/VeryGoodOpenSource).  We’ve learned a lot since opening shop three years ago, so to celebrate our birthday, we’re sharing 3 things we’ve learned about Flutter, 3 of our favorite Flutter projects, and 3 things we’re excited about for the future! ![Celebrating three years of VGV](/assets/images/blog/vgv-turns-3-what-weve-learned-about-flutter/body-0.jpeg) ## Three Things We’ve Learned About Flutter We’ve been working with Flutter since it’s official launch in 2017. We remember the [early days when Flutter was missing so many things](https://twitter.com/deremer/status/1369854045431676934) we take for granted now — there was no Firebase, Google Maps, and very limited third party packages that worked with Flutter. It’s impressive that now there are over 15,000 open source Flutter and Dart packages! Here are three things we’ve learned about Flutter over the years. ### Flutter is an ideal choice for development teams. Flutter is much more than just a UI toolkit! It can also be a gamechanger for accelerating app development for teams. We could go on about the benefits that Flutter brings to productivity ([we even wrote a blog about it here](https://verygood.ventures/whitepapers/building-enterprise-apps-with-flutter/)), however one often overlooked benefit you get when choosing to go with Flutter is the efficiency it can bring to the *development process* overall. Gone are the days of platform specific development teams. Now all engineers can work from the same codebase together, focused on building functionality and new features, rather than duplicating the same feature for two platforms. Using one unified framework, teams can build in a scalable way so that apps are optimized for long-term growth. There are numerous companies that have verified this experience through their own write-ups: **Realtor.com:** “*With our previous way of working, our iOS and Android teams were independently defining their own release schedule and were releasing whenever the next release was ready. Now, with five different teams making code contributions in both our Android and iOS apps, we quickly had to start working with a 2 week release train.*” [Read the full account here](https://techblog.realtor.com/scaling-mobile-development-at-realtor-com-with-flutter/). **eBay Motors:** “*We informally surveyed the team to help validate our decision to use Flutter, and the responses showed the majority of the team believed developing in Flutter was over twice as fast as developing for their prior platform. Perhaps more importantly, 100% of those surveyed enjoyed Flutter development more than iOS or Android. As it turns out, happy developers tend to be more engaged and productive.*” [Read the full account here](https://innovation.ebayinc.com/stories/ebay-motors-accelerating-with-fluttertm/). ### You don’t have to start from scratch with Flutter add-to-app Companies that already have an existing application don’t have to miss out on Flutter. With the [add-to-app functionality](https://flutter.dev/docs/development/add-to-app), piloting Flutter can be a low stakes experiment. This means that you can evaluate Flutter on your terms, make sure it fulfills business requirements, and try it out on your own before committing to the switch. Then, if you do decide to make the [switch to Flutter](https://verygood.ventures/solution/flutter-transformation/), you don’t have to completely abandon your native app — at least for the time being. You can transition your native app over to Flutter bit by bit, [an approach that we helped Betterment implement](https://verygood.ventures/success-stories/betterment/). With the arrival of [Flutter 2.0](https://medium.com/flutter/whats-new-in-flutter-2-0-fe8e95ecc65), there have been major upgrades to the Flutter ecosystem, including web in stable and desktop in stable early release. Flutter is becoming harder for companies to ignore. What’s really exciting is that Flutter is no longer just a framework for building Android and iOS apps. It’s becoming a truly portable framework—one that can be run on 6 six platforms: Android, iOS, web, Windows, macOS, and Linux! Flutter is growing into the go-to choice for building cross-platform applications from a single codebase. #### Technology choice is important, but what matters are the values and best practices you develop as a team. Over the past three years, we have honed our engineering process and [approach to building scalable apps](https://verygood.ventures/blog/scalable-best-practices/). The single most important question we ask ourselves when building Flutter apps is: *Is this scalable?* We design app architecture, develop team workflows, and determine how to implement features with this in mind.  The way we construct scalable applications is applicable to any language or framework. Luckily we’ve had three years to hone our best practices to ensure that all of our codebases remain consistent with our custom linter rules and 100% test coverage. We also created [Very Good Start](https://verygood.ventures/solution/very-good-start/) so that all of our projects could start with the same scalable foundation on day one. That said, Flutter is just *one variable* in our formula for success. We truly believe that Flutter is the current best solution available for building scalable cross-platform applications for all screens. However, technology is constantly changing, and we are open to embracing new languages, frameworks, and best practices! Of course, having talented teammates makes this all possible — VGV certainly would not be where we are today without our expert developers. The success of our business is directly tied to the success of each individual teammate. ## Three of Our Favorite Flutter Projects Over the last three years we’ve provided expertise to over 20 companies on Flutter. Here are three of our favorite projects: ### Hamilton — the Flutter app that started it all Hamilton was the first commercial Flutter app on the market outside of Google and we had the distinct honor of helping build it. Things have certainly changed from the early days of Flutter. We remember when we would run into a problem and would have to ask the Google Flutter team directly for help, instead of turning to the comprehensive documentation or many talented Flutter developers available today that can help answer questions.  ![Hamilton app screens showing the Flutter-built musical companion app](/assets/images/blog/vgv-turns-3-what-weve-learned-about-flutter/body-1.png) The Hamilton app is still going strong, especially as [live performances of the hit musical start back up again](https://youtu.be/cSZ3C7eFpVw). We look forward to the day when Hamilton returns to stages in the U.S.! The Hamilton app will always hold a special place in our hearts since it is the project that launched us into Very Good Ventures. Working on this project showed us that Flutter solves a lot of the pain points of traditional app development. We were so excited about its prospects that we built our entire company around it! ![Hamilton app home screen comparison: 2017 original vs 2021 redesign](/assets/images/blog/vgv-turns-3-what-weve-learned-about-flutter/body-2.png) *Left: original home screen from 2017; Right: new home screen from 2021* ## Showing the World What Flutter Can Do x3 Okay, we’re squeezing three projects into one here, but they all have one major thing in common: they’re all projects where we’ve partnered with Google to showcase significant Flutter milestones! 1. We built **Hamilton, the first commercial mobile app built in Flutter,** which launched in 2017 and has over 3.5 million installs. [Read more about it here](https://verygood.ventures/case-studies/hamilton/). 2. In December 2018, we built a **presentation slides desktop application** to prototype Flutter which was revealed onstage at Flutter Live in 2018. [Read more about our work here](https://verygood.ventures/success-stories/flutter-slides/). 3. A year later we collaborated with The New York Times and Google to create a **Flutter web prototype of the popular KENKEN game** for Google I/O. [Read about it here](https://verygood.ventures/success-stories/new-york-times/). ![Timeline of ](/assets/images/blog/vgv-turns-3-what-weve-learned-about-flutter/body-3.png) ### Very Good CLI  [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli) is the first major tooling project we’ve created. It all started with [Very Good Start](https://verygood.ventures/solution/very-good-start/), our approach to accelerating app development. Back in February of 2020, we started researching ways that we could generate entire codebases so that we could begin every project at VGV with the same scalable foundation and basic features that every app needs. We wanted to see if we could do this in minutes, as opposed to weeks of development (Spoiler: we did it! [Read our whitepaper about it here](https://verygood.ventures/whitepapers/ebook-introducing-very-good-start/)). While Very Good Start is intended to be used for our ongoing client consulting, we also believe it’s important to share our approaches and best practices with the community.  This inspired us to develop a free, simplified version of Very Good Start called Very Good Core. It contains some of the basic features of Very Good Start as well as the counter app that comes with “flutter create.” Very Good Core is a great option for solo developers looking for a very good foundation for Flutter projects. We considered open sourcing only Very Good Core, our version of the Flutter starter app, but we decided to go one step further and develop a [Very Good CLI](https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/) so that we could have the freedom to develop additional tooling in the future. We used our teammate Felix Angelov’s open source package [mason](https://github.com/felangel/mason), to generate our custom Very Good Core template. We may develop additional templates that you can generate with Very Good CLI, so stay tuned! ![How it started: planning in Feb 2020. How it's going: 657 stars on GitHub](/assets/images/blog/vgv-turns-3-what-weve-learned-about-flutter/body-4.png) *Very Good CLI progression* For an in-depth look at Very Good Core and Very Good CLI, check out this video by Flutterly that explains all of the benefits you get for free: ## Three Things We’re Excited About for the Future ### Flutter for embedded devices Developers have been incorporating the portable framework into embedded devices for a few years now. Back in 2019, The Flutter team [wrote about Flutter for embedded devices](https://developers.googleblog.com/2019/05/Flutter-io19.html) and highlighted the embedding API that can be used to run Flutter on platforms beyond just mobile. Since then, other companies have done their own work to incorporate Flutter into embedded devices. Sony Software Engineer Hidenori Matsubayashi gave a [presentation at the Embedded Linux Conference 2020](https://www.phoronix.com/scan.php?page=news_item&px=Sony-Using-Flutter-Wayland) about the decision to use  Flutter with Wayland for embedded devices. He notes that they chose Flutter for a few reasons, including its ability to easily create beautiful UI; support for mobile, desktop, and web devices; it’s open source and comes with a lot of information for how to use it; it natively compiles; and it includes a [custom embedder API-layer for specific platforms](https://github.com/flutter/flutter/wiki/Custom-Flutter-Engine-Embedders). Most recently, the [Flutter Engage keynote](https://youtu.be/zSbsIiluixw?t=1964) showcased another major brand using the Flutter Embedder API: Toyota. Daniel Hall, Chief Engineer, revealed that Toyota is partnering with Flutter to build next-gen infotainment systems with Flutter for their vehicles. ![Toyota Flutter partnership announcement from Flutter Engage keynote](/assets/images/blog/vgv-turns-3-what-weve-learned-about-flutter/body-5.png) *Screenshot from keynote about Toyota + Flutter* Flutter is truly living up to its designation as a *portal* framework. We are excited to see more companies use the embedded functionality of Flutter to bring beautiful apps to all screens — mobile, web, desktop, car infotainment, and beyond! ### More Companies Using Flutter Speaking of Toyota, the [Flutter Engage keynote](https://youtu.be/zSbsIiluixw) revealed some other key players that have joined the Flutter fun. Flutter is no longer the new kid on the block when it comes to building cross-platform applications, now Flutter is leading the way! Here are some companies that are using Flutter: - **Canonical:** Canonical announced that Flutter is now Canonical’s framework of choice for future mobile and desktop apps for Linux. Ubuntu also published the Flutter SDK in the Snap store so that developers can get started right away. - **Microsoft:** Another major announcement from the keynote was that Microsoft has partnered with Flutter to support Surface Duo foldable devices. - **iRobot:** The manufacturers of Roomba also build educational robots that help teach children how to code. They used Flutter to build a web app that uses the same code as the corresponding tablet app. We also [compiled a list](https://verygood.ventures/blog/top-companies-using-flutter/) of companies that we think are doing noteworthy work with Flutter in 2021. ### When Flutter Becomes “Mainstream” Hear us out—Flutter has made some remarkable progress since its launch, but it may be some time before it becomes truly mainstream. There are signs that the framework is maturing: major companies have been using it for years now, the Flutter and Dart ecosystem now contains over [15,000 packages](https://pub.dev/), and there are many articles, tutorials, courses, even podcasts to educate developers of all experience levels.  Yet we are still seeing people express concern about choosing Flutter, whether for an individual side project or an enterprise production app. The popular conversation around Flutter has not quite shifted from “Should I use Flutter?” to “What’s the best way to get started?”  We can’t pinpoint exactly when Flutter will no longer be considered “the new kid on the block.” Perhaps the recent arrival of Flutter 2.0 has already catapulted Flutter to the head of the cross-platform framework pack. Or maybe the arrival of the next best cross-platform framework will, instead of attracting developers, end up pushing them to feel confident in Flutter’s longevity. We believe that it’s only a matter of time before Flutter becomes ubiquitous for app development and we plan to be at the forefront of the movement. ## Here’s to Many More Years of Flutter Development! Thank you for letting us show the world what Flutter can do. We are working on some exciting Flutter projects and cannot wait to show you where we’re going! **Keep up to date on our latest** [**client successes**](https://verygood.ventures/success-stories/)**.**  **Read the latest insights from our team on our** [**blog**](https://verygood.ventures/blog/)**.** **Follow us on Twitter** [**@VGVentures**](https://twitter.com/VGVentures) **for news & updates.** ### Flutter starter app: Very Good Core & CLI Source: https://verygood.ventures/blog/flutter-starter-app-very-good-core-cli/ ## What is Very Good Core? Our team created [Very Good Core](https://github.com/VeryGoodOpenSource/very_good_templates/tree/main/very_good_core) to provide a starter app option for developers looking for a very good foundation. It comes with our best practices and opinionated architecture. As with any starter app, feel free to build upon it, remove code, and make it your own! ![Counter app generated from Very Good CLI](/assets/images/blog/flutter-starter-app-very-good-core-cli/body-0.png) *Counter app generated from Very Good CLI* ### Very Good Core Features Very Good Core follows the [Very Good Ventures way of architecting apps](https://verygood.ventures/blog/scalable-best-practices/) — this is not the *only* way, or the *right* way, it is simply the way that works best for us when building scalable applications and we want to make it available to the Flutter Community!  Very Good Core includes the following features: - [**Cross Platform Support**](https://flutter.dev/docs/development/tools/sdk/release-notes/supported-platforms)**:** Built-in support for iOS, Android, and Web (Desktop coming soon!) - [**Build Flavors**](https://flutter.dev/docs/deployment/flavors)**:** Multiple flavor support for development, staging, and production - [**Internationalization Support**](https://flutter.dev/docs/development/accessibility-and-localization/internationalization)**:** Internationalization support using synthetic code generation to streamline the development process - [**Sound Null-Safety**](https://flutter.dev/docs/null-safety)**:** No more null-dereference exceptions at runtime — develop with a sound, static type system - [**Bloc State Management**](https://bloclibrary.dev/)**:** Integrated bloc architecture for scalable, testable code which offers a clear separation between business logic and presentation - [**Testing**](https://flutter.dev/docs/testing)**:** Unit and Widget Tests with 100% line coverage (Integration Tests coming soon!) - [**Logging**](https://api.flutter.dev/flutter/dart-developer/log.html)**:** Built-in, extensible logging to capture uncaught Flutter and Dart Exceptions - [**Very Good Analysis**](https://pub.dev/packages/very_good_analysis)**:** Strict Lint Rules which are used at Very Good Ventures - [**Continuous Integration**](https://github.com/features/actions)**:** Lint, format, test, and enforce code coverage using [GitHub Actions](https://github.com/features/actions) ### We also wrote a comprehensive guide that gives an overview of the app architecture, features, and more. [**View it on GitHub here**](https://cli.vgv.dev/docs/templates/core). ## How it works: Very Good CLI You can bootstrap our very good Flutter app template, Very Good Core, with one command ***very\_good create*** via our CLI tool. [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli) is a Command Line Interface for Dart. Currently the CLI has support for creating new Flutter projects from scratch. Initially, the only template we support is Very Good Core, but we may support additional feature-rich templates, so stay tuned for future releases. Very Good CLI is built on top of [mason](https://pub.dev/packages/mason), an open source template generator written in Dart, which helps developers generate files from custom templates called bricks. If you want to try building your own Flutter app template (or any template, for that matter), head over to the [mason documentation for more](https://github.com/felangel/mason). ## Getting Started To generate the Very Good Core starter app with Very Good CLI, just activate the very\_good\_cli package from [pub.dev](https://pub.dev/packages/very_good_cli). ```shell $ dart pub global activate very_good_cli ``` Then, run the create command in a new project directory (just like flutter create): ```shell $ very_good create my_app ``` Voila, your new very good project has been created!  ![Output after generating Very Good Core using Very Good CLI](/assets/images/blog/flutter-starter-app-very-good-core-cli/body-1.png) *Output after generating Very Good Core using Very Good CLI* Each project comes with the build flavors out of the box — development, staging, and production. You can run them by using the built-in launch configuration for VSCode and Android Studio or using the following commands: ```shell # Development $ flutter run --flavor development --target lib/main_development.dart ``` ```shell # Staging $ flutter run --flavor staging --target lib/main_staging.dart ``` ```shell # Production $ flutter run --flavor production --target lib/main_production.dart ``` ## What about companies & development teams? We think that Very Good Core will be most useful for individual developers looking to quickly bootstrap a project that has a built-in scalable templated foundation. For teams looking for an enterprise-grade starter app, we built [Very Good Start](https://verygood.ventures/solution/very-good-start/), which we use in combination with our advisory services to accelerate app development. ### [**View our open source software here.**](https://verygood.ventures/resources/open-source-packages/) ### Testing Fundamentals of Flutter [Course] Source: https://verygood.ventures/blog/testing-fundamentals-of-flutter-course/ We have some very exciting testing news to share! A few months ago, our teammate Jorge developed a comprehensive Lesson on Flutter Testing for Caster.IO. While Caster.IO has since ceased operations, we are very fortunate to be the new home for hosting Jorge’s Flutter testing content. ***The best part is: it’s now free!***  We are very excited to make this content available to the developer community because we strongly believe that testing is a crucial part of the development process. It can make or break your application—lack of tests or offloading testing to a separate development team could lead to low-quality applications and last minute scrambles to squash bugs before launch. In contrast, integrating testing into the development process can increase confidence in your codebase and ensure that your app is able to ship at any point. This course will provide a nice foundation for Flutter developers looking to learn the fundamentals of testing, or serve as a handy reference for more experienced developers. We plan to release the full course in three batches over the next few weeks according to the below schedule. **You can view all of the content on this** [**YouTube playlist**](https://www.youtube.com/playlist?list=PLprI2satkVdFwpxo_bjFkCxXz5RluG8FY)**.** ## Testing Fundamentals of Flutter Tutorial Schedule ### Week 1 **Lesson 1:** [What is the difference between unit testing, widget testing, and integration testing?](https://youtu.be/M_eZg-X789w)  **Lesson 2:** [Writing your first unit test](https://youtu.be/8yh1tuT-bwI)  **Lesson 3:** [Running your test from the terminal](https://youtu.be/_9HWenMieGY) **Lesson 4:** [The structure of a good test](https://youtu.be/gJhc0eHzxdU) **Lesson 5:** [Using expect() in your tests](https://youtu.be/eBfaQRNPLAY) **Lesson 6:** [All you need to know about Matchers](https://youtu.be/GKPIA1Auk4I) **Lesson 7:** [Grouping your tests using the group() function](https://youtu.be/msF1FPfrAfQ) **Lesson 8:** [Testing code that throws an Error or an Exception](https://youtu.be/ssXyBYWSrYI) **Lesson 9:** [Sharing code between tests with setUp() and tearDown()](https://youtu.be/7NEmvBM4tp8) ### Week 2 **Lesson 10:** [Testing Futures](https://www.youtube.com/watch?v=iVsq9Y5ttbE) **Lesson 11:** [Testing Streams](https://www.youtube.com/watch?v=c-7dXgwCtKs) **Lesson 12:** [Writing your first widget test](https://www.youtube.com/watch?v=Ww4z9Ww29wI) **Lesson 13:** [The difference between pumpWidget(), pump(), and pumpAndSettle()](https://www.youtube.com/watch?v=jUlqOtUaj0g) **Lesson 14:** [Finding a widget in your widget tree using CommonFinders](https://www.youtube.com/watch?v=-gLIIyFa0X8) **Lesson 15:** [Interacting with your widgets using WidgetTester](https://www.youtube.com/watch?v=ho_-1rwZKIQ) **Lesson 16:** [Verifying your widget tests using a Matcher from flutter_test](https://www.youtube.com/watch?v=FXkRpa2cm20) **Lesson 17:** [Testing smaller widgets in isolation](https://www.youtube.com/watch?v=HoTZ5CcmDiM) ### Week 3 **Lesson 18:** [Controlling the behavior of your dependencies using mockito](https://youtu.be/Vgxxb7YAU6M) **Lesson 19:** [Stubbing synchronous methods with mockito](https://youtu.be/Q60EzlZ2CGA) **Lesson 20:** [Stubbing asynchronous methods with mockito](https://youtu.be/tZEoQmXG_mQ) **Lesson 21:** [Verifying interactions with mockito](https://youtu.be/CQZWc7-ljn4) **Lesson 22:** [Using golden files to verify pixel-perfect widgets](https://youtu.be/_G6GuxJF44Q) **Lesson 23:** [Collecting code coverage from your tests](https://youtu.be/xEFAiE0IB34) **Lesson 24:** [Running integration tests with flutter_driver](https://youtu.be/HEovdrFCJUA) **Lesson 25:** [Taking automated screenshots of your application](https://youtu.be/cx_KJWZvLC8) **Lesson 26:** [Running your tests on GitHub Actions](https://youtu.be/JQxuQDqfIMg) **Lesson 27:** [Using Codemagic for your CI/CD needs](https://youtu.be/n2EsTRz1RZw) ### Ready to learn more? [**Read our Very Good Guide to Flutter Testing.**](https://verygood.ventures/blog/guide-to-flutter-testing/) ### Flutter testing: A very good guide [10 Insights] Source: https://verygood.ventures/blog/guide-to-flutter-testing/ Testing is an important aspect of app development. Since Flutter provides so many types of tests (unit, widget, etc.), it can be hard to know where to begin. We’re writing this very good guide to provide an overview of our Flutter testing process and strategy at [Very Good Ventures](https://verygood.ventures/).  We work with many different companies of varying sizes and industries. This allows us to take all of the learnings from different projects and use them to continually define and refine our engineering process. Testing is one key area that is critical to our mission of delivering [high quality scalable apps](https://verygood.ventures/blog/scalable-best-practices/).  We’ve learned that tests can be a powerful tool to reduce risk, increase confidence in any codebase, and keep development teams on the same page by testing current expectations and assumptions. Testing is crucial for us to ensure that the code we write now will still function properly no matter how many features we add or how many developers we onboard to the project.  Scroll on to read 10 insights about why Flutter testing is important and resources for those looking to strengthen their testing strategy. ## 1. Learn about all of the Flutter test options available! In basic terms, a test is a piece of code that verifies the intended behavior of another piece of code. It can be as simple as testing that the output of a function or operation matches the expectation. For example, here is a simple test:  ```dart import 'package:test/test.dart'; void main() { test('1 + 1 = 2', () { const expected = 2; final actual = 1 + 1; expect(actual, equals(expected)); }); } ``` When it comes to building Flutter applications, there are many kinds of tests that each have their own use cases, so we created this handy chart to provide a quick overview:  table { width: 100%; border: 1px solid black; border-collapse: collapse; } th { padding-top: 12px; padding-bottom: 12px; padding-left: 12px; text-align: left; background-color: #13b9fdff; color: white; border: 1px solid black; } td { padding: 8px; text-align: left; border-top: 1px solid #dee2e6; border: 1px solid black; } td:first-child { font-weight: bold; text-align: left; } Test Type Definition (What It Tests) When Best Used Unit \- A function/method/variable in isolation \- Is a pure Dart test, no Flutter dependency \- Testing contract and business logic \- Quick feedback for developers to improve code confidence Widget \- A single UI Component \- The output is the widget sub-tree, not the rendered widget itself \- Testing UI in isolation \- Can include a broader feature but need to be contained Integration \- An end-to-end experience with mocked dependencies \- To find business logic breaks in a client End-to-End \- An end-to-end experience with a real backend and/or hardware \- Is a “black box” \- Attempting to validate the full end-user experience Golden \- The “pixel-by-pixel” spec \- Once you have widget tests and UI is locked down ## 2. There is a perfect formula for writing a good test: set up + side effect + expectations ## A good test will always follow this same structure and test one discrete thing. If written correctly, for any given input, a test will always produce the same output. Overall, writing a test should be a predictable and arguably boring process (check out our [blog on boring codebases here](https://verygood.ventures/blog/boring-code-part-1/)). ### Here are some signs to know you’re writing a bad test: - The goal of the test is unclear - The test has more than one reason to fail (tests should focus on one discrete behavior) - The test contains artificial delays - The test has hidden or internal dependencies - There are no assertions or expectations - The order in which a test is executed affects whether it will pass ## 3. Aim for 100% test coverage. There are many debates about the importance of test coverage and what the ideal threshold should be. We understand that code coverage is not a perfect metric; just because you have 100% test coverage does not mean your code is 100% bug-free or 100% perfect or works 100% of the time or that tests cover 100% of possible scenarios. It simply means that 100% of lines of code are exercised by one or more tests. This also does not mean that every single line of code needs a purposeful test — something as simple as a getter or setter function may be covered by another test.  Consistently enforcing a 100% coverage threshold is extremely important when it comes to building applications because it ensures that any future code added will 1. Require a test and 2. Won’t break existing tests. This doesn’t solve all problems, but can give some level of confidence to the quality of the codebase. 100% test coverage can also ensure that all logical branches within the codebase are accounted for, and thus, provide insight into developer intent — whether it’s a teammate later that day, you in a couple of months, or a client years down the line.  We created the GitHub Action [Very Good Coverage](https://verygood.ventures/blog/very-good-coverage/) specifically for the purpose of measuring and enforcing 100% code coverage. We also want to note that we exclude generated code from our coverage calculation, because our primary concern is testing code that our own engineers have written. Generated code should be tested independently as part of the package’s test suite. As a side effect, code coverage is also a good indication of unused code — oftentimes, code coverage reports will show methods or classes that are unused and can be removed.  ## 4. Tests are crucial for development teams. ## Tests empower teams to build with confidence and can help them grow and scale. With every pull request, contributors should include tests to demonstrate that their contributions will not affect others’ code. This keeps everyone on the team accountable and has the added value of providing confidence that you’re not breaking something when contributing to the codebase.  Writing tests with each pull request makes it every developer’s responsibility to maintain code quality, instead of placing the burden on the developers in charge of testing. Tests can boost productivity, teach good habits, and enforce predictable behaviors — which is crucial for companies looking to scale their apps.  ## 5. Automated tests should be used in addition to manual testing.  ## Automated tests don’t solve all of the problems — there are always some behaviors or integrations which are best tested with real world users on physical devices. For these cases, a combination of manual tests as well as automated integration tests are extremely valuable. Together they can further increase confidence that code is working correctly for the product.  Manual tests can be expensive and time-consuming, which is why they should be used sparingly and strategically. For teams that start off exclusively relying on manual testing, the product can evolve and reach a critical point where manual testing can take weeks, sometimes without a high level of confidence. It might make sense to bring in manual testing after a code freeze and just before a release. Before deciding to rely on manual testing, you should always ask the question: *can this be automated?* If so, how much effort would that be? Automating tests pays off over time and can reduce the reliance on manual testing down the line, saving time, stress, and improving the overall quality of the product.  ## 6. Automated tests should be baked into the development process. Some developers may avoid writing tests because they take additional time to write and maintain. They may even be the first thing to go when a developer or team wants to move fast. However, investing time in writing tests now can be critical in relieving pressure and eliminating the last minute scramble to fix a showstopper bug before shipping an app.  This is why we advocate for writing tests alongside writing features — not as a completely separate effort when preparing to ship an app — the more you get into the habit of testing as you go, the more confidence you will have in the quality of your codebase. *Ideally your product should be shippable at any point and tests can help you get there.*  There has never been a better time to start testing than right now! As with learning anything new, there may be a learning curve if you’re just starting to bolster your test coverage, but with practice, writing tests will become a natural extension of the development process. You will soon find that writing tests is a positive feedback loop. Start by establishing a current baseline, whether that’s 10% or 80% and try to increase gradually over time, while preventing regressions. A tool like [Very Good Coverage](https://verygood.ventures/blog/very-good-coverage/) can help you measure and stick to this threshold. Once you reach 100% coverage, your work is not done! You can continue to look for opportunities to test edge cases and more complex scenarios. In addition, any time you find a bug, the fix should always be accompanied by one or more tests to ensure the bug never surfaces again. ## 7. Writing tests can make the code review process easier. Internally, tests can greatly help with manual code reviews. When working on a team, everyone should be writing tests and submitting them with PRs (again, tests should be *baked into* the development process, not written later). This is especially beneficial for new teammates or junior teammates who are starting to familiarize themselves with the codebase and will likely be the future maintainers of the project. Beyond setting a good example, having comprehensive test coverage can serve as an educational resource because tests should communicate intent. Scanning tests for a feature or specific part of a codebase oftentimes gives a good overview of what a feature does and how it’s intended to function. Since it’s all code, at the end of the day, teammates can use their IDE of choice to tinker with implementation and see how their changes impact the tests. If possible, we recommend assigning two (or more!) designated code owners to a project so that they can be automatically added as reviewers before code gets merged. You can even get more granular and assign code owners for specific subdirectories of a project. This way, if someone on your team is an expert or a designated leader of a specific part of the codebase, they can automatically be added as a reviewer when changes occur to it. Over time, as testing becomes ingrained into team process and culture, PR review becomes much more predictable, reduces back-and-forth between reviewers and the author, and overall speeds up the development velocity. ## 8. Tests should only fail when there is a bug in the code or a product requirement changes. Automated testing can reduce pressure on developers and place the pressure on the system and the team. *A good mentality to have is: if a bug is introduced, it’s not an individual’s fault, it’s the system’s fault.* To account for it, the system (automated test suites and any additional tooling such as linter, formatters, performance benchmarks, etc.) should be updated or enhanced to ensure that the same bug never happens again.  If a requirement changes, developers will need to update tests accordingly. Developers should feel empowered to discuss requirements when tests fail. For example, if a test fails, you should ask yourself: did I truly introduce a bug, or is the requirement unclear, or can the test be improved? Sometimes, this is a sign that planning was imperfect or the testing infrastructure has gaps. In any case, it’s a moment to reflect and decide the best path forward as a team. Without tests, these moments of reflection might otherwise not happen and could further decrease quality of the code as well as introduce ambiguity in requirements and intended behavior. Ultimately it’s important to have individuals feel empowered and responsible to deliver a high quality product, rather than offloading that responsibility to a dedicated testing team. ## 9. Tests save companies time and money. ## If writing tests is extremely time-consuming or difficult, that could be a sign that the underlying implementation is either poorly architected or implemented. It’s important to identify this as early as possible and address the root cause of the problem before building on top of a shaky foundation. An untested codebase simply cannot scale and may also mean more bugs and problems in the future. This is why tests are extremely important to write as you’re building your codebase. They can save teams time and money; from our experience, one of the reasons for deciding to do a rewrite is simply lack of confidence and stability due to limited or no tests. It can be more expensive and time-consuming to rewrite a project from scratch than it would be to incorporate testing as part of the development process. ## 10. Never stop learning! There are many great resources for writing tests. ## If you want to learn more about Flutter testing, we recommend the following: - Our teammate Jorge Coca developed an amazing comprehensive course called Testing Fundamentals of Flutter. Originally available on Caster.IO, the full course is now available in this [YouTube playlist](https://www.youtube.com/playlist?list=PLprI2satkVdFwpxo_bjFkCxXz5RluG8FY). - Another great resource is [Reso Coder’s video series on Test-Driven Development](https://resocoder.com/flutter-clean-architecture-tdd/). - For code that crosses into native platform code, we mock the generated platform interface instead of touching a real device. See [Flutter Pigeon: type-safe platform channels](/blog/flutter-pigeon-type-safe-platform-channels/) for how we define and test that boundary. - Finally, if you use our teammate Felix Angelov’s [flutter_bloc](https://pub.dev/packages/flutter_bloc) library for state management, check out [bloc_test](https://pub.dev/packages/bloc_test), a Dart package that makes testing blocs and cubits easy.  *Felix Angelov and Jorge Coca contributed to this article.* ### Our favorite GitHub Actions for Flutter apps Source: https://verygood.ventures/blog/github-actions-for-flutter-apps/ GitHub Actions are somewhat underutilized tools that can supercharge app development. If you haven’t used them before, we highly recommend that you do so, especially with open source projects (since they have unlimited build minutes). With just a few lines, you can automate tasks that you would otherwise have to do manually with each build, save some time, and prevent errors! Our favorite GitHub Actions to use with Flutter app projects are: 1. [**Flutter GitHub Action**](https://github.com/marketplace/actions/flutter-action)**:** Gives you access to the Flutter command line so that you can format, analyze, and test your flutter code. It also allows you to specify which version and channel of flutter you want to use so that you can run your tests against the stable, beta, dev, or master channels. 2. [**Upload Artifact**](https://github.com/marketplace/actions/upload-a-build-artifact)**:** Upload coverage reports and visual architecture documents to GitHub. We use this to upload a visual representation of the app architecture, test coverage reports, and other important docs we may want to view after a workflow has run. 3. [**Very Good Coverage**](https://github.com/marketplace/actions/very-good-coverage)**:** We made this one to keep track of our test coverage and ensure that we’re not slipping below our 100% threshold. You can specify any  threshold up front to ensure that your coverage never dips below your desired limit. Check out how we built it in [this blog post](https://verygood.ventures/blog/very-good-coverage/). 4. **Simple cspell:** We use this one to run a quick spell check on builds. Unknown words are displayed as annotations so you can review and update any spellings accordingly. Don't worry, you can whitelist any unique words so they don't get flagged. ## How to use GitHub Actions First, view the [full documentation](https://docs.github.com/en/free-pro-team@latest/actions) for GitHub Actions. Once you’re ready, specify a workflow yaml file with the actions you want to use, then build and push changes as usual — it’s that simple! Beyond these four, project-specific checks are easy to add. One we rely on regenerates Pigeon's code on every run and fails the build when the committed output drifts from the contract, which we cover in [Flutter Pigeon: type-safe platform channels](/blog/flutter-pigeon-type-safe-platform-channels/). ### Enforce 100% Code Coverage with Very Good Coverage Source: https://verygood.ventures/blog/very-good-coverage/ At Very Good Ventures, we are passionate about writing tests for our codebases — in fact, we strive for 100% test coverage on all of our projects. To ensure that we can maintain this threshold with any new lines of code that we write, we created [Very Good Coverage](https://github.com/VeryGoodOpenSource/very_good_coverage), a GitHub Action that supports multiple operating systems to enforce a code coverage threshold.  By adding the below lines to the [GitHub Actions workflow](https://docs.github.com/en/free-pro-team@latest/actions/quickstart), you can enforce a minimum coverage threshold of your choosing (we recommend sticking with the default setting of 100%, but feel free to change to a number that best works for you and your project). You can also exclude files, such as generated code, from the calculation. ```yaml uses: VGVentures/very-good-coverage@v1.1.1with: path: "./coverage/lcov.info" min_coverage: 95 exclude: "**/*_observer.dart **/change.dart" ``` We highly recommend adding Very Good Coverage to new and existing projects to ensure all code contributions are being tested.  - **For new projects:** We recommend keeping the default min coverage of 100% for maximum confidence that every single line is covered.  - **For existing projects:** Generate an [lcov coverage report](https://sourceforge.net/directory/mac/?q=lcov), then use the current coverage from that as the minimum coverage for Very Good Coverage. This will ensure that the code never drops below the existing percentage. Each sprint, try to make it a goal to gradually increase the percentage until you reach 100%! - ## How We Built It: Four Nights of “Live” Coding If you want to see exactly how we built Very Good Coverage, we recorded the entire process from planning to publish. In early October, we set out to create a [series of #VeryGoodOSS videos](https://www.youtube.com/@VGVentures/streams) for the [Chicago Flutter Meetup Group](https://www.meetup.com/Flutter-Chicago/) that would showcase our love for the open source community and premiere during [DigitalOcean’s Hacktoberfest](https://hacktoberfest.digitalocean.com/). While things did not go as planned at times (we initially started using TypeScript but then pivoted to JavaScript because of a lack of type definitions for the underlying npm package and to keep things beginner-friendly), we kept most of the footage unedited. The result was four coding sessions that while not technically live, still showcase the real-time process of building upon an open source codebase. We compiled all of the videos in this [Very Good Open Source Playlist](https://www.youtube.com/@VGVentures/playlists)! A recap of what we accomplished each day: - **Day One:** Planned the scope of the project and the tasks we needed to complete. - **Day Two:** Recapped some of our off-screen activity where we pivoted to using JavaScript instead of TypeScript; then we dove into the code and began building. - **Day Three:** Continued building functionality. - **Day Four:** Cleaned up the code and published the package.  While lcov-cop originally lived under the Chicago Flutter organization, we shifted it to [Very Good Open Source](https://github.com/VeryGoodOpenSource) because we use it a lot on internal VGV projects and knew it would be easier to maintain there. Check out the Very Good Coverage GitHub Action in the [GitHub Action Marketplace](https://github.com/marketplace/actions/very-good-coverage) and all of our open source libraries at [Very Good Open Source](https://github.com/VeryGoodOpenSource). *Felix Angelov and Jorge Coca contributed to this article.* ### Best Practices for Building Scalable Flutter Applications Source: https://verygood.ventures/blog/scalable-best-practices/ When talking with clients, we often hear them say that their app needs to be scalable so that it can handle the growth of new users. Others say that their app needs to be scalable to handle new features and remain stable. We hear the word so often in different contexts that we thought we would write a blog post to discuss exactly what scalable means to us in the context of Flutter app development and how to achieve it. Scalable apps should be able to do the two things that we hear most from clients:  1\. Handle an influx of new users  2\. Support new features without breaking.  But that’s not all! **In order to be truly scalable, your Flutter app should also be easy to test and debug and have a composable structure that is easy to understand for all engineers working on it.**  In this blog, we discuss what we do to build scalable Flutter apps. After all, focusing on scalable architecture leads to huge benefits that saves engineering teams headaches and ensures that companies can consistently deliver high-quality apps and updates to their users. ## Why Build Scalable Code **Scalable architecture is arguably the most important consideration when building an app because it is essentially an insurance policy for your code.**  Imagine a world where the engineer who has been around since day one has all of the knowledge about the codebase in their head with no documentation in sight. Not that far-fetched, right? We’ve probably all encountered a situation like this — or done it ourselves.  This may be an OK solution when things are going well, but the second something breaks, you have to hop on a zoom call with the person and could spend hours tracking down the problem. If that person ever leaves the company, the team loses all of that valuable knowledge and the codebase will be hard to maintain.  **The reality is: your codebase will not survive if one person has all of the knowledge in their head.** That information needs to be documented and built into the architecture so that every member on the team knows how to build new features in a way that will not break the existing infrastructure. *Teams need to be able to scale their knowledge.* **Scalable code can also help you avoid dreaded spaghetti code.** Many developers have a horror story about a legacy codebase where deploying any new code meant breaking some other part of the codebase. Tracking down the problem only led to more issues and it could end up taking longer to find and fix the issue than it did to build out the feature. This does not have to be the case!  Scalable code ensures that you have the right architecture in place to be able to add new features to an application without breaking the rest of it. It also makes opening pull requests and merging them less scary because you can be sure that what you’re contributing will not introduce new problems. *Teams need to be able to scale new features.* ## How to Create a Scalable Architecture There are four qualities we strive for when creating scalable code. ### 1. Scalable code is well-tested *If you’re only testing once the app is complete, you’re doing it wrong!* Testing should be baked into the development process. There is a huge risk associated with relying only on manual testing right before a scheduled release. Oftentimes critical bugs and stability issues surface, resulting in either a poor quality release or a postponed release date. You should be able to write tests and reach 100% code coverage of the codebase. Some teams may opt for a lower threshold than 100%, but keep in mind that any part of your code that is left untested could lead to problems. A 90% code coverage threshold may sound high, but for a project with 50,000 lines of code, this means that 5,000 lines of code — a significant amount! — are untested. We created a [GitHub Action specifically to measure this](https://github.com/VeryGoodOpenSource/very_good_coverage) and help enforce a designated code coverage threshold. If it is impossible to reach a high level of code coverage, this is a sign that your code needs to be refactored in such a way as to make testing possible. **Untested code is simply unscalable; if some part is left untested, you’re opening up the door to potential bugs or having your app not work the way it was intended.** For a comprehensive resource on testing, our teammate [Jorge Coca developed a Caster.IO course on testing in Flutter](https://verygood.ventures/blog/testing-fundamentals-of-flutter-course/). ### 2\. Scalable code is easy to debug Scalable code should be easy to debug because if you have thorough tests, the likelihood of bugs being introduced is very slim.  If bugs do surface, tests you’ve written will likely fail. This can help you isolate the exact part of the code that is causing the issue in a matter of seconds and before the bug impacts end users. If the tests don’t fail and there is still a bug, that’s an indication that once the bug is identified and fixed, a test should immediately be added to ensure that bug never happens again. Having built-in debuggers and widget inspectors can also make debugging that much easier. Flutter’s built-in tooling provides performance profiling and networking devtools which will help developers spot bugs quicker and then write tests to catch future ones. ### 3. Scalable code is boring & easy to understand Codebases should be structured in such a way that any developer of any level — from junior engineers to senior level architects — should be able to understand the logic. In short, you should have a [boring codebase](https://verygood.ventures/blog/boring-code-part-1/). You can achieve this through the following: - **Avoid custom code whenever possible.** Scalable code should have very few custom solutions. For example don’t invent your own networking layer if you don’t have to. There are already existing solutions that address this, such as [Dio](https://pub.dev/packages/dio) or [GraphQL](https://pub.dev/packages/graphql). Getting started on a new codebase should not take hours and should rely on existing tooling where applicable.  - **Select and stick with one state management solution.** There are already plenty to choose from so there is sure to be one that fits your business needs. Our preferred solution is [flutter_bloc](https://bloclibrary.dev/#/) because it is predictable and reliable (and it also doesn’t hurt that the creator is a team member here at Very Good Ventures).  - **Write clear documentation.** When writing documentation, keep in mind that you may be the engineer who might be looking for context weeks, months, or years from now. Write documentation in such a way that if you were to come back to the code in the future, you would be able to jump right back in again, or at least be able to understand the intended function of each portion of code. - **Ensure consistency.** Your team should be on the same page when it comes to naming variables, functions, and classes. The codebase also should conform to pre-existing standards whenever possible, including following the [Effective Dart](https://dart.dev/guides/language/effective-dart) style guide and using popular packages when it makes sense. Check out [very_good_analysis](https://pub.dev/packages/very_good_analysis) for our recommended lint rules and analysis options. ### 4. Scalable code is composable Scalable applications all have this one thing in common: they should be composed of smaller, independent packages or modules.  This will ensure that each package has a single responsibility and can be tested and reused. This also ensures that engineers can work on the codebase in parallel without interrupting the work of other members.  Each package should also abstract underlying implementation details, which will help development teams iterate efficiently without needing exposure or experience with complex underlying topics. For example, if you’re building a feature and need to access transactions, there should be a library in the codebase that exposes an API to fetch transactions without forcing you to know where and how to load transaction information every single time it is needed.  We typically compose codebases into the following layers: - **Data acquisition:** Create data provider packages that fetch raw data from various data sources (such as third party APIs, databases, etc.). - **Business rules:** Create domain packages that interact with one or more data providers and apply business rules to them. - **Application itself:** This will consist of two parts: 1. UI what the user sees and how the user interacts with the application. 2. Application logic, which are application-specific rules, or what you might think of as state management. Feel free to experiment with adding additional layers as needed. We have found that having these [three layers](https://verygood.ventures/blog/boring-code-part-1/) gives us flexibility to adapt to changing project requirements without having an excessive amount of overhead — the very definition of scalable.   ## Who should build scalable code? Enterprise, midsize companies, and startups—in short, everyone! Scalable codebases should be the standard in the mobile application industry, but we wouldn’t be writing this article if this were the case. We hope that this provides some insight into how we ensure that all of the Flutter apps we work on are scalable — and can grow and adapt with our clients’ businesses, not trigger a rewrite six months from now. If you’re worried that your application’s codebase is not scalable, there’s no time like the present to start implementing habits that will put you on track. For example, if your code coverage is not at 100%, set a goal to raise your current threshold by ten percent within the next couple of weeks. It’s all about implementing good, scalable habits now so that your codebase becomes more and more stable over time. If you’d like to talk further about Flutter application codebases, [contact us](https://verygood.ventures/contact-us/)! We are passionate about writing scalable code and want to help you do it too. *Felix Angelov and Jorge Coca contributed to this article.* ### Highlights from eBay Motors' Flutter article Source: https://verygood.ventures/blog/ebay-motors-flutter-experience/ Earlier this week, [eBay Motors posted about their experience building out an Android and iOS experience for buying and selling vehicles on eBay.](https://innovation.ebayinc.com/stories/ebay-motors-accelerating-with-fluttertm/) As we read, we found ourselves nodding along in confirmation about their experience using Flutter — that it is faster, makes developers happier, and is the best cross-platform mobile development solution available. Here are some of the moments that resonated with us: ## *"Our first beta was in our CEO’s hands within three months of receiving our first product requirements, and a few months later we released our app to the public."* We are constantly amazed at how production times can shrink to a matter of weeks with Flutter. Ask any developer using Flutter and they will tell you that it is fast. *So fast* that teams can build out features quicker than ever before and roll out apps to Android and iOS at once, in turn speeding up the development process.  ## *"We had to take two teams, from different backgrounds with different approaches to solving problems, and bring them together into one cohesive unit."*  Flutter fundamentally changes the development process — for the better. We too have seen that companies adopting Flutter can also experience a cultural shift for teams that are used to working within discrete native silos. But powering through the adjustment period brings the immeasurable benefit of forming one cohesive engineering team focused on the goal of delivering quality software. ## *"We informally surveyed the team to help validate our decision to use Flutter, and the responses showed the majority of the team believed developing in Flutter was over twice as fast as developing for their prior platform. Perhaps more importantly, 100% of those surveyed enjoyed Flutter development more than iOS or Android."* We have also observed that Flutter drastically reduces time native teams used to spend on codebase maintenance or making sure one feature performed the same way for both mobile experiences. At the end of the day, engineers working with Flutter spend more time on the fun part of development. Flutter makes for happier and more efficient teams overall. We are impressed with the progress eBay Motors has made with Flutter in just a few months and excited to see what they build next! ### [**Read the full article from eBay Motors here.**](https://innovation.ebayinc.com/stories/ebay-motors-accelerating-with-fluttertm/) ### Introducing Very Good Analysis Source: https://verygood.ventures/blog/introducing-very-good-analysis/ At Very Good Ventures, we hold our code to high standards and we want to help you do the same!  **Very Good Analysis** is an open source package that contains lint rules for Dart and Flutter used internally at Very Good Ventures. It combines the [Effective Dart Style Guide](https://dart.dev/guides/language/effective-dart/style) and the [pedantic package](https://pub.dev/packages/pedantic) with additional rules to keep your projects in tip-top shape. ## About Very Good Analysis After working on many client projects, we realized that we were continually copying lint rules from different sources and customizing them to fit our needs. To make things more efficient and consistent, we decided to put them all into one repository and make it widely available. ### Introducing the Very Good Analysis package! ![Very Good Analysis package listing on pub.dev](/assets/images/blog/introducing-very-good-analysis/body-0.png) *Screenshot of Very Good Analysis on pub.dev* Some of the key aspects of Very Good Analysis include: - **Stricter type checks enabled:** Ensures that the Dart type inference engine does not implicitly cast or choose the dynamic type when the static type cannot be determined. - **Prefer const constructors:** Ensures that const constructors are used whenever possible for better performance. - **Prefer final fields:** When variable reassignments are not necessary, fields are marked final to ensure that they remain unchanged and promote optimization by the compiler. - **Sort constructors first:** Ensures that constructor declarations are placed above all other members to improve readability for developers. The full list of rules can be found in the [Very Good Analysis documentation](https://github.com/VGVentures/very_good_analysis/blob/main/lib/analysis_options.1.0.0.yaml). ## Very Good Analysis Markdown Badge ![Very Good Analysis badge](/assets/images/blog/introducing-very-good-analysis/body-1.png) If you use Very Good Analysis in your project, you can also unlock the Very Good Analysis badge on GitHub. The documentation contains instructions for how to add the badge to your README.md. ### Check out the [**package on pub.dev**](https://pub.dev/packages/very_good_analysis) and give it a like if you find it useful! ### *Felix Angelov contributed to this article.* ### Are you saying that my code is boring? Thank you! Source: https://verygood.ventures/blog/boring-code-part-1/ I spend a lot of time thinking about code. Good code. Code that is so easy to read and follow that is impossible for the person reading it to think “Hey Jorge, the intent of this snippet is not clear to me, could you please explain it to me? Let’s jump on a Zoom call!” Because let’s be honest, we’re all Zoomed-out.  So, that leaves me wondering: “What makes code ***good***?” 🤔 Is it how clever it is? Or how efficient it is? Or is it because I can just write a one liner to run the entire project? This question has been haunting me for a long time, until one day, it hit me: ## This codebase is boring! 💯 This ability to be able to find standard and reproducible patterns sparked one of the most interesting conversations I have ever had with an engineering team: during a retrospective session, a team member left a sticky in the column *Things that went well* that said **“This codebase is boring!”**  And boom!💥 just like that, I realized that that was the answer I was looking for: *boring code!* We work with a lot of engineering teams of all sizes: from early stage startups to big corporations, and our mentality is to always offer the same level of quality, attention to details and scalability to our partners. We aim to build apps that are scalable so that they can grow and change with the company. You would say that we have a “repetitive” model: something that we can implement efficiently over and over again. We focus on finding standard solutions and patterns that we can replicate: if we already solved a problem in a project, we can modularize it and turn it into a general package that can be used when other projects are trying to do the same thing.   ## Are you crazy!?🤪 How can *boring* code be *good* code?🤓 Most of the things that we do in a mobile application are, let’s be honest, more or less the same: fetch data from somewhere, apply some transformations, cache it, and then present it on the screen. And many of the features included in an application are also the same, regardless of their business line: create an account, authenticate, show me terms and conditions, logout the user, etc.  Producing ***boring code is the biggest compliment that an engineering team can receive***; after all, “surprises” in a project tend to be *not so good* surprises. No one likes receiving a 2a.m. emergency call, or spending 8 hours tracking down a bug that is almost impossible to reproduce and as engineers, we don’t like solving the same problem over and over again.  Having a codebase that is predictable, easy to navigate, well tested and properly automated makes it ***boring. But pleasantly boring!*** And that’s great, teams love the feeling! It lets them focus on their ***real and business-related challenges*** without the distractions of other tasks. ## How can I make my codebase boring?👩‍💻👨‍💻 From our point of view, there are two key components to make your codebase good… I mean *boring* 😉: - **Super declarative, crystal clear, zero-surprises APIs**: Each component of your project that is exposed publicly should not leave the consumer thinking “*Will this really work?*” Instead, the feedback you want them to have should be *“I can’t believe this was so easy to do!”* - **Ask yourself: *“Does this code belong here”?*** This question goes beyond the classical *“Should this code be written in the backend, or in the client?”* Within your client code, you should make a distinction between ***data acquisition*** components (network, database, GPS, bluetooth, camera), your ***business rules*** (e.g. I need to get my current location to fetch from the network all the open restaurants that serve Spanish tapas near me) and how to ***present that information*** to the user (in the case of Flutter, this is how you use your widgets and how to manage their state). With those two constraints guiding our development process, we could easily imagine any application in the world being modeled by those principles. For example, a food delivery app could be decomposed into components like this: ![Components of a boring code base](/assets/images/blog/boring-code-part-1/body-0.png) *Components of a boring code base* The only parts of this application that should be unique are the business rules and the presentation components: after all, it is in the business rules where products offer ***value***, and it is on the presentation side where products are ***pleasant*** to use. Data acquisition components, however, could be reused by many applications over and over again.  This approach can be applied to any Flutter application, from the simplest example you can think of to the enterprise platform with millions of users. We’ve repeated this pattern ***over and over again,*** so many times that it is a bit *boring* to apply now! 🥳 And we are so happy to have found boredom in development, because now our teams can execute efficiently, be predictable, and there are no more surprises! ## We want to help you be *very* *boring!* Part of our core values is to share with the community the challenges we already faced and solved so we can all keep working on more exciting and complex challenges. We are planning to release a series of articles detailing how we turn our code into ***good, boring code.*** Stay tuned! ### What Amazon AWS support means for Flutter Source: https://verygood.ventures/blog/amazon-aws-flutter-support-announcement/ Last week, Amazon announced AWS support for Flutter. With the [Amplify Flutter](https://docs.amplify.aws/start/q/integration/flutter) framework, developers can now access cloud computing capabilities within Flutter apps. *AWS gives developers access to three main components: open source libraries to access cloud-computing capabilities, command-line tools that can access cloud data, and services to deploy and host web applications. AWS can now be seamlessly integrated with mobile applications built with Flutter.*  Following on the heels of [Google reporting that over 90,000 apps in the Google Play Store have been built with Flutter](https://medium.com/flutter/announcing-flutter-1-20-2aaf68c89c75), Amazon’s announcement is yet another indication that Flutter is gaining momentum.  ![Screenshot of Amplify Flutter documentation](/assets/images/blog/amazon-aws-flutter-support-announcement/body-0.png) *Amplify Flutter framework documentation* ## Amazon’s Confidence in Flutter The Flutter community tends gravitate toward [Google Cloud Platform](https://cloud.google.com/) — including [Firebase](https://firebase.google.com/) which is extremely powerful with Flutter apps — but Amazon is now providing another route for cloud integration. This is big news for companies with operations in China and other regions where Google cloud services may simply not be an option. Companies with operations in Europe may also be interested since [Amazon continues to serve as the most popular cloud provider there](https://www.srgresearch.com/articles/amazon-microsoft-lead-cloud-market-all-major-european-countries). **In fact,** [**Amazon is the leading cloud provider worldwide at 33% of the market share**](https://www.srgresearch.com/articles/quarterly-cloud-spending-blows-past-30b-incremental-growth-continues-rise)**.** The fact that Amazon is creating “Flutter-first” products gives us more confidence in Flutter’s future. ![Cloud Infrastructure Services Market Graph from Synergy Research Group](/assets/images/blog/amazon-aws-flutter-support-announcement/body-1.jpeg) *Cloud Infrastructure Services Market Q2 2020* **This could be the tipping point for companies using AWS.** Companies that have been on the fence about switching to Flutter will now find it easier to do so, especially if they are already using the Amazon ecosystem. Before, clients would ask us if we could integrate Flutter with AWS and we would answer: “Yes, but…” We are optimistic that soon we will be able to answer: “Yes!”  **Ultimately, the limitations to adopting Flutter are shrinking.** While Amplify Flutter is still in developer mode, we aren’t migrating major apps over just yet. For now, we are digging into the framework as we eagerly await a stable release. For more information on the AWS Amplify news, read the [announcement here](https://aws.amazon.com/blogs/mobile/announcing-aws-amplify-flutter-developer-preview/). *Jorge Coca contributed to this article.* ### My Internship at Very Good Ventures: Sahit Source: https://verygood.ventures/blog/sahit-intern-experience/ As a freshman in college, I knew my first summer would be an important one. I knew that how I spent my time would set the foundation for the remainder of my college experience and future career. Having developed mobile apps in the past, participating in hackathons throughout all of high school, and volunteering my time as an Android Developer on campus, I was a programming enthusiast. So, I wanted to spend this summer gaining professional experience as a software engineer. ## Discovering Very Good Ventures When I came across [Very Good Ventures](https://verygood.ventures/), I was immediately interested. The company was innovative in its adaptation of multi-platform app technology using [Flutter](https://flutter.dev/) and very entrepreneurial in their pursuits. As an aspiring mobile app developer myself, I knew this would be a great place to grow as a developer and entrepreneur. After reaching out to [David DeRemer](https://medium.com/@deremer), VGV’s president, and interviewing with [Albert Lardizabal](https://medium.com/@al_vgv), a Principal Engineer on the team, I was extended an offer and was excited to begin. ## Learning Stage I spent the first month of my internship learning the Flutter framework and working on personal projects. During my first week, I met with my supervisor, Albert Lardizabal, to discuss my goals and to create an action plan for the summer. After figuring out the gaps in my knowledge, we decided that my priorities would be to learn state management architectures (such as the [Bloc Library](https://bloclibrary.dev/#/)), continuous integration/development practices (using [Codemagic](https://codemagic.io/start/)), and [testing](https://flutter.dev/docs/testing).  I spent the first few weeks researching these topics and creating sample apps to consolidate what I learned into tangible projects. My first personal project was a Flutter Playground application that is meant to be a testing ground for various Flutter widgets. [The full repository can be found here](https://github.com/sahitpen/flutter-sandbox).  My second project was a news application that allows users to search for stock-related news for various companies. [The full repository can be found here](https://github.com/sahitpen/stock-news-app).  While building these projects, I had biweekly code reviews with my supervisor to assess my code quality. These meetings were very helpful for learning how to write clean code that scales easily.  ## VGV Projects During this time, I was also helping with VGV projects. On June 27th and 28th, VGV helped host the world’s largest Flutter Hackathon - [Hack20](https://flutterhackathon.com/#/). Leading up to the day, the team needed help testing the website they built as well as marketing the event. I ran tests on the website to detect bugs, wrote social media posts, and contacted Flutter influencers to help spread the word. [Read more about Hack20 in our blog post here](https://verygood.ventures/blog/hack20-the-worlds-largest-online-flutter-hackathon-goes-virtual/). ## Client Projects During the second month of my internship, I became involved in VGV’s client work. The first client I worked with was a lottery corporation who was looking for help with their mobile application. My role was to research security solutions for their app and help determine which SDKs suited their needs.  The second client I worked with was Google’s Flutter Team. I was responsible for providing technical support for developers on one of their Flutter Discord servers and writing weekly reports for them. The third client project I worked on was by far the most involved. I worked with a financial services company to help build their mobile app from the ground up using Flutter. I was involved with implementing user registration features, banking features, and writing end-to-end tests. Working with clients was an incredibly rewarding part of my internship. It enabled me to explore various types of work, and it taught me to be adaptable. In addition, it allowed me to make real, palpable contributions that will potentially affect millions of customers around the world, and to me, that’s the most fulfilling part of this career. ![Github contributions graph with spike from May to August](/assets/images/blog/sahit-intern-experience/body-0.png) *The spike in my GitHub contribution activity from May to August!* ## Going Virtual When I initially received an offer in January, the plan was for my internship to take place in VGV’s New York office. However, once the pandemic hit, this was no longer the case. While I was initially a bit worried about how a virtual internship would go, my doubts were cleared almost immediately after I began working. The VGV team members created an inclusive and supportive virtual environment, and I am immensely grateful for all the mentorship I received.  ## Conclusion My 12 weeks at VGV allowed me to grow both professionally and personally. I’ve become a more experienced developer, studied the product management aspect of projects, and learned how to maintain relationships with both clients and teammates. Most importantly, I’ve gained a new sense of what this career path entails, and it’s something that I love.  *Sahit Penmatcha is a rising sophomore at the University of Pennsylvania, pursuing a dual degree in Computer Science and Economics. He worked as an Engineering Intern at VGV in Summer 2020.* ### #Hack20: The world’s largest online Flutter Hackathon goes virtual Source: https://verygood.ventures/blog/hack20-the-worlds-largest-online-flutter-hackathon-goes-virtual/ ## The Flutter Community inspires us every day, and this year’s community hackathon was no exception. Following Google’s very first Flutter Day, we helped organize [#Hack20](https://flutterhackathon.com/#/), the world’s largest Flutter hackathon — this time entirely online on June 27-28. While we were disappointed that we couldn’t gather with the community in person, we were heartened by the turnout and enthusiasm as developers across the world logged on to build some awesome projects. > *Over 2,400 participants joined #Hack20 and collectively created 250 Flutter projects to address this year’s themes: saving the planet and building a cyberpunk future.* In addition to serving as one of the many sponsors of the hackathon, we at Very Good Ventures thought it was important to contribute our time and developer skills to the event as well. Members from our team collaborated with [Google Developer Experts](https://developers.google.com/community/experts) that lead Flutter Communities around the world to organize the event — including building a registration, submission, and voting system in **just two weeks!** Accomplishing these tasks was not easy — from getting the word out, to building a website (using [Flutter for web](https://flutter.dev/web), of course), to dealing with changes on the fly — but it was certainly worth it. Here’s how we banded together to pull it off: ## Gathering the Flutter Community Online *The best hackathons are collaborative spaces to learn, meet new developers, and build exciting products. We were committed to achieving this level of interaction virtually for the Flutter Community during Hack20.* We scheduled six live stream sessions, including the [Hack20 Opening Ceremony](https://www.youtube.com/watch?v=mL_6PRfMW8Y) to go over this year’s themes, guidelines, and voting process. The other sessions ranged from [Flutter tutorials](https://www.youtube.com/watch?v=OPwZH-2UGw0) to [Q&A sessions](https://www.youtube.com/watch?v=AomFW87loBU) so that developers of all skill levels would be able to participate and create along with the community around the world. Although it was a challenge to figure out how to coordinate the live streams with multiple speakers in different locations, we wanted to provide the opportunity for developers to gather together online. We used [StreamYard](https://streamyard.com/) for the speakers and the [Flutter Community YouTube channel](https://www.youtube.com/channel/UCNUzIz3TsiHSbgn_66kLIww) to broadcast the streams. ![Screenshot of the Hack20 Opening Ceremony on the Flutter Community Youtube Channel](/assets/images/blog/hack20-the-worlds-largest-online-flutter-hackathon-goes-virtual/body-0.png) *Screenshot of the Hack20 Opening Ceremony on the Flutter Community YouTube Channel* ## Providing Support via Slack *We provided 24-hour support to ensure that developers could focus on creating.* We also designated team members to moderate a handful of Slack channels for those seeking help over the course of the hackathon. The volume of queries was a bit overwhelming at times, but we did our best to coordinate answers and technical support. We also used these streams and channels to remind participants about submission instructions and the voting process so that everyone could be on the same page. ## Community Voting *The hackathon wouldn’t be complete without the community choosing the winners!* We wanted to ensure that everyone had the chance to vote on their peers’ work. We asked all teams to submit a video highlighting their project, then developed a process for participants to rate randomly selected projects. After narrowing down the projects to the Top 20, we had participants vote again to determine the Top 3 Winners! ![Hack20 Top 3 Winners](/assets/images/blog/hack20-the-worlds-largest-online-flutter-hackathon-goes-virtual/body-1.png) *Hack20 Top 3 Winners* ## What’s Next We are incredibly thankful for all the developers that showed up to Hack20 and built creative, thoughtful, and cyberpunk projects. We especially loved hearing all of the heartwarming stories from participants — such as a father-son team who bonded over Flutter and young developers building their first apps. We consider this year’s hackathon a success! If you’re already looking forward to next year’s hackathon, you’re not alone! If we are fortunate enough to host in-person meetups for Hack21, the website can be refactored to streamline voting, host livestreams, and keep everything organized. But if we need to host online again, we’ve got the hackathon dashboard ready! [Check out this year’s Hack20 submissions and the hackathon dashboard here.](https://flutterhackathon.com/#/projects) *Sahit Penmatcha contributed to this article.* ### Flutter Interact recap Source: https://verygood.ventures/blog/flutter-interact-recap/ ## 11 Days of Flutter - Day 11 For our final day of the “11 Days of Flutter”, we present to you this recap of the main event — [Flutter Interact](https://flutter.dev/events). Be  sure to check out [all of the posts in the “11 Days of Flutter” series](https://verygood.ventures/blog-categories/flutter/). ## Flutter Interact Recap On December 11, 2019, New York City hosted Google’s annual Flutter conference — Flutter Interact! Everyone from “Flutter fledgling” to “Flutter famous” tuned in to see the latest innovations involving the Flutter framework. (All of the keynote presentations can be reviewed here). There are plenty of recaps out there, but here are the key announcements that caught our eye here at Very Good Ventures. ![Flutter Interact 2019 conference stage and key announcements](/assets/images/blog/flutter-interact-recap/body-0.png) ## Flutter 1.12 Flutter 1.12 has officially been announced along with a [list of new features](https://flutter.dev/docs/development/tools/sdk/release-notes/release-notes-1.12.13). Accompanying the newest Flutter release is also a new and improved Flutter Gallery App to view all the latest additions in action. On the iOS side, a number of modifications have been made to the Cupertino widgets. Some of the newest updates include appearance upgrades to both the CupertinoSwitch and CupertinoSlide widgets, CupertinoThumbPainter now allows for even further customization, and improvements to the CupertinoTimePicker. Possibly the highlight iOS announcement was the official dark mode support for Cupertino components. On the Android side of things, all new Flutter projects now come with AndroidX support, so no need to troubleshoot AndroidX error messages. Current app maintainers considering switching over to Flutter will be happy to learn that Add-to-App is officially supported. That means you can take your current existing native app, either iOS or Android (or both), and add your very own Flutter module/screen/feature. Flutter transformation has never been more accessible. ![Flutter 1.12 release highlights including Cupertino updates and Add-to-App](/assets/images/blog/flutter-interact-recap/body-1.png) ## Google Fonts Plug-in The Flutter team also announced a [Google Fonts plugin](https://pub.dev/packages/google_fonts) for flutter that allows you to use your preferred font directly in dart code. For many, this means the end of importing font files and adding them to your pubspec.yaml. Yet another breakthrough to increase efficiency and productivity. ![Google Fonts plugin for Flutter showing font usage in Dart code](/assets/images/blog/flutter-interact-recap/body-2.png) ## Dart 2.7  The Dart team announced the stable release for Dart 2.7. It’s been a busy year for Dart with a recent GitHub Octoverse report listing Dart as the #1 fastest growing language The forefront feature of Dart 2.7, adding unbeknownst power to the Dart language is the addition of Extension methods. These enable the developer to add functionality to any type, then call upon those methods with the autocomplete experience and speed. ![Dart 2.7 release featuring extension methods and fastest growing language](/assets/images/blog/flutter-interact-recap/body-3.png) ## Flutter for Web (now in Beta!) Flutter for Web was first released during the beginning of May, just months later it can now actively be used on beta. Plugin capabilities for Flutter web are rapidly growing as well as available features. Page routing, native text controls, mobile accessibility, mouse and hover events are just some of the latest features. While this is a graduating moment for Flutter web the journey to a stable release still persists. ![Flutter for Web beta announcement with new web capabilities](/assets/images/blog/flutter-interact-recap/body-4.png) ## Flutter for macOS Desktop support for macOS is available in the alpha channel. Major improvements have been made to keyboard accessibility, and you can even build the new Flutter Gallery app as a desktop app. We were particularly proud to see our game, Dash Dash (a highlight of the Very Good Party the night before) included as part of the keynote! ![Flutter for macOS alpha including the Dash Dash desktop game demo](/assets/images/blog/flutter-interact-recap/body-5.png) ## Flutter Octopus One of the most impressive demos of the keynote was when they displayed Visual Studio Code code debug, which allows the developer to run on multiple devices and platforms simultaneously. This was displayed in the form of a computer on a table connected to 7 different devices. A code change was made and using Hot Reload the change was reflected on all 7 of the devices. Not only that but you can also put a breakpoint in your dart code and track code debugging on all devices. ![Flutter Octopus demo with VS Code running on 7 devices simultaneously](/assets/images/blog/flutter-interact-recap/body-6.png) ## Hot UI Prior to the latest AndroidStudio release there has not been a way to preview your app’s UI without running a simulator or an actual device. Well, all of this has changed with the introduction of HotUI. Developers will now have an integrated tool to not only preview their UI but also make changes to it without actually having to touch the code. ![Hot UI feature in Android Studio for live Flutter widget preview](/assets/images/blog/flutter-interact-recap/body-7.png) ## Supernova Many of the keynotes during Flutter interact were focused on creating a cohesive, undying connection between designer and developer. Supernova is one of the tools to help with this integration. It allows you to generate code from Sketch or Adobe Ex UI, it’s even possible for Supernova to convert complex layers such as buttons and listviews. This not only creates a bridge between design and development but also a gateway for designers of any level to learn Flutter. ![Supernova tool generating Flutter code from Sketch and Adobe XD designs](/assets/images/blog/flutter-interact-recap/body-8.png) ## Adobe Ex Flutter plugin Along the same lines of designer-developer assimilation is Adobe Ex’s open source Flutter plugin. Which allows for direct manipulation of Dart code through Adobe XD. This plugin will generate code for any selected component in XD. Not only will it generate the code, but the designer can also make changes in Adobe XD and after running the Flutter plugin, code changes will be reflected immediately. This plugin is now available for early access, [sign up here](https://flutter.gskinner.com/xd-to-flutter/).   ![Adobe XD Flutter plugin generating Dart code from design components](/assets/images/blog/flutter-interact-recap/body-9.png) ## DartPad DartPad has recently been updated to include capabilities for compiling Flutter. This means that anyone with internet access and a web browser now has the ability to build their very own Flutter app, no IDE or Flutter SDK needed. ![DartPad updated with Flutter compilation support in the browser](/assets/images/blog/flutter-interact-recap/body-10.png) ## Rive The real - time interactive design tool previously known as Flare built by 2Dimensions have recently rebranded to Rive. At Interact Rive announced the recent addition of shadow, blur, and mask effects to the editor and showcased some open source animations displaying the additions. Their web app is open source along with some incredible animations created by their professional artists. ![Rive animation tool showcasing shadow, blur, and mask effects](/assets/images/blog/flutter-interact-recap/body-11.png) ## Flutter Clock Challenge Google is holding a challenge to see who can build a beautiful clock face UI in flutter for the Lenova Smart Clock. A panel of google experts will judge submissions based on visual aesthetic, code quality, novelty of the idea, and overall execution. There will be a grand prize of a loaded iMac Pro (valued at $10,000), 4 winners will receive a Lenovo 10” Smart Display with Google Assistant and up to 25 Winners will receive a Lenovo Smart Clock. If you’re interested full details can be found at flutter.dev/clock. Submissions are due midnight, on January 20th. ## Wrapping Up With our favorite cross platform framework rising to top 10 on the github leaderboards it has indeed been an exciting and prosperous year for Flutter. The community continues to strengthen and grow which leads to revisions and breakthroughs being made nearly daily. More and more companies are not only embracing Flutter as the solution to their cross platform needs; but also seeing it as a tool that fosters design and development integration with the added value of a swift execution of delivery. Animation in Flutter is just at the brink of its full potential, and at the rate of growth experienced so far, there’s no telling what’s possible within the foreseeable future. While next year’s innovations are still unknown for most, the Flutter evolution will undoubtedly excite designers and developers world-wide. ### What is Flutter Capable Of? Source: https://verygood.ventures/blog/what-is-flutter-capable-of/ ## 11 Days of Flutter - Day 7 As a Very Good Ventures-approved superhero for developers, Flutter saves many of our days. So, what is it capable of? We’re here to enthrall you with its superpowers.  If you’ve been following our 11 Days of Flutter leading to Flutter Interact, you already know Flutter is Google’s UI toolkit for building beautiful, natively-compiled applications for mobile, web and desktop from a single codebase. It allows developers to easily create a variety of app types for ANY screen.  From consumer mobile apps, like our Hamilton app, to web puzzle games, such as our New York Times Kenken app (see our work [here](https://verygood.ventures/success-stories/)) Flutter’s powers know no end. Throughout its existence, we’ve also seen it build business mobile apps, desktop applications, 2D games, Chrome extensions, apps for assistant devices, and computer-generated art. So, if you’re ready for a creative solution that lets you do all the things when it comes to app development, you’re ready for Flutter’s full power. What will *you* make with Flutter? {% embed url="https://www.slideshare.net/deremerd/what-is-flutter-capable-of" title="Video" %} Check out Flutter Interact on Dec. 11 or nosh and network over Flutter-filled conversations and interactive experiences at Very Good Venture’s [unofficial Flutter Interact pre-party](https://verygood.ventures/) on Dec. 10, 7-10 p.m. at The Chrysler Building. ### Create an Autoscaling API with Firebase in just 10 minutes! Source: https://verygood.ventures/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/ ## Background At [Very Good Ventures](https://verygood.ventures), we create mobile apps for a variety of brands and companies. Behind every good app is a scalable backend API. Recently, we’ve become heavy users of [Serverless / Cloud Function](https://en.wikipedia.org/wiki/Serverless_computing) architectures, specifically the tools provided by [Firebase](https://firebase.google.com) and [Google Cloud Functions](https://cloud.google.com/functions/). We love it for how easy they are o deploy, manage, and scale. So, we want to show you how to quickly set it up. ## What we will do We are going to set up a Node.js API server using [Express](https://expressjs.com/) using cloud functions. When we’re done, you’ll know firsthand how fast, easy, and inexpensive it is to create an API that will autoscale using Firebase. ## Assumptions & Pre-requisites To hit our 10 minute goal, we have to make a few assumptions and you have to be prepared with a few things.  1. You need a computer with Terminal or other command line tool 2. You will need a text editor (e.g., [Atom](https://atom.io), [VS Code](https://code.visualstudio.com/)) 3. You need **Node.js** installed. You can do that [here](https://nodejs.org/en/download/) or try on a Mac: **brew install node** 4. You need a [Firebase](https://firebase.google.com/) account 5. As a bonus at the end, we’ll hook up a custom domain. For that, you will need a domain name that you can change the DNS settings for. Try something like [dnsimple](https://dnsimple.com), or [Google Domains](https://domains.google/) ## Step 1: Create a Firebase Project The first thing we need is a Firebase project. Go to the Firebase website (you should be logged in).  Create a new project by clicking the “Add Project” button. ![Firebase console Add Project button on the home page](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-0.png) ![Firebase new project creation dialog](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-1.png) ## Step 2. Setup the Firebase CLI - Open Terminal. (Note: We’ll say ‘Terminal’, but we mean the command line, so use whatever you have for that) - Install the [Firebase CLI Tools](https://firebase.google.com/docs/cli) by executing **npm install -g firebase-tools** (if this doesn’t work you don’t have Node properly installed) - Next, login to the CLI by executing firebase login - You should be prompted to authenticate with Google (make sure you login with the account that created your Firebase project!) - Execute firebase **projects:list** (or firebase list in older CLI versions) and you should see the project you just created in the list. If not, you’re logged in with the wrong user. ## Step 3. Setup your Firebase Project Folder - In Terminal, navigate to a project folder you want to use for this (e.g, **~/Developer/api-on-firebase**) - Execute **firebase init** ![Terminal output from running firebase init command](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-2.png) - Select “**Functions**” and “**Hosting**”. *Note that you can use Database, Firestore, or Storage in your project as well. They’re all great tools, and you should check them out. But for this, we’ll just focus on Functions and Hosting.* ![Firebase init feature selection with Functions and Hosting selected](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-3.png) - Select the project you created in Step 1 from the list - Select TypeScript. Ok, you *could* pick JavaScript, but we’d really recommend [Typescript](https://stackoverflow.com/questions/12694530/what-is-typescript-and-why-would-i-use-it-in-place-of-javascript). :-) - Now just hit ‘**Enter**’ until the wizard is done (or you can type “Y”, which is the default if you just hit Enter). ## Step 4. Create Your ExpressJS App as a Cloud Function The firebase init command does many things and we encourage you [to read up on them](https://firebase.google.com/docs/cli#initialize_a_firebase_project). But for now, what we care about is the **/functions** folder.  - Navigate to **/functions** - Open the folder in your editor of choice (e.g., Atom or VS Code) - Now open **/src/index.ts** ![Default index.ts file in the functions/src directory](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-4.png) This is the main file that declares your Cloud Functions. In practice, you may have many individual functions declared in this file. For now, we’ll do just one… our Express app. ### Setup your Express API - Back in Terminal, make sure you’re in the /functions folder - Execute **npm i express --save** - Go back to **/src/index.ts** in your editor - Delete all the commented out lines, then add the following under the **import \* as functions from ‘firebase-functions'** line. ```ts import * as express from "express"; const app = express(); app.get("/", (_req: express.Request, res: express.Response, _next: any) => { res.status(200).send("This is Very Good!"); }); export const ExpressApi = functions.https.onRequest(app); ``` ### You did it! You just created a Firebase Function, called **ExpressApi**. It is an HTTPS triggered function that will respond to web requests. Obviously it does basically nothing at this point, but from here you can do anything you’d normally do with an Express API. ## Step 5. Setup Hosting Rewrites for our Cloud Function ###### This step is optional. But it only takes a moment, and it will enable us to add a custom domain for our API. If you don’t want to set up a custom domain, skip to Step 6. Firebase allows us to host static files and websites under its Hosting feature. This is really great, and is perfect for static websites as well as React and Angular apps. But, we can also use it to get a custom domain for our Cloud Function by using **rewrites.** - Delete the file **public/index/html**. The reason is that the files in the public directory will take precedence over any rewrites. So just get rid of this default index page, or our root function request will not work. - In your editor, open **firebase.json** in the root folder of the project, add the following to the object inside the hosting object, and save. ```json "rewrites": [{ "source": "**", "function": "ExpressApi" }] ``` Your **firebase.json** should look something like this: ```json { "functions": { "predeploy": [ "npm --prefix \"$RESOURCE_DIR\" run lint", "npm --prefix \"$RESOURCE_DIR\" run build" ], "source": "functions" }, "hosting": { "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ], "rewrites": [{ "source": "**", "function": "ExpressApi" }] } } ``` ### What does this do? This is going to point any requests to our Firebase Hosting to our cloud ExpressApi cloud function. This is the magic that will give us the custom domain. ## Step 6. Deploy! Now, we’ll get our code and hosting rewrites deployed to Firebase. This is super simple. Just do… - Go to your **/functions** folder - Execute **firebase deploy --only functions,hosting** When it’s done your function will be deployed at the Function URL provided. Also notice the Hosting URL which is the URL Firebase gives us for our hosting. ![Terminal output of firebase deploy showing Function and Hosting URLs](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-5.png) ### See if it worked - Open your Function URL in your browser. It should say “This is Very Good!” - Now, try theHosting URL. It should *also* say “This is Very Good!” ## Done: You now have a highly scalable API Wasn’t that fast? Wasn’t that easy? You don’t have to set up any servers. No apt-get! No SSH keys! No autoscaling logic! No load balancers! It’s also really really cheap! You only get charged when your cloud function is invoked…so that means that if nobody is hitting your API overnight, you won’t be paying for a running server instance that isn’t being used. Lastly, it’ll scale massively and automatically. Firebase Functions will just do it automatically for you based on the traffic you receive and at an incredibly low cost. Pretty slick! So, at this point you could call it quits. You can just use your Function URL or Hosting URL as your the URL for your API. That’s it! But we like to be fancy, so let’s get a custom domain for our API. ## BONUS: Setup Custom Domain We will assume you already own the domain you want to use and that you have access to its DNS settings. ### Setup Firebase Hosting - In the Firebase console, click into your project, and then select the “Hosting Tab”. - Tap the “Connect Domain” button - Enter the domain you want to use in the input box and hit continue ![Firebase Hosting Connect Domain dialog for entering a custom domain](/assets/images/blog/create-an-autoscaling-api-with-firebase-in-just-10-minutes/body-6.png) - You will need to verify ownership of your domain. Follow the instructions. Note that it might take a while for Firebase to pick them up. - Lastly, add the A records Firebase gives you to your DNS ## That’s it! Once your DNS records propagate, your domain will now point to your Express API running on Firebase Functions! ### Flutter is All the Things Source: https://verygood.ventures/blog/flutter-is-all-the-things/ ## FLUTTER JUST GOT MORE REVOLUTIONARY. Flutter is a game-changing technology for creating native mobile apps. The rapid growth of the developer community is testament to the benefits Flutter provides to mobile developers. [During the developer keynote at Google I/O 2019, Adam Seligman revealed the public availability of Flutter for Web!](https://www.youtube.com/watch?v=LoLqSbV1ELU&feature=youtu.be&t=3280) Flutter just got a whole lot more revolutionary. We had a hand in making this moment a reality, so we want to share some thoughts on what Flutter on the Web means for the Flutter community. {% video url="https://www.youtube.com/watch?v=LoLqSbV1ELU" title="Video" %} ## FLUTTER IS A UI TOOLKIT FOR ALL THE THINGS! We have been fortunate to have spent the last two months working with Flutter for Web, and we have to say that once again Flutter has blown us away. We can now build workable apps for nearly every screen. Flutter continues to change everything for developers. ## First, Flutter made us more productive *Mobile* developers. As [the team](https://verygood.ventures/) behind the Hamilton app, we put the *early* in “early adopters”. Since then, we have only built apps using Flutter — we simply haven’t had a need to build purely native apps. Not to say there aren’t reasons to go native, but for our team and the apps we’ve built, Flutter has always done everything we needed it to do while significantly improving our productivity. ![Hamilton app built with Flutter running on iOS and Android](/assets/images/blog/flutter-is-all-the-things/body-0.jpeg) ## Then, Flutter turned us into Desktop app developers. Last year when [Flutter Desktop Embedding](https://github.com/google/flutter-desktop-embedding) (FDE) hit the scene, the skills that Flutter developers cultivated suddenly could be used to create apps for Windows and macOS. [Our jaws dropped when we realized that creating native desktop apps was suddenly a part of our toolkit](https://medium.com/flutter-nyc/revealed-flutter-slides-at-flutter-live-18-7af4cac17ed3) without having to learn anything new. {% video url="https://www.youtube.com/watch?v=NQ5HVyqg1Qc" title="Video" %} ## Now, Flutter enables us to be productive Web developers. The loudest applause at Flutter Live came when “Hummingbird” was announced. The excitement for Flutter on the Web is clear, and we think that the applause is about to get a whole lot louder. When the Flutter Web version of the New York Times’ KENKEN game was revealed at Google I/O, it kicked off a new chapter for Flutter. ![New York Times KENKEN puzzle app built with Flutter for Web](/assets/images/blog/flutter-is-all-the-things/body-1.png) The New York Times KENKEN app prototype does exactly what Flutter has always promised — it delivers a beautiful, performant native experience from a single codebase. And that now includes the Web, whether using a desktop or a mobile browser. Flutter is now truly the multi-platform framework it set out to be. > ***Attention all Flutter developers: you are now web developers!*** ## WHAT’S EXCITING ABOUT FLUTTER ON THE WEB? Over the last six weeks or so, our team at [Very Good Ventures](https://verygood.ventures/) worked closely with the New York Times and Flutter teams to help develop the **New York Times KENKEN app — the first prototype built using Flutter for Web**! Along the way, we’ve learned a ton and if you can’t tell already, we can hardly contain our enthusiasm. ## 1. It’s Flutter, but for the Web! > *“Now, thanks to Flutter, creating apps for the web is as easy as setting a different build target.”* There’s a reason we are a mobile development company and not a web development company. Web is just, umm, different. We have deep respect for web developers that wrangle the insanity that is modern web development. But, we’ve always preferred working with mobile. Yet now, thanks to Flutter, creating apps for the web is as easy as setting a different build target.. We can create apps, logic, and layouts the same way that we’ve been developing mobile apps with Flutter, but now we can deploy them to the Web. We often build Angular or React sites as CMS tools for our mobile apps. It’s not ideal because it generally means different people have to work on those tools than work on the apps. When we got into FDE, we were excited about the potential to build CMS tools as Mac apps in addition to desktop extensions of our apps. But now, we will can also build them for the web — using Flutter. That means the same teams that build our mobile apps can build anywhere else our apps need to be. ## 2. It works, and you already know how to do it. > *“If you’re a Flutter developer, you already know how to develop Flutter for the Web.”* Ok, so it’s still really early and even though Flutter for Web has improved leaps and bounds in the last six weeks, it still has a long way to go. But it already works! Just look at the KENKEN prototype site for proof. A huge shout-out to the Flutter team because what they have accomplished is awesome. To temper the enthusiasm… Is Flutter fully optimized for all browsers or 100% perfect? No. It may never be. But it’s certainly good enough for prototypes, and we’re comfortable enough to start building real sites with it. Most importantly, if you’re a Flutter developer, you already know how to develop Flutter for the Web. You set up and Flutter a little differently for the Web, but the development experience is pretty much the same thing! ## 3. It’s hot reload turned up to 11. > *“Testing Flutter apps in the browser is your new reality.”* You love hot reload, right? What if I told you that you can test your app in a browser instead of a simulator? Sounds great right? Well, testing Flutter apps in the browser is your new reality. It turns out that the browser is our new favorite simulator. While building the KENKEN app, we were targeting iOS, Android, macOS, Windows, Chrome, and Safari (including both mobile and desktop versions). Very quickly Chrome became our default build target during active development. Just pause on that for a moment—Flutter for Web is incredibly new and early, yet it’s already become the easiest renderer for creating and testing layouts. ## 4. It’s insanely useful for prototyping. > *“Everyone knows how to click a link and open a website!”* You know what turns out to not be easy for non-technical people? Figuring out how to install an APK or setting up TestFlight. Not to mention the cost of developer licenses or approval processes. However, everyone knows how to click a link and open a website! Flutter on the Web is about to be a big deal for prototyping. With Flutter on the Web, it’s easy to deploy static assets to a web host. During KENKEN development, we hooked up Travis CI to automatically build the Web assets and deploy it to Firebase Hosting. We were doing 5–10 builds a day, and we were able to get feedback from stakeholders on the latest builds *within minutes*. ## 5. It’s pixel perfection on every screen. > *“Generally speaking, the layout for a similar screen size is the same on Flutter on the Web as it is on iOS, Android, or desktop.”* Flutter on the Web gives us similar platform-to-platform layout and design reliability to what we see with iOS and Android. Yes, there are differences. Yes, you need to build and test on every platform. But generally speaking, the layout for a similar screen size is the same on the Web as it is on iOS, Android, or desktop. This is amazing and useful. We can lay something out using Flutter for Web and guess what… it looks the same on iOS or Android. By carefully considering how our design would scale to different sizes and aspect ratios, we saved ourselves a lot of time and effort, and of course we can still customize the experience based on the screen, input support, and device type. ## 6. It’s adaptive to the needs of different platforms. > *“As long as we caveat that Flutter on the Web is just getting started, it’s safe to say the we already have a lot of what we need.”* You might be thinking: “this all sounds great, but websites and apps have different requirements” and you’re absolutely right. As long as we caveat that Flutter on the Web is just getting started, it’s safe to say the we already have a lot of what we need. Need to recognize gestures vs. mouse clicks? No problem. Have a physical keyboard? You have access to it. Need to support hovers and mouse overs when they’re available? Yes, you can do that. To be clear, Flutter absolutely requires customizations for each platform. It is not and never will be a silver bullet. But it gets us really close. The vast majority of the code is shared. Yes, we have different branches for desktop, mobile, and web. But we could have totally different repositories. ## 7. It’s Mobile => Web, not Web => Mobile > *“For teams faced with questions like: ‘Should we build iOS or Android first?’ or ‘Should we build web or mobile first?’, Flutter allows us to simply answer: ‘Yes!’.”* This is really what makes Flutter for Web different: it is mobile development taken to the web and not the other way around. The most obvious analog to Flutter is React Native. React Native enabled web developers using React to create high quality mobile apps. React Native is great, but it never worked for us because we are mobile developers, not web developers, so it was harder for us to get into. We feel that Flutter’s mobile-first focus will ultimately be its differentiator as it moves into the web. The last 10 years has seen a massive migration of talent into mobile development. As these developers discover Flutter as a tool for their mobile development needs, they will simultaneously learn skills that will make them serviceable web developers via Flutter. The explosion of productivity this implies is going to be exciting to watch. To be clear, knowing how to use Flutter is not going to instantly turn someone into an expert web developer. It’s no different than building Flutter mobile — to be an expert Flutter developer you also need to be an expert iOS and/or Android developer. The same is going to apply here. Flutter is not a panacea for web development, but it gives mobile developers a new tool that helps them build for the Web the same way they build mobile apps. So, for teams faced with questions like: “Should we build iOS or Android first?” or “Should we build web or mobile first?”, Flutter allows us to simply answer: “Yes!”. ## ONE FRAMEWORK TO RULE THEM ALL? At [Very Good Ventures](https://verygood.ventures/), we’re obviously bullish on Flutter. We embraced it early and we’ve never looked back. We’ve been fortunate to achieve a number of firsts with Flutter: [the first commercial Flutter app](https://medium.com/flutter-io/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs-f37f81856be4), [the first widely-seen Flutter Mac app](https://www.youtube.com/watch?v=NQ5HVyqg1Qc&feature=youtu.be&t=9301), and now the first Flutter Web prototype. We love Flutter and it has made a tremendous impact on our work and our lives. What has made us the most excited about Flutter is that it keeps making our lives easier. Not only has the core framework and dev tooling continued to improve, but the Flutter team has continued to add value to our skills by creating solutions to target new platforms and device types. With the public availability of Flutter for Web and the launch of the New York Times KENKEN prototype, we are seeing the realization of Flutter as a *multi*\-platform framework for any device with a screen. Our prediction is that over the next year, the Flutter community is going to grow even faster than it did last year. We predict that more big companies will start embracing Flutter in earnest. We envision the startup community will pick up Flutter in earnest because it eliminates the question of: “Which platform first?” Is Flutter better than native apps, React Native, or Xamarin? Is it better than React, Angular, or regular web development? That exercise is left to the reader. It really depends on each unique team and the needs of the product. It’s certainly not the end-all-be-all, nor is it for everyone. But, for us, it’s amazing and we love it. ### Revealed! Flutter Slides at Flutter Live ‘18 Source: https://verygood.ventures/blog/revealed-flutter-slides-at-flutter-live-18/ On December 4th, 2018, [Flutter Live ’18](https://developers.google.com/events/flutter-live/) showed the world the first major glimpse of the exciting vision for [Flutter](http://flutter.io). Not only did Flutter achieve v1.0, but we saw a host of amazing new tools as well as [Flutter Desktop Embedding](https://github.com/google/flutter-desktop-embedding) and [Hummingbird](https://medium.com/flutter-io/hummingbird-building-flutter-for-the-web-e687c2a023a8) in action. One of the most incredible moments, in a keynote filled with plenty, was when it was [revealed that the entire keynote presentation was itself running in a Flutter app](https://youtu.be/5SZZfpkVhwk?t=121)! For our team at [Very Good Ventures](https://verygood.ventures), this moment was also also incredibly nerve-wracking. You see, we built the Flutter Slides app used in the keynote in close partnership with Tim Sneath and the incredible design team at Google. This moment was make-or-break, and as is typical with our Flutter experience, everything went beautifully. ![Flutter Slides app open source announcement](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-0.png) *Flutter Slides is now open source* We are incredibly proud of what we accomplished with the Flutter team, and we hope that members of the Flutter community can use this library as a jumping off point to learn about using Flutter for desktop. To that end, we’d like to tell you a little more about Flutter Desktop Embedding and our experience building the app for Flutter Live ‘18. ## What is Flutter Desktop Embedding? [Flutter Desktop Embedding](https://github.com/google/flutter-desktop-embedding) is an implementation of Flutter that enables Flutter apps to run on Windows, macOS, and Linux. It’s still very early in its lifecycle, but its promise is extraordinary. It means that a team of developers using Flutter to build for iOS and Android, can also build apps for the major desktop platforms — without learning a new language, without a different set of build tools, and with reusable code. > **“This is amazing. I just ran BetaBubs, and it just…worked. I guess we’re macOS developers now too.”** > \- Kevin Gray of Very Good Ventures ![BetaBubs Play Emojis running as a macOS desktop app via Flutter Desktop Embedding](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-1.png) *Our emoji app for kids, BetaBubs Play Emojis, worked on macOS with virtually no code changes!* The first time Kevin, a lead Flutter engineer at Very Good Ventures, set up Flutter Desktop Embedding and ran our BetaBubs app as a Mac app, he blurted out “This is amazing. I just ran BetaBubs, and it just…*worked*. I guess we’re macOS developers now too.” It was an incredible moment as our team realized that we could now develop for a whole new platform — the desktop! Just like that, our years spent becoming experts in Flutter, a nascent multi-platform development framework we were using for mobile apps, had produced a whole new world of opportunities. In retrospect, we always found it interesting that Google always described Flutter as *multi-platform* rather than cross-platform. Our team was lucky enough to find and love Flutter from its very early days. We started using Flutter when it was still in alpha, and we were fortunate to develop [the Hamilton app](https://medium.com/flutter-io/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs-f37f81856be4), which went on to become a flagship Flutter product. From the earliest days, Flutter was always *multi-platform.* Now we all can clearly understand that choice of words — [Flutter’s vision is a single unified UI platform, not just for iOS and Android apps](https://youtu.be/5SZZfpkVhwk?t=28), but for anywhere you want to paint pixels — whether for Mac, Windows, Raspberry Pi, ChromeOS, and even web. ## The Flutter Slides App for Flutter Live‘18 When we set out to build the Flutter Slides app, we had basically no constraints or defined scope. It simply need to work, had to be easily updated as content creators finished their slides, needed to showcase what Flutter can really do, and it could not possibly crash (Oof, can you imagine 😳). ### Minimum Viable Slides Our first task was figuring out the minimum feature set required to create an effective slides app. By the time we started, we had less than a month until the big day, so we had to be frugal with our choices. First, we need to be able to layout content on a slide. Since the window of desktop app can be resized, we needed a layout system that would be relative to the window and scale content up and down accordingly (create a slide in Google Slides and resize your window and watch how the content behaves to see what we mean). On mobile, content doesn’t necessarily resize so that it looks the same at all scales, so this was a new challenge. Next, we needed to be able to move forwards and backwards between slides. We needed keyboard and mouse click bindings to make this happen and we needed to be certain it would work with wireless “clickers”. Of course once we had many slides, we found ourselves needing to quickly jump non-sequentially from one to another. We needed a slide sorter! Then it was clear that to be interesting, the slides would need visually compelling transitions and element animations. So we devised a system for transitions between slides and a re-usable set of animations we could apply to the content. ![Flutter Slides File Open dialog for importing slide content](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-2.png) *File -> Open. Your slides running in Flutter!* Lastly, we needed an easily maintainable asset and content management approach. So, we devised a JSON schema, folder structure, and file import system (hello File -> Open) to streamline the process of maintaining content. ### Content Management Anyone who has ever created a major keynote with many speakers knows that the slides are never finished until the last moment. This was a major challenge for the project. Content was created first in Google Slides, and then our team would translate it into the app. Fortunately, we anticipated this challenge and rather than build each slide with custom slide-specific code, we devised a JSON file with a reusable slide schema. So rather than change code, we simply needed to change a single content file. While we did occasionally have to write some new code if a slide had a new set of requirements, most slides could be laid out and populated with content simply by updating the JSON. ```json { "advancement_step": 1, "type": "rect", "fill": "0xFF000000", "x": 1060.0, "y": 340.0, "height": 100.0, "width": 100.0, "animation": { "curve": "easeOut", "duration_in_milliseconds": 3000, "opacity_start": 0.0 } } ``` While maintaining JSON comes naturally for developers, it’s not very intuitive for non-technical people. Really the JSON system is just a starting point, and in comparison to other slide products, we know the system is not perfect. With our data structure in place, the next step would be to add an “edit” mode where we could add content boxes, add text and images, group and ungroup, reposition elements, and do the things Google Slides and Powerpoint can do. We’d love to hear ideas from the open source community for improving on this system. ### It’s an App! Of course the most exciting prospect of building a presentation as a Flutter app is that we can do anything Flutter can do. No longer does a presentation have to be limited by what Google or Microsoft or Apple have built as features for their presentation applications. We can build anything we want! For the keynote, we wanted to ensure that we used Flutter for animations where it made sense. For complex animations, the sensible choice was to use Flare, [Lottie](https://pub.dartlang.org/packages/lottie_flutter), or animated gifs/webp. But we still wanted to demonstrate Flutter’s native animations capabilities. So, we built a variety of transitions, carousel animations, scaling effects, and other useful (and reusable) animations. ![Animated four-box transition for Flutter's Four Pillars in the Slides app](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-3.gif) *Try doing this in Powerpoint…* Perhaps the flagship animation for the presentation was the unique 4-box transition we created for Flutters “Four Pillars”. Our goal was to create a transition flow that demonstrated each pillar individually while keeping them all rooted together as a system. The final effect is simply something that is not possible in the normal presentations tools (at least not without embedded videos or incredibly advanced Powerpoint ninjas). ![Flutter counter app running live inside the Slides presentation](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-4.gif) *Slides that are a real Flutter app running real Flutter code* Additionally, it was crucial that we show the world that really *any* Flutter code can run as a desktop app. So, of course what better way to illustrate this than to show the screen every Flutter developer has seen — the starter “counter” app. We added the real code to the application, and Tim demonstrated live during the presentation that you can tap the “+” to increment the counter. No longer must a slideshow be limited to text, images, and videos. *We can run code!* Just consider the implications for technical conferences and demos during presentations. Now, instead of showing screenshots or video captures, we can embed our real code and run real apps in our slides! Amazing. ## What's Next It was exhilarating to hear the audience’s reaction when Tim minimized the app and showed the world that he wasn’t just talking about Flutter on the desktop, he was *using* it for Flutter’s biggest moment. ![Tim Sneath revealing the Flutter Slides desktop app during the keynote](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-5.png) Our team at [Very Good Ventures](https://verygood.ventures/) was deeply honored to be a part of this amazing moment. This app was built very fast, with no pre-defined spec, with requirements changing to the last minute — and it accomplished its mission admirably. We couldn’t be happier. But, we also know it could be even better. What started as a means to make the audience’s jaw drop, is now an inspiring product in its own right. It’s still early days for [Flutter Desktop Embedding](https://github.com/google/flutter-desktop-embedding) and it has its limitations. We have countless ideas of how we could make it better, and we’re sure the community will as well. We can’t wait to watch it grow and see what the community can dream up. If [Flutter Live ’18](https://youtu.be/kpcjBD1XDwU) inspired you and made you excited for what Flutter can do, we hope that this app helped to demonstrate the amazing potential of Flutter on the desktop. We hope that the community will be inspired by this project — not only to keep advancing it and add more features to make an amazing presentation tool, but also to be inspired to tackle new Flutter projects on Windows and macOS. Now, let’s see what we can make with Flutter in 2019! ![Very Good Ventures team celebrating the Flutter Slides launch](/assets/images/blog/revealed-flutter-slides-at-flutter-live-18/body-6.png) ## Success Stories ### Amusement Park Leader Scales With Flutter Apps Source: https://verygood.ventures/success-stories/amusement-park-flutter-apps/ Universal Destinations & Experiences (UDX), part of Comcast NBCUniversal, operates some of the world’s most iconic theme parks and entertainment destinations. With a mission to create immersive experiences powered by innovation, UDX continually invests in technology to enhance guest engagement and operational excellence across its global attractions portfolio. ## The Challenge Universal Destinations & Experiences (UDX) aimed to tackle two major challenges:  - Unify its park mapping experience across mobile and web while maintaining a single, efficient codebase - Enhancing mobile food and drink ordering in their Universal Studios and Universal Hollywood apps ### Park Mapping UDX’s existing map setup required separate development efforts—Angular for web and Flutter for mobile—leading to inefficiencies. To streamline operations, reduce maintenance overhead, and drive engineering scalability, they wanted to integrate a Flutter-based mapping component that could function seamlessly across both environments.  There was also a need to align the web team, which was primarily focused on Angular, on the benefits of Flutter as a cross-platform solution. The initiative aimed to demonstrate that Flutter could effectively support both web and mobile, fostering adoption within UDX’s broader engineering organization. ### Mobile Food & Drink Ordering UDX aimed to enhance and optimize their existing mobile food and drink ordering (MFDO) functionality as a self-service kiosk in restaurants. With an increasing number of guests relying on digital experiences, UDX needed to improve operational efficiencies by reducing staff dependency during low volume times, and driving additional revenue—all while ensuring seamless integration with existing restaurant point-of-sale (POS) systems. With strict deadlines ahead of park openings, Universal needed an efficient, scalable, and cross-platform solution that streamlined operations without creating redundant codebases. ## Our Solution ### Park Mapping We partnered with UDX to develop a unified Flutter Map component, designed as a shared mobile and web widget. The approach included: - **Flutter + Angular Integration:** Seamlessly embedding the Flutter Map component within UDX’s existing Angular-based web application, ensuring a consistent experience across platforms. - **Engineering Scalability:** Creating a shared Flutter codebase that serves both mobile and web, reducing the need for separate development efforts and enabling long-term scalability. - **Proof of Concept (PoC) Implementation:** Demonstrating Flutter’s feasibility and performance on the web, showcasing its ability to extend beyond mobile. This successfully built confidence in cross-platform adoption and influenced UDX’s web team to embrace the technology. ### Mobile Food & Drink Ordering We refined and expanded the MFDO experience within Universal’s Flutter-based mobile apps. VGV conducted an in-depth code and services audit to assess feature readiness and priority, then nine weeks of iterative build sprints and QA, resulting in: - **Enhancing the User Experience:** Introducing new features that offer guests more information and choice while integrating contextually relevant features to increase engagement and revenue. - **Kiosk Integration:** Rewriting the existing mobile food and drink app into a Flutter-powered kiosk solution, enabling seamless self-service ordering without staff involvement during low-traffic hours and improving overall operational efficiency. - **Streamlining Systems:** Engineering a single-source Flutter codebase running on Windows machines—a first-of-its-kind implementation. - **Future-State Planning:** Delivering a scalable, reusable solution, reducing overhead for additional expansions. ## The Impact  ### Park Mapping With VGV’s unified map component, we increased efficiencies while streamlining digital experiences, providing:  - **Engineering Efficiency:** Reduced code duplication, enabling a single codebase for both web and mobile mapping experiences. - **Cross-Team Alignment:** Successfully demonstrated Flutter’s web capabilities, leading to increased buy-in from UDX’s web engineering team.  - **Long-Term Scalability:** Provided a foundation for future cross-platform development, reducing operational complexity and increasing flexibility. > "VGV is very adaptable. They do things we don't even ask. Their team says 'Here is some value we can provide, can we do this?' and it's great."  – Mikal Wessel, Director of Global Product Management, UDX ![__wf_reserved_inherit](/assets/images/success-stories/amusement-park-flutter-apps/body-0.png) ### Mobile Food & Drink Ordering By optimizing mobile food and drink ordering, we not only streamlined operations within Universal Studios parks, but also enhanced the guest experience. This includes: - **Improved Operational Efficiency:** Streamlined kiosk operations and reduced overhead with one scalable codebase. - **Enhanced Guest Experience:** Provided a seamless, user-friendly mobile ordering process. - **Increased Mobile Engagement & Revenue:** Drove higher adoption of Universal’s mobile apps and boosting in-app transactions. - **Successful On-Time Delivery:** Met critical deadlines for all projects, ensuring a successful launch on a very tight timeline. > “There's a night and day difference between being consulted and working with true experts. VGV delivers a quality product and improves the process and people around them. It's consistently a pleasure to have the partnership, feature after feature.” – Calvin Goodman, Senior Lead Software Engineer, UDX ![__wf_reserved_inherit](/assets/images/success-stories/amusement-park-flutter-apps/body-1.png) ### Fintech Leader Achieves Seamless Flutter Migration Source: https://verygood.ventures/success-stories/betterment/ Betterment is an online investment company dedicated to providing smart money management solutions to customers. The company continues to see massive growth, with 500,000+ customers to date. Betterment’s mobile app is a crucial tool for business success, as it allows customers to invest and access financial advice at their fingertips. We worked with Betterment to pilot Flutter for their application, establish Flutter expertise and best practices, and train their engineering team on the new mobile framework. ### The Challenge Betterment, a leader in digital investing and financial advice, wanted to modernize their mobile app experience and explore Flutter as a potential solution. However, adopting a new technology at scale came with uncertainties and risks: - Could new Flutter-code be integrated smoothly with their existing native codebase? - How would they migrate their large, legacy native apps to Flutter without rebuilding everything from scratch? - How could they ensure that their engineering team—which had little experience with Flutter—could learn it quickly enough to contribute to production code? ![Betterment mobile investing app with legacy native codebase ready for Flutter migration](/assets/images/success-stories/betterment/body-0.webp) ### The Solution To address these challenges, VGV designed a phased, low-risk strategy that would allow Betterment to test Flutter, prove its value, and gradually migrate their app while enabling their engineers. Our work focused on building confidence, creating standards, and setting up long-term success. This included: - **Piloting Flutter in Their App:** Building a non-critical feature (account-creation flow) using Flutter, then integrating it into the existing native code via Flutter’s “add-to-app” functionality.  - **Designing a Migration Strategy:** Creating a plan to migrate pieces of the app gradually, keeping the rest in native, so as to balance risk, maintain stability, and allow incremental progress.  - **Establishing Best Practices & Engineering Standards:** Determining standards for writing, organizing, and scaling Flutter code in this hybrid environment. - **Delivering Training:** Upskilling Betterment’s engineers to get them up to speed, so they could build features using Flutter themselves soon after training. ![Betterment Flutter pilot featuring the account-creation flow integrated with existing native code](/assets/images/success-stories/betterment/body-1.webp) ### How We Did It Our approach combined practical engineering work with hands-on enablement, ensuring both short-term wins and long-term adoption: - **Flutter Pilot**: Built the first feature in Flutter (the account-creation flow), then connected it to the existing native codebase. This proved that “add-to-app” integration works and performs well. - **Transformation Plan**: Rather than rewriting the entire app, we helped plan a phased migration: port portions of the app to Flutter over time, and build all new features in Flutter, while leaving stable parts of native code in place until they are ready to move. - **Best Practices Implementation**: Set up architecture, code organization, tooling, and workflows so that as more of the app is ported, quality and maintainability are preserved. - **Training & Enablement**: Ran formal training sessions (with “Super Declarative!”) for Betterment’s engineering team, including group sessions, hands-on examples, breakout groups. In just one week, engineers went from minimal Flutter knowledge to building features in Flutter that are now part of the production app. ![Betterment engineering team training session on Flutter development best practices and architecture](/assets/images/success-stories/betterment/body-2.webp) ### The Results **775,000+** Betterment Accounts **20+** Flutter Developers Trained **$32b** Assets Under Management (as of 3/2023) > “Even with a talented team, converting two large, legacy codebases into a single codebase using an unfamiliar technology was a challenging proposition. Working with VGV gave us the added confidence we needed to evaluate Flutter, figure out that we love it, and commit to migrating our entire app.” - **Sam Moore, VP Architecture**, Betterment ## Project Summary After years of mobile app development, [Betterment](https://www.betterment.com/)’s mobile codebase was becoming difficult to manage and their native app teams’ velocity was starting to slow. Flutter was emerging as an appealing solution, but they wanted to ensure that they could replatform safely and efficiently with no interruption to their users. VGV helped Betterment pilot Flutter for their application, establish Flutter expertise and best practices, and train their engineering team on the new mobile framework. ## Outcomes ### Outcome 1 #### Roadmap to Flutter Transformation Switching an existing app to a completely new framework can feel daunting. VGV helped Betterment reduce productivity loss by working side-by-side with engineers to help them smoothly transition their native apps to Flutter without breaking their stride. ### Outcome 2 #### Embedded Experts Our team was on-hand starting at the pilot of their first Flutter feature through the weeks after training to answer questions from Betterment’s engineering team and help them find solutions. ### Outcome 3 #### Team Synchronicity Betterment’s Android and iOS teams are now closely aligned after Flutter training. The entire team now builds in one single codebase and moves forward together — allowing for better collaboration and productivity. ### From Audit to Action: A Strategic Braze Optimization Roadmap Source: https://verygood.ventures/success-stories/blade-braze-optimization-roadmap/ Blade is a premier urban air mobility company transforming how people move through cities and across key global destinations. Built around speed, convenience, and a distinctly luxury experience, Blade operates high-frequency airport transfers, regional routes, and bespoke charter services for events like the Cannes Film Festival and Monaco Grand Prix. With a commitment to modernizing short-distance travel, Blade blends world-class hospitality with innovative aviation partners, creating seamless, elevated journeys for today’s most discerning fliers. **BLADE partnered with VGV to unlock the full potential of their marketing stack and lifecycle campaigns.** Through a comprehensive audit of their Braze implementation, VGV delivered a strategic playbook with technical optimizations, new campaign use cases aligned to business goals—driving first purchases, encouraging second purchases, and optimizing the passholder experience—along with recommendations to leverage underutilized features for greater personalization, scalability, and automation. ## The Challenge  After launching its redesigned Flutter-powered app with Very Good Ventures (VGV), BLADE—the pioneer in premium urban air mobility—focused on maximizing user engagement. With over 100 million miles flown and a growing customer base, the company had already invested in a best-in-class MarTech stack, including Braze, Segment, BigQuery, and GA4, in order to enable personalization and scalable engagement. Despite these tools, BLADE saw an opportunity to maximize Braze's advanced capabilities. With evolving business priorities and a lean marketing team, they recognized the potential to deepen personalization, unlock additional features, and drive more repeat purchases. The engagement strategy focused on driving repeat behavior—increasing the number of users completing their first and second purchases while reducing the number of passholders who purchased a flight pass without booking a trip. The goal was to encourage more customers to book their second ride sooner, shortening the time between their first and subsequent flights. ### A Premium Customer Journey Encouraging incremental purchases in BLADE’s premium market presents a unique challenge. With flights priced as a premium travel experience—and demand heavily shaped by seasonality, geography, and key events—converting even a single additional trip represents significant business value. At the same time, BLADE customers expect a premium, personalized experience—not just during their flight, but throughout every interaction with the brand. This means their lifecycle messaging must be impeccably timed and context-aware, adapting to where each customer is in their journey. The challenge lied in balancing precision, personalization, and timing—crafting campaigns that feel effortless and exclusive, while driving meaningful business growth. ![__wf_reserved_inherit](/assets/images/success-stories/blade-braze-optimization-roadmap/body-0.jpg) ##### But First, What is Braze?  [Braze](https://www.braze.com/) is a leading customer engagement platform built for enterprise-scale, cross-channel communication. Designed with a mobile-first foundation, it empowers organizations to deliver personalized messaging across email, push, in-app, SMS, and more—all in real time. Its scalability and orchestration capabilities make it ideal for enterprises seeking to enhance customer retention, boost customer lifetime value (CLV), and unify customer experiences across every touchpoint. ## Our Solution  VGV delivered a Braze Playbook with 34 recommendations, crafted during a six-week engagement that moved through three phases: audit, playbook development, and early execution.  The recommendations were staged using a clear framework designed to evolve with BLADE’s technical maturity: - **Takeoff:** Immediate wins launched with the existing setup—no extra tools, data, or development required. - **Hover:** Intermediate campaigns requiring moderate development effort and enriched data. - **Fly:** High-value, long-term opportunities demanding deeper integration, cross-team collaboration, and more investment—but promising the most transformative results. This phased approach gave BLADE a staged roadmap—balancing tactical quick wins with strategic initiatives to deliver both immediate impact and sustainable growth. We also built and launched a handful of priority next-gen campaigns, leveraging advanced Braze features, including behavior-driven messaging and AI optimization. As part of this initiative, VGV also designed and developed a **Braze Design System**—a custom set of drag-and-drop components hard-coded for Braze. These components dynamically adapt based on the recipient’s profile, ensuring offers, rewards, and action nudges are tailored to each customer’s journey stage. The system was engineered for maximum efficiency, enabling BLADE’s marketing team to build and launch campaigns faster while maintaining consistency and precision. > "VGV took our lifecycle marketing to a new level. A fresh audit and playbook gave us a clear, prioritized roadmap, and their execution unlocked use cases we hadn’t been able to activate on our own.” - Christiana Weller, VP of Marketing ## How We Did It The engagement began with a comprehensive audit of BLADE’s CRM ecosystem. VGV evaluated active Braze campaigns, assessed adoption of key features, and benchmarked performance against best practices across email and push channels. This diagnostic phase uncovered gaps in data architecture, campaign orchestration, and feature utilization—insights that directly informed the roadmap ahead. From there, VGV delivered a staged playbook that balanced tactical quick wins with strategic, transformative opportunities. To maintain momentum, our team immediately began implementing select recommendations while developing a comprehensive Braze data tracking plan. This plan not only provided the data needed to power the recommended use cases but also optimized BLADE’s data structure to reduce data point consumption—making their Braze implementation more cost-effective, personalized, and automation-ready. Key lifecycle campaigns and use cases included: - **Post-flight return booking nudges** to drive repeat rides - **Enhanced experiences for Pass holders**, strengthening loyalty - **Contextual permission requests** for location and push notifications - **Enhanced flight receipts** with dynamic, personalized content - **Post-booking add-on upsells** to increase average order value - **Geo-triggered offers** for BLADE helicopters when customers land at select airports - **“Surprise & delight” campaigns** for loyal customers - **Live Notifications** (Android Live Updates and iOS Live Activities) for real-time flight and service updates - **Pet-centric messaging** for travelers flying with animals - **Birthday campaigns** to foster personalized engagement moments Together, these initiatives created a highly personalized, end-to-end lifecycle strategy that elevated BLADE’s premium customer experience while unlocking measurable growth opportunities through Braze. > “The custom Braze Design System they built has made our team faster, more efficient, and far more personalized in how we communicate with customers.” - Christiana Weller, VP of Marketing ![__wf_reserved_inherit](/assets/images/success-stories/blade-braze-optimization-roadmap/body-1.jpg) ## The Result  The Playbook engagement provided BLADE with a clear path forward, transforming their Braze setup from underutilized tools into an actionable growth engine.  Before partnering with VGV, BLADE had invested significantly in Braze but was only scratching the surface of its advanced capabilities. As first-time Braze users, their initial data architecture limited what was possible. The Playbook brought clarity—outlining unlockable use cases, untapped features, and a prioritized implementation path that empowered the team to use Braze to its full potential. In just six weeks, VGV: - Delivered a prioritized roadmap showing what to do first and why - Surfaced data gaps to guide engineering priorities - Implemented a component-based email system for faster builds, consistency, and scalable personalization - Launched a revamped welcome series to improve new user onboarding - Built optimized engagement flows for BLADE Airport passholders to enhance the experience - Upskilled the team on Figma and HTML, increasing self-sufficiency > “VGV combined strategic clarity with exceptional technical expertise, and they were an incredible partner every step of the way." > \- Christiana Weller, VP of Marketing ### Modular Email Design System Built for Scale Source: https://verygood.ventures/success-stories/blade-email-design-system/ Blade is a premier urban air mobility company transforming how people move through cities and across key global destinations. Built around speed, convenience, and a distinctly luxury experience, Blade operates high-frequency airport transfers, regional routes, and bespoke charter services for events like the Cannes Film Festival and Monaco Grand Prix. With a commitment to modernizing short-distance travel, Blade blends world-class hospitality with innovative aviation partners, creating seamless, elevated journeys for today’s most discerning fliers. ## The Challenge  As part of BLADE’s broader CRM transformation, one of the key recommendations from the Braze Playbook engagement was to modernize the company’s email system. At the time, BLADE relied on a single, static email template that required weekly manual updates—creating limitations in design flexibility, creativity, personalization, and overall performance. Key issues BLADE needed to improve were:  - **Limited Flexibility:** The team had a drag-and-drop (WYSIWYG) editor, which restricted custom design and personalization capabilities. - **Inconsistent Branding:** Without standardized components, emails often looked different across campaigns and providers. - **Accessibility & Dark Mode Issues:** The existing design didn’t meet accessibility standards or render properly in dark mode. - **Slow Builds:** Manually updating content each week consumed valuable time and resources. To elevate the customer experience and align with BLADE’s premium brand, the company needed a smarter, modular approach that balanced creativity with operational efficiency. ## Our Solution  VGV developed a custom Modular Design Kit (MDK): A fully HTML-based, component-driven email system designed for flexibility, scalability, and personalization. The solution involved:  - Moving to a fully HTML-based design system, enabling advanced personalization. - Achieving brand consistency across mailbox providers with full accessibility and dark mode support. - Introducing modular, reusable components, with multiple modules that can be assembled into templates, reducing design and build time for each campaign. - Implementing Liquid Logic to dynamically tailor content by audience, driving more relevant, high-performing emails. - Empowering the marketing team with hands-on training, improving agility, and campaign independence. ![__wf_reserved_inherit](/assets/images/success-stories/blade-email-design-system/body-0.png) ## How We Did It The new MDK redefined BLADE’s email production workflow, turning a previously manual process into an agile, scalable system. The work process included:  - **Audit & Alignment:** We began by auditing existing campaigns to identify the most frequently used components, then mapped out new modules to support future use cases. Together with BLADE, we defined standards for typography, colors, imagery, and accessibility—including full dark mode optimization. - **System Build:** Within a week of design approval, our team built all modules and templates in HTML, creating a reusable system that allowed BLADE to easily assemble branded emails using copy-paste logic. - **Strategic Deployment:** We identified key lifecycle campaigns to relaunch with the new system, starting with the Welcome Series—the first customer touchpoint—and a tailored series for Passholders. - **Enablement & Training:** VGV trained BLADE’s team on using the new design system through Figma and HTML sessions, and provided written documentation to ensure long-term independence. This phased approach gave BLADE a staged roadmap, balancing tactical quick wins with strategic initiatives to deliver both immediate impact and sustainable growth. ## The Result  The MDK laid the foundation for a scalable CRM infrastructure. With reusable modules, BLADE can now adapt quickly to new campaign needs, personalize at scale, and maintain the premium brand standards their audience expects. ### Blade Elevates Luxury Travel App With Flutter Source: https://verygood.ventures/success-stories/blade-mobile-flutter-app/ Blade is a premier urban air mobility company transforming how people move through cities and across key global destinations. Built around speed, convenience, and a distinctly luxury experience, Blade operates high-frequency airport transfers, regional routes, and bespoke charter services for events like the Cannes Film Festival and Monaco Grand Prix. With a commitment to modernizing short-distance travel, Blade blends world-class hospitality with innovative aviation partners, creating seamless, elevated journeys for today’s most discerning fliers. **In just 8 weeks, VGV delivered Blade’s first client-facing mobile app: a fully redesigned, high-performance digital experience using Flutter, architected for speed, luxury, and long-term scalability across platforms.** ## The Challenge Blade, a pioneer in premium air mobility, partnered with Very Good Ventures (VGV) to reimagine and modernize its mobile experience and meet the evolving expectations of today’s elite travelers. While the app had served its purpose for many years, its legacy backend and older web technologies created challenges around scalability, performance, and delivering a smooth user experience. From high-frequency airport transfers in Manhattan and the Hamptons, to bespoke charters for global events like the Cannes Film Festival and Monaco Grand Prix, Blade’s clientele expects fast, elegant, and frictionless service—on the ground and in the air. To match that expectation, Blade needed a digital experience as seamless as its flights to reflect its premium brand, while also ensuring that its internal technology could scale effectively. ## The Solution VGV took a holistic approach that combined strategy, design, and engineering to deliver a digital experience as seamless and sophisticated as Blade’s flights. Our team focused on building a performant, future-ready foundation using Flutter—enabling unified development across iOS and Android while maintaining the premium design and high-end functionality Blade’s users expect. Through close collaboration with Blade’s stakeholders, we defined core experience principles, modernized the app’s architecture, optimized its design system, and implemented a scalable backend integration using Dart Frog. The result is a luxury mobile experience that balances elegance with technical precision and positions Blade for long-term growth and innovation. The new Blade app is built around three core experience principles: - **Get There Sooner:** Streamlined booking tools eliminate unnecessary friction, helping fliers get from point A to point B with maximum efficiency. - **Stress-Free Experience:** Intuitive flows and dependable tools simplify coordination, making travel feel effortless. - **Luxury-Level Services:** Every detail in the app reflects the Blade standard, with rich visuals and polished interactions designed for comfort, convenience, and delight. ![__wf_reserved_inherit](/assets/images/success-stories/blade-mobile-flutter-app/body-0.jpg) ## How We Did It To support these principles, VGV introduced a number of key innovations: - **Backend for Frontend (BFF) Architecture with Dart Frog:** Dart Frog was implemented as a Backend for Frontend (BFF) layer to abstract backend complexities. This allowed the mobile app to function smoothly with progressive development by simulating real data before the backend APIs were ready, ultimately giving Blade the flexibility to refine its APIs. - **Modernizing the Mobile Experience with Flutter:** The app was rebuilt using Flutter, providing Blade with a unified codebase that delivers native performance across iOS and Android, enabling a more efficient and consistent multi-platform experience. - **Design System Optimization:** VGV expanded upon Blade’s initial designs to create a robust and scalable design system, refining existing UI components and crafting new ones to support evolving product needs. The result is a modern, high-end interface that reinforces Blade’s luxury brand while ensuring consistency across every screen. This flexible foundation enables seamless booking, intuitive flight management, and supports long-term growth with a cohesive visual language. ![__wf_reserved_inherit](/assets/images/success-stories/blade-mobile-flutter-app/body-1.jpg) ### Growth Strategy in Motion Beyond app development, VGV collaborated with Blade to shape flexible, agile strategies that evolve with their business needs. Together, we identified dozens of potential ideas and then prioritized the top opportunities based on their business impact and potential lifts. Key wins include: - A modular design kit (MDK) for personalized, scalable email communication, built to boost engagement and conversion rates while reducing redundant work and driving operational efficiency. - A Braze acceleration playbook to unlock more value from Blade’s marketing stack. - A prioritized roadmap of future enhancements based on business impact. This approach ensured that the initiatives we pursued not only improved customer engagement and operational efficiency, but also aligned effectively with Blade’s growth goals. ## The Result Blade’s revamped mobile app isn’t just a redesign; it’s a redefinition of what luxury travel looks like in a digital-first world. Today, Blade is equipped with a premium, scalable, and future-ready digital experience that reinforces its position as a leader in the air mobility space. - **Improved Performance & Stability:** A modern architecture supports faster development and smoother user interactions. - **Elegant and Cohesive Design:** The app’s UI reflects Blade’s brand ethos and consistently exceeds customer expectations. - **Efficient multi-platform development:** Using Flutter, Blade now has a cost-effective and scalable mobile solution. - **Scalable Foundation**: Built for growth, the platform enables future integrations, VIP experiences, and global expansion. ### Cross-Platform Game Development With Flutter Source: https://verygood.ventures/success-stories/cross-platform-flutter-game/ Landover is a Catan-inspired strategy board game that transports you to a vibrant world of islands and resources. Play solo games against a wide array of different bots all with varying skill levels, personalities, and play styles. Or play up to 8 player games on giant maps with friends, family, or other Landover community members! ## The Challenge [Landover.io](http://landover.io) set out to build a modern strategy board game inspired by *Settlers of Catan* (including its *Cities & Knights* and *Seafarers* expansions), but fully reimagined for digital play across both mobile and web. The goal wasn’t simply to digitize a board game, but to create an engaging, scalable, and competitive experience that worked seamlessly on every device. To achieve this, the team faced several significant challenges: - **Cross-Platform Performance Demands**: Delivering smooth, native-like performance across iOS, Android, and web browsers with one unified codebase. - **Real-Time Multiplayer Synchronization**: Ensuring accurate, low-latency state management for multiple players across devices during live matches. - **Complex Game Logic**: Replicating nuanced rules, expansions, and dynamic map creation in a backend system while keeping the experience intuitive for users. - **Scalable Architecture**: Designing infrastructure that could handle thousands of concurrent games and future growth. - **Consistent User Experience**: Adapting layouts, interactions, and visuals for small mobile screens and large web browsers without compromising usability. - **Competitive Features & Monetization**: Supporting rankings, premium subscriptions, and custom maps to keep players engaged and generate revenue. ## ![Landover strategy board game interface with island map, resources, and multiplayer gameplay on mobile and web](/assets/images/success-stories/cross-platform-flutter-game/body-0.jpeg) ## Our Solution To solve Landover’s unique challenges, the Very Good Ventures team defined a clear technical and design strategy. The goal was to create a scalable, real-time game that could live on both mobile and web without sacrificing performance or user experience. We accomplished this by combining cross-platform development, real-time backend services, and a user-first design philosophy, including: - **Building once, deploying everywhere with Flutter** so the same codebase powered iOS, Android, and web experiences. - **Running multiplayer logic on Firebase** using Firestore, Cloud Functions, Authentication, Hosting, and other services to keep games synchronized in real time. - **Designing for consistency across devices**, with responsive layouts and adaptive game mechanics that felt natural on both small screens and larger browsers. - **Supporting growth and competition from the start** by enabling features like custom maps, premium subscriptions, and ranking systems. - **Delivering** **in iterations rather than all at once**, launching early betas to validate mechanics, stress-test infrastructure, and gather real user feedback. ![__wf_reserved_inherit](/assets/images/success-stories/cross-platform-flutter-game/body-1.png) ## How We Did It Once the strategy was set, the team shifted to execution. Delivering a real-time strategy game across platforms required disciplined architecture, careful workflow management, and continuous iteration. Our process balanced backend reliability, frontend polish, and rapid feedback cycles to ensure stability and scalability. - **Architecture & Backend Development** - Defined a robust backend architecture using TypeScript and Node.js to handle complex game state, logic, and synchronization. - Leveraged Firebase’s real-time services (Firestore, Cloud Functions) to provide a reliable source of truth for every game instance. - **Frontend & Cross-Platform Adaptation** - Built a responsive UI with Flutter to ensure one codebase worked seamlessly across devices. - Tailored layouts and interactions so the experience felt intuitive whether played on a phone, tablet, or browser. - **Testing & Iteration** - Validated core game mechanics (resource distribution, dice rolls, expansions) early in the build. - Expanded gradually with new features and refinements, guided by extensive playtesting and bug tracking. - **Organization & Project Management** - Adopted a Kanban-style workflow for clarity on tasks, timeframes, and ownership. - Scaled the team using staff augmentation when additional expertise was required. - **Launch & Feedback Loop** - Released an open beta to validate real-world usage and capture user feedback. - Used analytics and player insights to refine performance, balance gameplay, and improve engagement. ![Landover open beta gameplay showing cross-platform performance across devices with real-time multiplayer](/assets/images/success-stories/cross-platform-flutter-game/body-2.jpeg) ## The Result - **Reached over 19,000 monthly active players**, proving demand and validating the stability of the platform at scale. - **Delivered a seamless cross-platform experience**, where users could jump between iOS, Android, and web without losing quality or performance. - **Introduced competitive and monetization features**, including premium subscriptions, player rankings, and support for custom/user-created maps that kept the community engaged. - **Earned international recognition**, with support from the Uruguayan Government to showcase *Landover* at the Game Developers Conference (GDC) in California. - **Achieved a technical milestone**, becoming one of the first real-time strategy games successfully built and launched in Flutter., - **Created** **a foundation for future growth**, with an architecture designed to expand as new features, modes, and player communities emerge. ### 5-Star Air Carrier Scales Digital Experience Source: https://verygood.ventures/success-stories/five-star-air-carrier-flutter/ JSX is a 5-star air carrier revolutionizing short-haul travel in the United States. Combining the convenience of private aviation with attainable fares, JSX delivers a semi-private, frictionless flying experience across major routes. Known for its innovation and hospitality, JSX operates with a mission to make flying fast, fun, and joyful again. ## The Challenge JSX, a 5-star air carrier that offers the perks of private airfare at attainable fares, faced a pivotal moment in their growth journey. As the company scaled, it sought greater ownership of its digital customer experience to align with its brand and operational goals. Their outdated Xamarin-based mobile app posed significant challenges: - **Limited Ownership:** JSX’s reliance on external expertise and outdated social tools hindered their ability to adapt and innovate. - **Technical Constraints:** The deprecation of Xamarin and the complexity of Navitaire's backend systems slowed progress and stifled opportunities. - **Growth Ambitions:** Expanding beyond core flight services to ancillary revenue streams (e.g. loyalty programs, rental cars, and additional services) required digital solutions capable of supporting new offerings.  JSX needed a partner to deliver a modern app that would not only address these challenges, but also: - Enable scalability, innovation, and operational control - Focus on driving revenue - Enhance the customer experience   ## Our Solution We partnered with JSX to build a Flutter-based mobile app that combined modern design, robust engineering and scalable architecture with an overarching focus on the customer experience and revenue growth opportunities. > “We knew VGV were the Flutter experts. That aspect in itself was a no-brainer. When we made the decision that Flutter was right for us, we wanted VGV.” – JSX Digital Product Owner ![__wf_reserved_inherit](/assets/images/success-stories/five-star-air-carrier-flutter/body-0.avif) This new end-to-end transformation delivered: - **Best-in-Class Code Architecture:** A robust, modular, and scalable app architecture based on Flutter, adhering to strict best practices and high-quality engineering standards. - **Scalability for Growth:** An app structure and experience built to support new revenue opportunities and expand JSX’s digital ecosystem. - **Modern UI/UX Design:** A clean, intuitive interface that embodies JSX’s brand and drastically improves usability. - **Backend Integration Excellence:** Seamless backend connectivity by leveraging Navitaire’s Dart SDK to streamline integration, ensuring seamless backend connectivity. - **Process Transformation:** Improved workflows and knowledge sharing processes that gave JSX empowered ownership, helping them transition from supporting their digital products to fully owning them.  > "The team has done an amazing job. Our team has had nothing but positive things to say. Thank the team for me as they've performed this transition above expectations." – Craig Airitam, Chief Information Officer, JSX ![__wf_reserved_inherit](/assets/images/success-stories/five-star-air-carrier-flutter/body-1.avif) ## How We Did It **Discovery & Design Foundations:** We began with a comprehensive UX/UI audit to align designs with JSX’s brand identity and operational needs. By creating reusable components and streamlined workflows, we developed a scalable design system that delivered a consistent, high-quality user experience. Close collaboration with stakeholders ensured strategic architectural decisions to support JSX’s growth and digital ambitions.  **Development & Delivery:** Using Flutter, we built a modular, enterprise-grade app with 100% test coverage, CI/CD pipelines, and automated quality checks for reliability and scalability. The team overcame complex integration challenges with Navitaire’s backend, optimizing internal processes for efficiency. **Beyond the App:** The VGV design team extended JSX’s brand beyond their app, crafting an integrated UX/UI experience across key touchpoints. This included the Flight Information Display System (FIDS), providing real-time flight updates in JSX hangars, and the In-Flight Web Home Page, a responsive platform offering flight status, refreshment options, and exclusive promotions for travelers.  **Knowledge Transfer & Offboarding:** VGV delivered a seamless handoff, praised by JSX leadership as one of their most effective transitions. This collaboration empowered JSX to take ownership of its platform and continue innovating confidently. > “We are a better team because of your team. We are very proud of our process and product now.” – JSX Digital Product Owner ![__wf_reserved_inherit](/assets/images/success-stories/five-star-air-carrier-flutter/body-2.avif) ### Holobooth Photo Experience Powered by Flutter Source: https://verygood.ventures/success-stories/flutter-forward-holobooth/ Google We partnered with Google and the Tensor Flow team to create a virtual photo booth experience showcasing Flutter and machine learning. The demo was featured as part of the Flutter Forward event in January 2023. ## TensorFlow.js for facial detection The Holobooth builds on the first version of the [Photo Booth app from Google I/O 2021](https://medium.com/flutter/how-its-made-i-o-photo-booth-3b8355d35883). This version uses the [camera](https://pub.dev/packages/camera) plugin for web to detect a user’s face within the frame of a camera and the [TensorFlow.js](https://www.tensorflow.org/js)[MedaPipe FaceMesh model](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection) to live map facial features within the frame of a camera. This allowed us to create an immersive and futuristic photo booth experience where a user’s facial expressions are reflected in real-time on a virtual Dash or Sparky. ### Rive background animations and avatars Beyond face detection, we needed to animate the app's background and Dash and Sparky avatars to create a high-quality, interactive, and fun experience for users. We turned to [Rive](https://rive.app/), a web app built in Flutter specializing in highly performant animations, to add a spark to Dash and Sparky. We used Rive [State Machines](https://help.rive.app/editor/state-machine) to control how the avatars responded to movements so the avatar could mimic a user's behavior and expressions. By utilizing the [FaceMesh](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection) model’s feature detection, we were able to correlate the detected features to specific coordinates on our avatar models. To transform the input from the models and determine the appearance of the avatar on screen we use the StateMachineController. ![Rive-animated Dash and Sparky avatars mimicking user facial expressions via TensorFlow.js face detection](/assets/images/success-stories/flutter-forward-holobooth/body-0.gif) ### Sharing your experience with the help of Firebase Playing with the animations and avatars in real-time sounds pretty fun, but sharing the fun makes it that much cooler! Using [Cloud Functions for Firebase](https://firebase.google.com/products/functions) and [Cloud Storage for Firebase](https://firebase.google.com/products/storage), we were able to create a GIF or video that users can share to social media. With the click of a button you can share your Holobooth experience immediately with friends who can click into your post and create their very own! ![Holobooth sharing flow using Firebase to generate and post animated GIFs to social media](/assets/images/success-stories/flutter-forward-holobooth/body-1.webp) ### Scalable architecture Of course, scalable architecture and testing practices are at the heart of everything we do. We used [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli) to generate the project, which meant our first commit had null safety, internationalization, and 100% unit and widget test coverage. Check out the [open source code](https://github.com/flutter/holobooth) for how we break down our code into a [layered architecture.](https://verygood.ventures/blog/vgv-approach-to-application-architecture/) ### Machine learning and what’s to come Flutter Forward Holobooth was our first major project incorporating machine learning into a Flutter app. Much of our initial development work involved experimenting with various machine learning models and figuring out how to incorporate TensorFlow.js into a Flutter web experience. We’re excited to further explore how to incorporate machine learning into digital experiences for users on any device. [Give Holobooth a try!](https://holobooth.flutter.dev/) ## Stats ## What's Next ## How It's Made A deep dive into how we built the Flutter Forward Holobooth, including code snippets from the app and solutions for challenges we encountered. [Read article on Medium →](https://medium.com/flutter/how-its-made-holobooth-6473f3d018dd) ## Open source code The codebase for Flutter Forward Holobooth is open source in the Flutter repository. [View on GitHub →](https://github.com/flutter/holobooth) ### Flutter News Toolkit Helps Publishers Ship Faster Source: https://verygood.ventures/success-stories/flutter-news-toolkit/ Google is a global company with hundreds of thousands of employees around the world. The company is most known for the Google search engine, as well as numerous other cloud computing, internet, hardware, and software services. Google launched Flutter in 2017 as a solution to build apps for all screens and has since partnered with us numerous times to demonstrate Flutter’s amazing potential. ### The Challenge In a rapidly changing media landscape, news publishers are under growing pressure to deliver polished, full-featured mobile apps for both iOS and Android. Yet many lack the engineering resources to build two native applications, maintain them, and integrate modern features—all without inflating costs or delaying time to market. In addition: - Many local news outlets have tech teams focused on web rather than mobile. - Developing separately for iOS and Android doubles effort, cost, and maintenance burden. - Publishers need best practices, templates, and integrations to reduce overhead and avoid reinventing the wheel.  ### Our Solution To address these challenges, Google and Very Good Ventures teamed up to design and build the [Flutter News Toolkit](https://flutter.dev/news): an open-source, best-practice toolkit that empowers publishers to rapidly build, customize, and deploy high-quality mobile news apps from a single codebase. This unified toolkit: - Supports both iOS & Android from one codebase, reducing duplication. - Incorporates design templates, UI components, and workflows based on research with publishers around the world. - Includes built-in features like navigation, search, trending stories, ad serving, account management, newsletter signup, social sharing, and subscription support. - Provides an open-source model so publishers can adapt, extend, and integrate with existing systems. “Very Good Ventures is laser focused on process and making life easier for the developers. Their team is very responsive, and we definitely appreciate their transparency, flexibility, and attention to detail.”  > "Very Good Ventures is laser focused on process and making life easier for the developers. Their team is very responsive, and we definitely appreciate their transparency, flexibility, and attention to detail.” **- Timothy Hoang, Technical Solutions Product Manager**, Google ### How We Did It The implementation involved collaboration across design, engineering, and publication stakeholders. Together, we used both strategic planning and rapid prototyping to ensure the toolkit met real publisher needs and could be used effectively—even by teams with limited resources. - **Audit & Research:** We conducted in-depth audits of existing news sites and apps, plus interviews with publishers (a global mix of both large and small organizations) to understand use cases and priorities. - **Template Definition:** The team defined a core template experience of content objects and UI components that could be easily customized for branding—covering logos, colors, typography, and layout. - **Cross-Platform Development:** We leveraged Flutter and Dart for unified app development across iOS and Android, while using tools like Dart Frog for backend functionality to enable shared code and tooling. - **Feature Prioritization:** The team prioritized features based on their value to end users, ensuring strong performance, accessibility, and internationalization from the outset. - **Rapid Prototyping & Delivery:** We built an MVP—with leading Moroccan [news outlet Hespress](https://verygood.ventures/success-stories/hespress-news-app/)—in just six weeks, then expanded into a full toolkit delivered over six months, enabling publishers to adopt quickly and scale over time. > "This toolkit is something that publishers had been asking for, and we executed this project with Very Good Ventures in a very short amount of time.” -**Zoey Fan, Senior Product Manger**, Google ### The Result **80%** Less time for publishers to launch a mobile app **10%** Audience size increase **6 weeks** To launch an app to multiple platforms The toolkit got its first use with [leading Moroccan news outlet Hespress](https://verygood.ventures/success-stories/hespress-news-app/). With Very Good Ventures, they used the toolkit to create a new English language app. In only 6 weeks, VGV was able to partner with Hespress to develop their apps from start to finish, utilizing login, strategic ads placement, subscription, newsletter engagement, search, UI best practices, and more.  ##### Why Google built the Flutter News Toolkit and watch a sample app get set up ### Fitting the World Cup in Your Pocket If you are a news outlet and your national football team is contending for a title in the biggest tournament in the world, you know that readers want the latest news about their team right in their pockets. It shouldn’t matter whether that pocket has an Android or an Apple phone; they just want to cheer for their team.  At the close of 2022, [Moroccan news outlet Hespress](https://verygood.ventures/success-stories/hespress-news-app/) provided the latest news about the national team through their new multi-platform English language app. With the help of VGV and the Flutter News Toolkit, they were able to build the app in just six weeks. “Building mobile apps is very difficult and challenging for news publishers who have solid name recognition but only a handful of employees other than the editorial staff,” said Zoey Fan, Senior Product Manager at Google. “We sought a technical solution and methodology that would make it possible for these organizations to develop mobile apps with limited resources.” “We calculate that within the first year of launching a mobile application, a news outlet can increase by 10% daily active users or monthly active users, said Timothy Hoang, Technical Solutions Product Manager at Google. ### The Challenge of Mobile App Development  “Many local news outlets don’t have the engineering resources of a network news organization or publishing conglomerate,” said Timothy Hoang, Product Manager at Google. “Their tech stacks are dedicated to the website, and they likely don’t have mobile developers on staff.” Repurposing teams primarily familiar with and responsible for maintaining a web presence puts the site performance at risk and sets the stage for excruciatingly slow mobile app development. The costly alternative is adding personnel. “For these outlets, their primary pain points revolved around funding additional teams to perform both Android and iOS development,” said Hoang.  The Flutter team at Google wanted to build on the years-long efforts of the Google News Initiative maintaining and publishing best practices from news organizations of all sizes. Hoang and Fan imagined a resource that brought in those best practices and popular features to help news organizations implement the features they wanted, customize as necessary, and launch quickly.  For the technology challenge, Flutter was a natural fit for development across different mobile devices. A Flutter app built from one single codebase can be deployed to Android, iOS, and other platforms. Instead of doubling the effort and writing apps for both iOS and Android, developers can build the features once and be able to deploy to both platforms.  The toolkit can also be used by larger publishers with existing native apps seeking to replace or streamline an app. The Flutter News Toolkit and Very Good Ventures development expertise can migrate to Flutter in weeks.  Flutter has huge advantages, but the migration brings a shift in programming languages, a change in styles, and a new widget toolkit. Both frontend development and the design team are affected. Very Good Ventures has a proven training process to build the skills within the team so that teams can [shift to Flutter efficiently](https://verygood.ventures/solution/flutter-transformation/). ### Unlocking the Potential of Flutter and Dart  Very Good Ventures has proven processes for developing applications in Flutter that have been key in the rapid development of the Flutter News Toolkit.  For example, one of the open source tools within the Flutter News Toolkit is [Dart Frog](https://dart-frog.dev/), a fast, minimal backend framework for Dart developed by Very Good Ventures. Dart Frog unlocks many new capabilities for developers already using Dart, including the ability to share code, tooling, and processes across the front and backend. “Very Good Ventures is laser focused on process and making life easier for the developers,” said Hoang. “They find economies of scale everywhere, always suggesting technologies that improve efficiency, like setting us up so that developers only need to learn Flutter and Dart to work on both front and backend development.” Beyond deep technical experience, Very Good Ventures also offers an eagerness to collaborate. “The team at Very Good Ventures is very responsive, and we definitely appreciate their transparency, flexibility, and attention to detail,” said Hoang.  “They kept us on time and on budget by suggesting which features to prioritize based on the value to the users. They provided us regular updates on implementations, bug fixes, and outstanding tasks, so we never had to wonder about the project status,” said Hoang. ### Quality Design Out of the Box Users of the toolkit don’t just get Google News Initiative best practices and Flutter code. They get the benefits of Very Good Ventures’ design insights and approach as a starting point for their app. At the outset of the design process, VGV Design sought to find a fundamental set of features that would provide the greatest value to publishers. In addition to a broad audit of news sites and mobile apps, the team conducted interviews with publishers across the globe to understand their needs. VGV spoke with news organizations large and small, with a diverse mix of editorial emphases: from regional reporting, to niche topics, to general news. They took the common patterns they observed and created a template experience with a core set of content objects and user interface components that different publishers can easily configure to deliver their distinct editorial experiences. The template addresses publishers’ brand identity needs as adeptly as editorial ones. VGV’s research, plus their experience creating and implementing design systems and white-label experiences for clients, informed the template’s visual customization features. It includes simple but powerful theming capabilities that let publishers easily and consistently apply their brand identity — logo, color palette, typography, etc. — to the entire application. > “Very Good Ventures puts a lot of design guidance into the toolkit as well. The template is not just a block of code. It offers a beautiful UI to start with. The user can easily customize, but they start from a great looking, performant app,” said Fan. ### Expanding the Audience for Hespress The toolkit got its first use with [leading Moroccan news outlet Hespress](https://verygood.ventures/success-stories/hespress-news-app/). With Very Good Ventures, they used the toolkit to create a new English language app. In only 6 weeks, VGV was able to partner with Hespress to develop their apps from start to finish, utilizing login, strategic ads placement, subscription, newsletter engagement, search, UI best practices, and more. In addition to leveraging the toolkit features available out of the box, Hespress worked with VGV to design and implement article commenting to further drive user engagement.. As the Moroccan team [went deep into the FIFA World Cup tournament](https://en.hespress.com/54531-morocco-beats-spain-in-penalties-moves-to-quarter-finals.html), users from around the world kept up to date via the new Hespress mobile app. ### Open Source and Supported for Longevity The toolkit is more than a one-time fix for development; just as Flutter is an open source framework, the Flutter News Toolkit is also open source. The toolkit will be maintained by Google with the help of the community. “We welcome pull requests from developers with new features and fixes,” said Fan. ### Finding a Partner in Very Good Ventures “The Flutter News Toolkit required collaboration between many working groups in and out of Google. It was so satisfying to build a solution that our users – our publishers – wanted,” said Fan. From design to product enhancement, Very Good Ventures was able to produce the Flutter News Toolkit in just six months.  The initial development of the MVP only took six weeks. “This toolkit is something that publishers had been asking for, and we executed this project with Very Good Ventures in a very short amount of time,” said Fan.  Very Good Ventures provided more than technical capability. “The Very Good Ventures team is just so organized,” said Fan. “I just have a lot of confidence in them. When they take on a project, I know we will get good results.” ### Key Features in the Flutter News Toolkit Built from data-driven best practices, any organization who implements the toolkit has, out of the box: - Navigation - Search - Trending Story ("Recirculation") Card - Page templates, including video - Ad-serving options - Premium content - Account management, including onboarding - Newsletter sign up - Social sharing - Subscription - Google -  Ads support ### A Score for News Organizations Around the World With Very Good Ventures guiding the way, the team put the Flutter News Toolkit together on time and on budget, clearing the way for publishers all over the globe to quickly and easily deploy their mobile applications.  The toolkit provides the equivalent of at least three months of development on day one, with all the core services integrated and even prebuilt continuous integration/continuous deployment automations. Best practices for accessibility and internationalization are already in place. Organizations can fully customize their apps and use or replace any part they choose. Thanks to the toolkit, Hespress was able to transmit the very good news of the national team’s FIFA victories to fans around the world. For publishers interested in the Flutter News Toolkit, Google has made the open source repo available. For organizations seeking to accelerate deployment or manage the complexities of migration, Very Good Ventures offers additional expert resources. ## Stats ### 10% audience size increase --- ### 6 weeks to launch an app to multiple platforms ### Building the First Major Flutter macOS App Source: https://verygood.ventures/success-stories/flutter-slides/ Google is a global tech company with hundreds of thousands of employees around the world. The company is most known for the Google search engine, as well as numerous other cloud computing, internet, hardware, and software services. Google launched Flutter in 2017 as a solution to build apps for all screens and has since partnered with us numerous times to demonstrate Flutter’s amazing potential. Google tasked us with building a slides desktop application for macOS using Flutter to demonstrate how Flutter Desktop Embedding works. ### First Major Flutter Desktop App Google regularly turns to Very Good Ventures to demonstrate Flutter's capabilities — we built Hamilton, the first commercial mobile app using Flutter, as well as the first web app, KENKEN for The New York Times. For this project, we used Desktop Embedding — an implementation of Flutter that enables Flutter apps to run on Windows, macOS, and Linux — to build a slides presentation app. ![Flutter Slides desktop presentation app running on macOS using Flutter Desktop Embedding](/assets/images/success-stories/flutter-slides/body-0.webp) ### Customizable Slides We designed the slides to contain rich text and had to make them resizable to fit any window so that users would have a similar experience when viewing on mobile compared to desktop. We also implemented navigation controls to move through the slides. All slide content is contained within one JSON file for easy updates in one place. ![Customizable resizable slides with rich text and navigation controls powered by Flutter](/assets/images/success-stories/flutter-slides/body-1.gif) ### Animations and Embedded Code The slides applications includes a variety of transitions, scaling effects, and carousel and other useful animations. But the shining star of the project was demonstrating that any Flutter code could run as a desktop app. Now, instead of limiting presentations to screenshots or video captures of code, the app can embed code and run real apps directly in the slides. ## Project Summary Google turned to us to create a slides application in Flutter, complete with customizable design elements and animations. At Flutter Live '18, Tim Sneath delivered the keynote and revealed that his entire presentation was running in the Flutter app that we built! ## Stats ### 120k+ Views on YouTube --- ### 200+ Stars on slides open source repository --- ### 100% Code re-usable between desktop and mobile ## Outcomes ### Outcome 1 #### Animations Made Easy One of the major animations we created for the presentation was the unique four-box animation we created for Flutter's “Four Pillars.” With Flutter we implemented this effect easily — something that would not possible in typical presentation tools. ### Outcome 2 #### Build for Mobile and Desktop Our slides application was the first major application built in Flutter for desktop. In addition to creating mobile applications with Flutter, we could develop for desktop without having to learn a new language. ### Outcome 3 #### Dedication to Open Source Participating in the Flutter Community is essential to our work. After the conference, we made the code for the slides application available as a Flutter [open source repository](https://github.com/timsneath/slideplayer) on GitHub. ### High-Performance Unlimited Comics App Launch Source: https://verygood.ventures/success-stories/globalcomix-flutter-app/ GlobalComix serves as a no-cost digital reading and distribution platform, facilitating creators and publishers in the sharing and sale of their comics. It offers global licensing opportunities and provides deeper insights into their readership. ## The Challenge GlobalComix—a digital distribution platform for creators to publish and sell comics worldwide—wanted to build native-style mobile apps for iOS and Android. Their goal was to give comic enthusiasts a dynamic, immersive reading experience with access to a wide variety of story types, art styles, and formats. At the same time, they needed to preserve high visual quality while adapting their existing website into mobile, ensuring user-friendly interactions, smooth navigation, and responsive image and data handling. Key obstacles included: - **Ambitious Deadlines:** Development needed to start before final designs were complete. - **Reader Experience Expectations:** Users demanded intuitive features like gestures, pinch-to-zoom, and multiple reading formats (single page, double page, scrolling). - **Backend Alignment:** The backend was already built, requiring careful coordination to align endpoints and integrate with the new mobile architecture. - **High Visual Standards:** The apps had to deliver crisp, responsive visuals while handling diverse comic formats efficiently. ![__wf_reserved_inherit](/assets/images/success-stories/globalcomix-flutter-app/body-0.png) ## Our Solution To address GlobalComix’s challenge, we proposed a structured strategy combining design, development, and project management best practices. The solution focused on delivering a refined mobile reader experience while managing scope, workflow, and quality throughout the build. This included: - **Flexible Scope Design:** Wireframes were supplied early, and additional screens/specs were added dynamically as the project progressed. This allowed better estimation of each section and ensured the mobile app matched the intended functionality. - **User-First Reader Experience Design:** After exhaustive analysis of reader gestures and preferences, the app supported single-page, double-page, scroll, zoom, etc., delivering reading mechanisms optimized for mobile use. - **Backend Alignment & Architecture Assessment:** Because GlobalComix already had an existing backend, VGV mapped endpoints, understood what existed, and planned next steps in partnership with their team to ensure seamless integration. ![GlobalComix mobile app reader experience with backend integration and flexible scope design](/assets/images/success-stories/globalcomix-flutter-app/body-1.png) ## How We Did It Turning this solution into reality required close collaboration, iterative development, and a focus on execution. Very Good Ventures partnered with GlobalComix throughout the build to ensure every feature matched both technical requirements and reader expectations. We achieved this by: - **Building alongside design**, developing functionality early and refining as the design system matured. - **Collaborating continuously** by holding regular check-ins and progress reviews to adapt quickly to feedback. - **Refining interactions through testing**, including iterating on gestures, scrolling, and zooming until they felt seamless and natural for readers. - **Delivering in iterative stages** by rolling out features incrementally, gathering input, and improving rapidly to stay on schedule. ![GlobalComix iterative development process with gesture-based comic reading and seamless scrolling](/assets/images/success-stories/globalcomix-flutter-app/body-2.png) ### The Result - High-quality visuals adapted for mobile devices, across various comic formats. - An intuitive, seamless reading experience, making it easier for users to interact via gestures, zooming, and tailored layouts. - Integration with the app’s existing backend in a way that preserves performance and data integrity. - Met the ambitious delivery schedule through close collaboration, continual feedback, and efficient project management. ### Modernizing Tax e-Filing With Flutter and Cloud Source: https://verygood.ventures/success-stories/h-a-berkheimer/ Berkheimer has over 80 years of successful collection and administration for the state of Pennsylvania. As the state’s largest tax administrator, efficient collection and distribution of state revenue is paramount. Building a mobile app with Flutter ensured that the app could be accessible to all taxpayers while keeping development costs as efficient as possible. We built a best-in-class E-File app for Pennsylvania’s largest tax authority. ### The Challenge Tax authorities face growing pressure to deliver digital tools that are not only secure and reliable, but also intuitive for everyday users. For H.A. Berkheimer, Pennsylvania’s largest tax authority, the need was urgent: their existing electronic filing and payment systems were outdated, difficult to navigate, and not optimized for mobile. Key challenges included: - **Outdated technology** that couldn’t scale or meet modern security expectations. - **Poor user experience**, especially for non-technical or first-time users. - **Limited mobile support**, leaving taxpayers unable to file or pay conveniently on their devices. - **Lack of integrations** with secure payment platforms and mobile document capture. ![H.A. Berkheimer outdated tax filing system highlighting the need for mobile-first modernization](/assets/images/success-stories/h-a-berkheimer/body-0.webp) ### Our Solution We partnered with Berkheimer to create a new, mobile-first E-File app built in Flutter. Our solution prioritized usability, security, and seamless integration, ensuring that taxpayers could quickly and confidently complete critical tasks. App elements included: - **Seamless Integrations:** Payment processing via the Official Payments platform and document capture with the Kofax SDK. - **User-Centric Design**: An intuitive wizard-style flow guiding users step-by-step. - **Modern & Secure Cloud Backend**: Infrastructure designed for performance, high availability, and compliance. ![Berkheimer E-File Flutter app with wizard-style tax filing flow, payment processing, and document capture](/assets/images/success-stories/h-a-berkheimer/body-1.webp) ### How We Did It Delivering a secure, intuitive, and scalable app required a thoughtful approach to design and engineering. Our process included: - **Design & UX Mapping**: We created user journeys, prototypes, and interfaces that emphasized clarity, ensuring even first-time users could navigate complex filing tasks. - **Technology & Architecture**: Flutter was selected to deliver cross-platform consistency. Our team designed and deployed a secure cloud backend to handle sensitive taxpayer data with confidence. - **Integrations**: We implemented payment APIs for official processing and integrated Kofax SDK for document capture using mobile cameras. - **Testing & Launching:** Rigorous usability testing, performance stress testing, and security audits ensured the app was reliable at launch. Our team rolled this out incrementally to monitor adoption and refine the experience. ![Berkheimer tax app design and UX mapping with user journey prototypes for intuitive e-filing](https://cdn.prod.website-files.com/68c903027170e246562111c9/68df6ffaf5e6dc324f903288_68d5608fa6151379d2f5bdeb_HAB%2520graphic%25201.png) ### The Result #### Multi-platform Mobile App VGV used Flutter to create the best tax app for PA residents accessible to both iOS and Android mobile devices. Eliminating the duplicative costs of developing for multiple platforms was essential to Berkheimer’s efficiency goals. #### Support & Enhancement Since Berkheimer does not have an in-house mobile team, VGV has been a continuous partner to ensure long-term app success. Our experts monitor the app, apply updates, fix bugs, and continuously improve and enhance the product as its requirements and features grow. ### Stats **100s of 1000s** Annual tax dollars processed **2x** YoY user growth ## Project Summary Berkheimer Tax Innovations is Pennsylvania’s largest and most trusted tax administrator in Pennsylvania, processing millions of dollars in state revenue. As the industry leader, Berkheimer wanted to modernize and improve the user experience of filing and paying PA taxes. VGV created a clean, intuitive design and scalable Flutter app to streamline business operations and usher in a new era of customer experience. ## Stats ### 100s of 1000s Annual tax dollars processed --- ### 2x Year over year user growth ## Outcomes ### Outcome 1 ![Berkheimer E-File app on iOS showing the cross-platform Flutter tax filing experience](/assets/images/success-stories/h-a-berkheimer/body-2.png) ![Berkheimer E-File app on Android demonstrating multi-platform mobile tax payment](/assets/images/success-stories/h-a-berkheimer/body-3.png) ### Outcome 2 #### Multi-platform Mobile App VGV used Flutter to create the best tax app for PA residents accessible to both iOS and Android mobile devices. Eliminating the duplicative costs of developing for multiple platforms was essential to Berkheimer’s efficiency goals. ### Outcome 3 #### Support & Enhancement Since Berkheimer does not have an in-house mobile team, VGV has been a continuous partner to ensure long-term app success. Our experts monitor the app, apply updates, fix bugs, and continuously improve and enhance the product as its requirements and features grow. ### Hespress Launches News App in 6 Weeks With Flutter Source: https://verygood.ventures/success-stories/hespress-news-app/ Hespress is the leading Moroccan online news website, founded in February 2007. The Hespress English language service team produces news articles, analysis, and feature stories for readers with a wide variety of interests—providing rich coverage of everything from politics and social issues to cultural, sports and regional happenings. ### The Challenge As Morocco’s first and most-visited online news platform, Hespress was ready to expand beyond its Arabic readership by offering an English-language app to reach international audiences and the Moroccan diaspora.  But they faced a series of obstacles that made a fast, cost-effective launch difficult. Building separate iOS and Android apps would have consumed too much time and resources, and the team needed a solution that could guarantee speed, scalability, and performance without compromising on user experience. At the same time, Hespress wanted to differentiate themselves in a crowded media landscape by ensuring their app could stand up to high reader expectations. Key challenges included: - **Limited Timeline:** The app needed to be live in just a few weeks to capture a growing audience. - **Cross-Platform Complexity:** Separate iOS and Android builds would double development and maintenance costs. - **Performance Concerns:** Readers expected lightning-fast load times and reliable push notifications. - **Ad-Blocker Disruption:** Hespress needed a way to serve updates and content effectively even with ad blockers in play. - **Team Readiness:** Internal developers were familiar with native platforms, but not yet with Flutter or Dart. ### Our Solution To meet these challenges, Hespress partnered with VGV and selected the [Flutter News Toolkit](https://verygood.ventures/success-stories/flutter-news-toolkit/) as the foundation for its app strategy. This approach was chosen for its speed, scalability, and ability to reduce costs while ensuring performance across platforms—and it emphasized long-term sustainability so Hespress could continue evolving its digital products after launch. Together, we: - **Used the Flutter News Toolkit** to accelerate development with a pre-built, news-focused architecture. - **Consolidated development** into a single cross-platform codebase for iOS and Android. - **Established a future-proof design system** with reusable components and flexible layouts. - **Equipped Hespress’s team** with Flutter expertise to manage ongoing development. - **Reduced launch time** and long-term maintenance costs through efficient tooling. > "VGV was responsive to our needs and had an optimized workflow that made the whole process effortless. We were pleased with how the team made sure the final product met our expectations and did the extra work to finalize all the details at the point of launch.” **\-Yassin Bendaoud, Social Media Manager,** Hespress ### How We Did It The project was executed as a tightly managed six-week engagement, with VGV guiding the Hespress team through a collaborative and hands-on process. The focus was on execution excellence—taking the strategic plan and breaking it into actionable, efficient steps. This included: - **Customizing the Flutter News Toolkit** to fit Hespress’s brand and editorial needs. - **Training Hespress engineers** in Dart and Flutter to build confidence and capability. - **Aligning Figma designs** directly with Flutter code to streamline handoffs. - **Applying agile project management** to prioritize features and drive iteration. - **Delivering and testing critical features**—such as push notifications, instant updates, and optimized performance—before expanding functionality. > "VGV was responsive to our needs and had an optimized workflow that made the whole process effortless. We were pleased with how the team made sure the final product met our expectations and did the extra work to finalize all the details at the point of launch.” **\-Yassin Bendaoud, Social Media Manager,** Hespress ### The Result - Launched a polished iOS and Android app in just six weeks. - Engaged readers with faster load times, push notifications, and intuitive design. - Streamlined operations with one codebase instead of two. - Prepared internal teams to expand features like video, personalization, and new content formats. ## Project Summary VGV partnered with Hespress to launch a high-performance English-language news app in just six weeks, helping the publisher expand its reach beyond Arabic-speaking audiences and connect with a broader global readership. By using Flutter and the Flutter News Toolkit, the team streamlined development across iOS and Android, reduced long-term maintenance complexity, and delivered a scalable foundation built for speed, reliability, and future growth. ### Interactive Photo Booth Built for Google I/O Source: https://verygood.ventures/success-stories/i-o-photo-booth/ Google is a global company with hundreds of thousands of employees around the world. The company is most known for the Google search engine, as well as numerous other cloud computing, internet, hardware, and software services. Google launched Flutter in 2017 as a solution to build apps for all screens and has since partnered with us numerous times to demonstrate Flutter’s amazing potential. We partnered with Google to build an interactive photo booth experience for Google I/O 2021 using Flutter and Firebase. ### Camera plugin for web The main feature of the app is the camera. We created a camera plugin specifically for the web that follows the federated plugin architecture. We implemented two main APIs for initializing the camera and taking the photo. While we built this plugin for the I/O Photo Booth, the implementation is not specific to this project, and could be used to build other Flutter apps on the web. View the [open source code](https://github.com/flutter/photobooth/tree/main/packages/camera). ![I/O Photo Booth camera plugin for Flutter web using federated plugin architecture](/assets/images/success-stories/i-o-photo-booth/body-0.webp) ### Firebase backend integrations We used [Firebase](https://firebase.google.com/) for various backend functionality such as storage, hosting, and performance monitoring. [Firebase cloud functions](https://firebase.google.com/docs/functions) were a key part in developing the social sharing feature of the app. The cloud functions allowed us to generate social posts with the user's photo, metadata, and a link back to the I/O Photo Booth. ![Firebase backend powering social sharing, cloud storage, and performance monitoring for I/O Photo Booth](/assets/images/success-stories/i-o-photo-booth/body-1.webp) ### Scalable architecture We built the I/O Photo Booth with scalable architecture patterns that we implement at VGV. We used [Very Good CLI](https://github.com/VeryGoodOpenSource/very_good_cli) to generate the project, which meant our first commit had null safety, internationalization, and 100% unit and widget test coverage. We used [flutter_bloc](https://github.com/felangel/bloc) for a reliable and predictable state management solution. Implementing a feature-driven monorepo structure allowed our team to work on features in parallel and ensured that UI was separated from business logic. View the [codebase](https://github.com/flutter/photobooth). ## Project Summary We were excited to team up with Google to build [**I/O Photo Booth**](https://github.com/flutter/photobooth) for Google I/O 2021. Attendees were able to access a completely virtual photo booth experience on the web from their device of choice. Users could take selfies with Google mascots: Flutter's Dash, Android Jetpack, Chrome's Dino, and Firebase's Sparky and decorate their photos with props. Users could also share their photo creations to social media for the event! ## What's Next ![How It's Made deep dive article on building I/O Photo Booth with Flutter and Firebase](/assets/images/success-stories/i-o-photo-booth/body-2.png) ### How It's Made A deep dive into how we built I/O Photo Booth with Google, including code snippets from the app and solutions for challenges we encountered. [Read article on Medium →](https://medium.com/flutter/how-its-made-i-o-photo-booth-3b8355d35883) ![I/O Photo Booth open source Flutter repository on GitHub](/assets/images/success-stories/i-o-photo-booth/body-3.png) ### Open source code The codebase for I/O Photo Booth is open source in the Flutter repository. [View on GitHub →](https://github.com/flutter/photobooth) ### Advancing Flutter Game Development With I/O Pinball Source: https://verygood.ventures/success-stories/i-o-pinball/ Google is a global company with hundreds of thousands of employees around the world. The company is most known for the Google search engine, as well as numerous other cloud computing, internet, hardware, and software services. Google launched Flutter in 2017 as a solution to build apps for all screens and has since partnered with us numerous times to demonstrate Flutter’s amazing potential. We teamed up with Google to build I/O Pinball, powered by Flutter, Firebase, and the Flame Game Engine. ![Dash, Sparky, Chrome Dino, and Android Jetpack gathering around a pinball machine.](/assets/images/success-stories/i-o-pinball/body-0.png) ### I/O Pinball Powered by Flutter and Firebase A closer look at how we built a classic pinball experience using Flutter, Firebase, and the Flame Engine. [Read article on Medium →](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d) ## Project Summary Built for Google I/O '22, [I/O Pinball](https://pinball.flutter.dev/) showcases the power of Flutter for building games. We also used Firebase to keep track of scores for the global leaderboard and the [Flame Engine](https://flame-engine.org/) for elements like animations, physics, and collision detection. The full source code can be found on [GitHub](https://github.com/flutter/pinball). ### Dow Jones Cuts Development Costs 50% With Flutter Source: https://verygood.ventures/success-stories/marketwatch-stock-market-game/ Since 1882, Dow Jones has been finding new ways to bring information to the world’s top business entities. Beginning as a niche news agency in an obscure Wall Street basement, Dow Jones has grown to be a worldwide news and information powerhouse, with prestigious brands including The Wall Street Journal, Dow Jones Newswires, Factiva, Barron’s, MarketWatch and Financial News. ![Dow Jones MarketWatch Virtual Stock Exchange game built with Flutter for cross-platform deployment](/assets/images/success-stories/marketwatch-stock-market-game/body-0.svg) ### The Challenge Building a new mobile app was critical for Dow Jones’ MarketWatch team, but they faced a familiar dilemma: limited time, high expectations, and the need to support both iOS and Android audiences. The company wanted to launch a feature-rich version of its Virtual Stock Exchange game before the new academic year began, but traditional native development would require duplicate work, slowing delivery and driving up costs. - **Goal:** Engage the education and university market with a mobile version of the Virtual Stock Exchange. - **Deadline:** Launch before the semester start (~3 months). - **Constraint:** Native development meant separate iOS and Android codebases, adding time and cost. ### Our Solution Dow Jones turned to VGV and our expertise with Flutter to deliver a high-quality cross-platform app with a single codebase. By partnering with us, they could rapidly design, build, and test the app—ensuring it was beautiful, performant, and ready to scale. This included: - **Going Cross-Platform with Flutter**: Instead of writing and maintaining two separate native apps, the team built once in Flutter and deployed everywhere. This dramatically accelerated development and eliminated duplicate engineering effort. - **Focusing on Speed and Polish**: With just three months to launch, efficiency was paramount. Flutter’s Hot Reload and widget-based design enabled rapid iteration without sacrificing design quality, helping the team keep momentum while still delivering a refined experience. - **Choosing Future-Proof Technology**: Beyond this project, adopting Flutter positioned Dow Jones to scale and reuse code across future products. The app became a proof point internally that modern frameworks could deliver both cost savings and strategic value.  > “*Thanks to Flutter and VGV, we delivered on our goal of building a fun, engaging, and performant app for all screens and mobile devices in significantly less time and cost than if we built it natively. The team and thousands of players are very happy with the results." -* ***Clarence Kwei, SVP Consumer Technology,*** *Dow Jones* ### The Result **3 Months** Fast-tracked launch on iOS and Android **50% Cost Savings** Compare to native development Want an even closer look? Dive into all the technicalities with our in-depth [Flutter Showcase](https://flutter.dev/showcase/dow-jones)! ## Stats ### 50% reduction in development costs ### FinTech Platform Doubles Adoption With Flutter Source: https://verygood.ventures/success-stories/nav-fintech-flutter-app/ Nav is the leading financial health platform for small business with 1.7 million users. Nav has helped over 380,000 small businesses find financial products, and also provides small business owners access to an expansive network of business services like accounting, payroll, and tax services. ### The Challenge Nav, the leading credit and financial health platform for small businesses, aimed to become a mobile-first company with frequent deployments and provide small business owners with a dynamic and personalized experience.  They had already made progress by uniting their iOS and Android apps under Flutter, but by 2022 it became clear that more foundational work was required to shift toward mobile-first development. Key issues included: - **Access:** 67% of users were accessing Nav via mobile browser, rather than native apps, signaling that the mobile app experience needed to improve  - **Features:** Several core features present on the web app weren’t yet implemented or fully functional in the mobile apps - **Systems:** Their Flutter codebase and state-management system lagged behind and needed updates - **Functionality:** Frequent technical debt and outdated dependencies were slowing down new feature development. ![Before](/assets/images/success-stories/nav-fintech-flutter-app/body-0.webp) ### Our Solution VGV partnered with Nav on a nine-month engagement to overhaul their mobile development practices and train their internal team, with structured key components:  - Establish a **mobile-first strategy** by bringing feature parity to the apps and creating the expectation that all new features would launch on mobile first. - **Modernize the technology stack**, including upgrading Flutter dependencies, migrating to null safety, and adopting a scalable state management solution. - **Embed VGV experts** alongside Nav engineers and designers to transfer knowledge and accelerate delivery. - **Reshape workflows** to prioritize rapid iteration, regular releases, and data-driven decisions supported by analytics. - Build a **foundation of best practices** in testing, code hygiene, and deployment cadence to ensure long-term efficiency. > "You will hear this word over again: Trust. There was a genuine trust between our team and the VGV team. I speak for everybody; we did not think there was a VGV and a Nav team. There was one team.” **\-Zain Kahn, Head of Product**, Nav ![Nav mobile app interface showcasing the modernized Flutter experience for small business financial health](/assets/images/success-stories/nav-fintech-flutter-app/body-1.webp) ### How We Did It Executing this solution required more than planning—it demanded an embedded partnership where VGV operated as part of Nav’s team. Together, we combined strategic intent with hands-on engineering, design, and process refinement. The work unfolded in structured phases that gradually improved both the technology and the way Nav built for mobile. - **Kickoff & Integration**: VGV adapted to Nav’s workflows and set up paired programming sessions, shared project management practices, and embedded a designer to ensure seamless collaboration. - **Codebase Modernization**: We upgraded dependencies, migrated to Dart null safety, refactored state management, and cleared technical debt to make the apps more stable and scalable. - **Feature Parity & Readiness**: Mobile functionality was brought in line with the web app, creating consistency across platforms and positioning mobile as the default experience. - **Release Rhythm**: A two-week deployment cadence was established, supported by stronger testing, higher code coverage, and analytics integration for measuring feature impact. - **Team Culture**: Through daily pairing and communication, VGV became a trusted extension of Nav’s engineering team—fostering knowledge transfer, camaraderie, and long-term capability. > "VGV is by far the most tightly-integrated \[of our consultants\] with our team. They were in our channels, getting involved in the banter. It worked great for us. They acted as true partners and core members of our team.” **\-Ammon Lockwood, Director of Software Engineering**, Nav ### The Result **2x User Growth** In app registrations and monthly active users **200% Increase in Engagement** In average user logins per month ### NYT Launches First Commercial Flutter Web App Source: https://verygood.ventures/success-stories/new-york-times/ The New York Times is one of the most influential media companies on the planet and the 3rd largest publication in the US. Its online and mobile puzzle games are enjoyed by millions of daily users and are one of the company’s top revenue sources. With Flutter on the Web, app development became an astonishingly good technology solution. Google and the New York Times turned to VGV to show the world what is possible. Working with the incredible teams at the New York Times and Google, we created a multi-platform KENKEN game that showcases the power of Flutter on the web. New York Times KENKEN puzzle app made its debut on stage at Google I/O 2019. ### The Challenge The New York Times wanted to push the boundaries of digital publishing and demonstrate the power of Flutter beyond mobile. Their vision was to create a highly visible, multi-platform application that could run seamlessly on web, desktop, and mobile—all from a single codebase.  At the same time, the project needed to serve as a real-world, public proof of concept for Flutter Web, showcasing its potential to millions of users. This meant the app couldn’t just function—it had to be polished, responsive, and performant on every platform. Adding to the challenge, the team had to move quickly, iterating on design and development in tight cycles without the delays that usually slow down mobile app releases. In all, the team needed to: - **Demonstrate Flutter’s web capabilities** in a high-profile, commercial setting. - **Deliver a single app that worked seamlessly across six platforms:** iOS, Android, macOS, Windows, Web, and Mobile Web. - **Balance shared code with platform-specific polish**, ensuring each version felt tailored to the device. - **Enable rapid iteration and feedback loops** to meet deadlines and maintain design quality. - **Ensure production-level performance and reliability**, since the app would be showcased by both Google and The New York Times. ![NYT KENKEN puzzle app running across web, mobile, and desktop platforms from a single Flutter codebase](/assets/images/success-stories/new-york-times/body-0.webp) ### Our Solution Very Good Ventures partnered with The New York Times and Google to transform this vision into reality. The team selected KenKen, a popular and challenging number puzzle, as the perfect vehicle to showcase Flutter Web’s power.  By building the NYT KENKEN as a multi-platform application, the project could highlight Flutter’s promise: one codebase, multiple platforms, consistent performance. At every step, the focus was on making the app feel “right” no matter the device, without sacrificing the speed of iteration required for such a high-profile launch. Our approach included: - **Selecting KenKen as the Flagship App**: Its global recognition and engaging gameplay made it the perfect puzzle to demonstrate Flutter’s versatility and performance. - **Committing to a Single Codebase Strategy**: Instead of splitting resources across different platforms, the project fully embraced Flutter’s promise of “write once, run anywhere.” - **Prioritizing Responsive Design & Adaptability**: The app was designed to feel consistent and fluid on every platform, while still respecting each device’s form factor. - **Balancing Shared Logic with Platform Polish**: Strategic decisions were made on where to reuse code and where to add device-specific enhancements for a seamless user experience. - **Setting the Stage for Rapid Iteration**: From the outset, the team emphasized processes that would allow for quick feedback and tight collaboration between designers, developers, and stakeholders. ![KENKEN number puzzle gameplay with responsive design adapting to different device form factors](/assets/images/success-stories/new-york-times/body-1.webp) ### How We Did It Turning this strategy into reality required a disciplined engineering process and cutting-edge use of Flutter’s capabilities. We built a robust development workflow that supported experimentation, continuous delivery, and multi-platform deployment at scale. By combining automation with thoughtful testing and release practices, the team ensured the app wasn’t just a prototype, but a production-ready product capable of supporting millions of users. To do this, we: - **Implemented a CI/CD Pipeline**: Automated testing and builds ensured stability and consistency across six platforms while accelerating delivery timelines. - **Used Firebase Hosting for Instant Updates**: Instead of waiting days for app store review cycles, stakeholders could see changes within minutes, speeding up collaboration. - **Developed Responsive layouts & Platform Optimizations**: Features like touch gestures on mobile, mouse/keyboard support on desktop, and scalable layouts ensured usability everywhere. - **Managed Cross-Platform Deployment From One Codebase**: Flutter’s architecture was fully leveraged to deliver iOS, Android, macOS, Windows, Web, and Mobile Web apps without fragmenting the code. ## The Results **#1** First commercial Flutter web demo **6-in-1** A single codebase produced apps for iOS, Android, mac, Windows, Web, and mobile Web **1.1M+** Digital app subscribers ## Project Summary The New York Times puzzles are wildly popular with millions of players every day. But developing new games and maintaining existing ones was a huge undertaking given the complexity of web, mobile, and desktop platforms. Very Good Ventures collaborated with the Times and Google to explore Flutter on the Web as a viable alternative — creating the first commercial Flutter Web prototype that debuted at Google IO 2019. ## Stats ### #1 First commercial Flutter Web demo --- ### 6-in-1 A single codebase produced apps for iOS, Android, mac, Windows, Web, and mobile Web --- ### 1.1M+ Digital app subscribers ## Outcomes ### Outcome 1 #### Prototype Proven In mid-2020 the NY Times added Flutter to their primary games app after successfully piloting Flutter. ### Outcome 2 ![KENKEN win animation celebrating a completed puzzle](/assets/images/success-stories/new-york-times/body-2.gif) *KENKEN win animation* ### Outcome 3 #### Rapid Iteration Using Firebase Hosting and Google Sign-in, we were able to drastically reduce the cycle time of app builds and QA reviews. ### Slickdeals Doubles Releases With Flutter Source: https://verygood.ventures/success-stories/slickdeals-flutter/ Slickdeals has spent more than 20 years as a community of real shoppers helping other shoppers find, evaluate and share the most up-to-date online deals and coupons from all the top retailers. It has the dual distinction of being both a trusted, long-tenured business and a born-digital company, reaching its audience through its website, browser extension and its popular mobile app. ### The Challenge Slickdeals, a community-powered shopping platform where users find and share the best deals, relied heavily on its mobile apps as a primary channel for user engagement. But years of separate development across iOS and Android had created major hurdles. The team needed to reduce tech debt, deliver features more consistently, and keep critical monitoring tools intact. They found themselves struggling with: - **Diverging Codebases:** iOS had shifted from C to Swift, while Android moved from Java to Kotlin, creating parallel but mismatched foundations. - **Inconsistent Feature Rollouts:** iOS might ship an update weeks before Android (or vice versa), frustrating users and slowing innovation. - **Maintaining Performance & Reliability:** while carrying the weight of accumulated technical debt. - **Observability:** Keeping existing tools, like Embrace, was non-negotiable—and they had to be supported in any new framework to avoid losing visibility into crashes and user behavior. - **Efficiency:** Separate native teams and processes meant duplicated effort for every feature or fix. ### Our Solution Slickdeals needed a bold but sustainable approach to unify its mobile development. After evaluating options, the company made a strategic decision to migrate to Flutter while preserving its critical observability stack. This required both the right technology and the right partners. - **Conducting a “bake-off” evaluation** between React Native and Flutter, to confirm Flutter’s stronger long-term sustainability, consistent tooling, and active community. - **Gradually migrating functionality**, beginning with foundational components, incrementally expanding to full Flutter implementations, and temporarily wrapping certain native features. - **Adopting** **a philosophy** of writing clean, maintainable, “boring” code—prioritizing clarity and sustainability over complexity. - **Committing to robust quality practices**, achieving around 80% automated test coverage for Dart code, while leveraging Embrace observability to continuously monitor app health and user behavior. > "Organizations with apps as a central part of their business models are increasingly needing to re-evaluate their traditional approach of a native app on both iOS and Android. We wanted to take a page out of the world of video games, and leverage one platform to deploy both of our app experiences. It's much better to get a scalable platform for about half the work, so that we can really focus on the user's experience, not just implementation.” **\-Christopher Gulliver, Director of Engineering**, Slickdeals ### How We Did It Once the strategy was set, Slickdeals, VGV, and Embrace focused on disciplined execution. The migration unfolded incrementally, with careful attention to risk, quality, and team alignment. - A proof of concept for the Embrace Flutter SDK was rapidly developed and tested, reaching beta within three weeks. - Migration began with core app foundations and gradually expanded to full features, bridging native components only when absolutely necessary. - iOS and Android engineers adopted shared workflows, tools, and state management practices, breaking down long-standing silos. - Code quality was emphasized through clean, maintainable patterns and ~80% automated test coverage in Dart. - Continuous observability ensured by Embrace allowed the team to monitor performance, crashes, and user experience throughout the migration. > "When we brought Very Good Ventures on to help us migrate from the native apps to Flutter, they were team members for all intents and purposes. When they joined, we onboarded them like team members, they were in all of our team calls, and they shared in our team culture. We made sure they were comfortable in that space. At one point, I asked one of my team members for their opinion on how the integration was going, and they said, 'I don't know where our team ends and their team begins.'" **\-Christopher Gulliver, Director of Engineering**, Slickdeals ## The Results The transition to Flutter reshaped Slickdeals’ mobile development, enabling faster releases, stronger team alignment, and a scalable foundation for future growth. - Feature release velocity doubled thanks to a unified codebase and streamlined workflows. - Technical debt was significantly reduced by replacing duplicated platform-specific code with cross-platform implementations. - iOS and Android users now receive features and updates simultaneously, improving consistency and user experience. - A custom Flutter SDK for Embrace preserved full observability into performance, crashes, and user behavior. - High test coverage and a cleaner architecture created a more resilient and maintainable codebase. ### About Slickdeals Slickdeals has spent more than 20 years as a community of real shoppers helping other shoppers find, evaluate and share the most up-to-date online deals and coupons from all the top retailers. It has the dual distinction of being both a trusted, long-tenured business and a born-digital company, reaching its audience through its website, browser extension and its popular mobile app. The usability and experience of that app are absolutely essential for the success of Slickdeals as a brand — maintaining a loyal audience today requires online brands to be mobile-friendly. The Slickdeals team therefore always needs to have quick and efficient workflows and processes in place to deliver bug fixes or new features to the app. > "Organizations with apps as a central part of their business models are increasingly needing to re-evaluate their traditional approach of a native app on both iOS and Android" said Slickdeals Director of Engineering Christopher Gulliver. "For some projects, a native app is unavoidable, but I've found that is becoming increasingly rare. We wanted to take a page out of the world of video games, and leverage one platform to deploy both of our app experiences. It's much better to get a scalable platform for about half the work, so that we can really focus on the user's experience, not just implementation. ### Development Divisions and Difficulties The Slickdeals app has changed and evolved several times since its initial launch. Both versions of the app had changed languages at various points, with the iOS version migrating from C to Swift and the Android version changing from Java to Kotlin. Over time, the app accumulated tech debt. Team changes, development strategy shifts, and other choices through the years made it difficult for Slickdeals to efficiently roll out new code. Under the most recent development process, the iOS and Android teams were each free to create features and bug fixes, which opened up speed and functionality gaps. One team would almost always roll out a new update before the other, thwarting the company's desire for day-and-date updates and a cohesive experience for users. "We always struggled to do that simultaneous release," said Gulliver. "One team would be really focused on how to do things right, whereas the other team would be pretty clever and do it quickly. Both had their trade-offs and I wanted to grab both those strengths associated with those trade-offs and bring them into a unified team." Understanding that the split between teams and methodologies was unsustainable, Slickdeals searched for a way to unify its codebase. This process came with some non-negotiables. For example, the mobile monitoring and observability platform [Embrace](https://embrace.io) had become an essential tool for Slickdeals' engineers, so any proposed solution would need to include Embrace support. ### Moving Forward With Flutter The Slickdeals team performed due diligence, assessing potential multi-platform frameworks that could enable the unification of its app codebase going forward. In the end, the decision came down to a choice between Flutter and React Native. The decision to go with Flutter was largely tied to trends and sentiments in the development space. Flutter has a strong reputation among developers. Since the framework's introduction, companies and their engineers have been gravitating toward Flutter, giving it a feeling of positive momentum. Considering that some of a platform's strength comes from its ability to sustain an active community of users and feature developers, this positive sentiment has a positive material impact. *"We were looking for a platform that we could leverage and sustain for 5+ years," Gulliver explained. "When we looked at the numbers it was clear that React Native was losing ground, and the traction Flutter was making was astounding. The community was vibrant and exciting, and more than that, they seemed to be solving a lot of the challenges the other cross-platform efforts struggled with."* Changing to a whole new multi-platform development paradigm is a major shift, and is best handled with an expert partner. Slickdeals chose Very Good Ventures for this role because of VGV's core values: A commitment to simplicity in both codebase and development practices, as well as a dedication to doing things the right way. > "We were sitting on a mountain of tech debt. We wanted to make sure that if we were going to be doing the effort of effectively porting features, not code, to Flutter, that we were writing the new code correctly. That's where a lot of the core values that Very Good Ventures had resonated with us," Gulliver observed. There was one complication: Embrace, the tool Slickdeals' engineers needed for visibility, was not compatible with Flutter. This quickly turned into an opportunity. Introduced by Slickdeals, VGV and Embrace began [collaborating on an Embrace Flutter SDK.](https://verygood.ventures/blog/building-a-flutter-sdk-for-embrace/) "The real power of Embrace is being able to dig in and understand what's causing the crash — what was the call stack — in a human-readable way," said Gulliver. "How often is this happening? When a user experiences it, what were they doing beforehand? You're really getting that full in-depth picture of what's happening. We've been using that for a long time. One of the things we were looking at when we were evaluating Flutter as a whole is: Do our existing services have a path to Flutter?" The Embrace team was eager to expand their tooling capabilities to Flutter, and dove into the project. Embrace created a proof of concept for the SDK, and in three weeks, VGV made changes to get the POC in line with its best practices, getting the new version into beta in approximately three weeks. > "Very Good Ventures was very responsive to anything that came up, and made sure to get our take on what the API would probably look like and the important parts that we needed up front, and then down prioritizing the little things that could wait until a bit later," Gulliver said. With Embrace support now available, the project could turn to its main focus: Moving the many features of the Slickdeals app to Flutter. These features shifted on a gradual schedule as they started with the foundation. They wanted to make sure initialization and core components were done right. Some features needed to remain native, at least for the moment, and were wrapped in Flutter code to become part of the new multi-platform ecosystem. The flexibility and benefits of Flutter quickly became apparent. It wasn’t all a smooth journey, and they’re still not quite done yet. Slickdeals has faced several challenges throughout the process as the team size and composition shifted, and the implications of some early decisions became clear. “Platform views are certainly a trade off. In some ways they’re a super power, they give you the ability to lift the entire native experience and wrap them in what amounts to a Flutter widget. To apply a popular quote; with great power comes great responsibility. Looking back, I wish we would have used less platform views to get the app out. The legacy code often required some interesting and time consuming contracts for the platform views to function properly due to the complexity of the underlying native code. This ended up costing us more time than a port would have” Gulliver reflects, “If I had to offer one piece of advice, it would be to seriously consider committing to porting the view to Flutter from the outset, especially if there’s any possibility that an external request might force you to go back and update native code.” A few developers had difficulties leaning into the change from native to Flutter and they found themselves less able to contribute. Flutter, like any other opportunity, you get out of it what you put into it.  “Flutter is not a silver bullet, but it’s the closest thing we’ve found so far,” shared Gulliver. ### A Steady Stream of Backend Improvements Throughout the process of collaboration with Very Good Ventures, the Slickdeals team found it easy to collaborate with the external experts — the groups quickly learned to act as one. > "When we brought Very Good Ventures on to help us migrate from the native apps to Flutter, they were team members for all intents and purposes. When they joined, we onboarded them like team members, they were in all of our team calls, and they shared in our team culture. We made sure they were comfortable in that space," Gulliver explained. "At one point, I asked one of my team members for their opinion on how the integration was going, and they said, 'I don't know where our team ends and their team begins.'"  Working on the Slickdeals app is now significantly simpler than it was in the era of uneven parallel development. As it is, the newly-unified development team is benefiting from using one tool for key processes such as state management. The Slickdeals engineers are achieving important benchmarks, such as implementing a new standard of 80% automated test coverage for dart code. In addition to these improvements, there are plenty of familiar factors still in play, including Embrace — the monitoring and observability platform is still providing a comprehensive, efficient view of the codebase. In fact, with the new Embrace SDK, the tool has become a fully featured part of the Flutter ecosystem, reaching not just Slickdeals but any organization that wants to use it. The SDK incorporates learnings from similar Flutter packages to make it easy for engineers to use, while also using APIs to deliver key  features such as page view and moment tracking, allowing it to serve as a go-to tool for Flutter development. The internal Slickdeals team, once split between iOS and Android, can now work as a unified team. The split during the native-development era involved one team that embraced "clever" methods and one that skewed "cautious." One team would deploy new features to production first, while the other was slower, but with more stability. By merging the teams and empowering them with a new set of norms and best practices, Slickdeals is able to get the best of both worlds. The developers are now full participants in the rich Flutter ecosystem, with all the solutions, actions, APIs and plugins that come along. The Slickdeals app is poised to remain the company's go-to touchpoint for years to come. “We are thankful for the partnership we formed with VGV. They were always there for us, helping to train our team and adjust team size or composition based on our needs. They were even there months after our contract ended, willing to help out in any way they could. Our teams bonded in a way I’ve never seen with an outside agency.” Gulliver shared. ### A future with Less Tech Debt Freed from the added costs and delays associated with unchecked tech debt, the Slickdeals team is free to innovate. All new developers are being trained in Dart and Flutter best practices. The code they create will be healthy, simple and "boring," preventing too much complexity from creeping in. "They're not only learning Dart, they're also learning 'These are the core pillars of good architecture, and these are solutions to the common problems you have to deal with.'" said Gulliver. He added that "With Very Good Ventures already being experts, they were able to start down that path at a run as our team started to ramp in it and learn, and eventually they caught up." The Slickdeals app is in a strong position now, complete with the ecosystem maturity it needed and with a well-maintained multi-platform development process and monitoring system. Engineer behaviors and skills on the combined team have leveled up, and more features are moving to the Flutter platform to open a path to a fully multi-platform future. > “With the speed of iteration happening on the Flutter platform, we’re really excited to see where it will lead.” Gulliver considered. Organizations of all sizes and across industries are feeling the same kinds of pressure that moved Slickdeals to make changes, facing tech debt and wanting to merge their separate development teams. The way it has always been done is no longer the only path toward a high-quality user experience. To find out if a similar solution could help your company, [contact Very Good Ventures' experts](https://verygood.ventures/contact-us/). ### Elevating NASCAR's VIP Experience: Driving Engagement, Sponsor Value & Scalable Growth Source: https://verygood.ventures/success-stories/trackhouse-nascar-vip-experience/ Trackhouse Entertainment Group is a dynamic sports and entertainment company redefining motorsports culture through innovation, fan engagement, and brand partnerships. Founded by Justin Marks, the organization owns and operates the Trackhouse Racing Team in the NASCAR Cup Series, competing with a bold vision to merge racing, entertainment, and storytelling into a modern, tech-forward experience that attracts new audiences and elevates sponsor value. We reimagined the Trackhouse VIP experience at 200 mph—transforming the chaos of NASCAR into a seamless, multi-device journey powered by Flutter. ## A Straightforward Opportunity When Trackhouse, one of NASCAR’s most innovative teams, came to us, their ask seemed straightforward: build an iPad app for their pit box. They wanted a way for VIP guests to feel more connected to the action—a digital brochure with information about the team, the drivers, and the race day experience. But as soon as we got on the ground at Daytona, it became clear that the challenge was bigger than a single app. The pit box was loud, chaotic, and disorienting even for experienced fans. Guests struggled to follow the race, lost context once they stepped away from the box, and missed opportunities to engage with Trackhouse and its sponsors throughout the day. A brochure-style app would have checked the box. But it wouldn’t have solved the problem. ## Expanding the Frame Instead of designing for a moment, we mapped the entire VIP journey—before, during, and after the race. That’s when we saw the real opportunity: this wasn’t about building an app, it was about building an experience. We used a prototyping-first approach, rapidly testing concepts trackside with real guests. Flutter let us move fast, building functional prototypes we could deploy directly in the pit environment. Through overlapping cycles of design research and prototyping, we gained confidence in what would truly matter: - Context for newcomers to understand what they were seeing. - Awareness so VIPs could track their car no matter where they stood. - Connection to the team, the drivers, and each other. - A way to extend the experience beyond a single device or moment. By thinking in terms of ecosystems—not apps—we could maximize ROI with minimal extra effort. Features and data pipelines designed once for iPads could be reused for Jumbotrons, kiosks, and mobile apps. ## More Than an App This is the real magic: by looking beyond a single request, we unlocked an ecosystem. Trackhouse asked for an app, and we gave them a comprehensive, multi-device experience—one that turned chaotic race days into unforgettable moments of connection. This wasn’t just a win for Trackhouse. It was a demonstration of how Flutter and a build-first mentality can deliver outsized impact: rapid prototyping, confident product definition, and a scalable foundation for physical/digital experiences that thrive even in the most extreme conditions. ## A Multi-Device Experience The result was a suite of connected solutions that transformed race day: - **Pit Box iPad App:** A real-time dashboard with car telemetry, in-car video, live race tracking, and evergreen team content—putting VIPs in the driver’s seat. - **Jumbotron Displays:** Large-scale, branded race info screens for fans standing behind the pit box—reusing the same core data and visualizations, with almost no new code. - **Localized Pit Box Server:** A hybrid edge/cloud setup using Mac Minis to deliver uninterrupted telemetry and media streams—even under extreme race-day network congestion. Together, these platforms elevated the Trackhouse VIP experience into something seamless, immersive, and resilient. Guests no longer had to piece together what was happening; they were brought closer to the race than ever before. Sponsors gained richer visibility. And Trackhouse gained a scalable digital foundation for future innovation. > “F1 is the world’s gold standard, and this interactive user experience positions Trackhouse among the best that motorsports currently offers.” — **Andrew Lukanich, CRO, Trackhouse Entertainment Group** ### V1 Sports Doubles Revenue With Flutter Migration Source: https://verygood.ventures/success-stories/v1-sports/ Founded in 1995, V1 Sports is the pioneer in producing swing video analysis learning and instruction solutions. More than 10,000 of the best coaches, instructors, and training facilities, and over four million athletes, have used the company’s products. V1 PRO Studio creates a world-class video capture environment with multiple high-definition, high-frame cameras for optimal analysis. Today, the V1 Sports platform – V1 PRO, V1 GOLF, V1 GAME, and V1 BASEBALL – connects coaches and athletes through video, ground pressure analysis from the V1 Pressure Mat, and on-course performance data for measurable improvement. Headquartered in Michigan, V1 Sports employees are dedicated to creating transformative solutions for coaches and athletes to improve quicker and enjoy the game more. ### The Challenge V1 Sports, a leader in sports video analysis and coaching tools, had built its product suite on six separate iOS and Android applications. Each app had its own native codebase, leading to duplicated effort, higher costs, and slow release cycles. Maintaining multiple codebases meant every update, bug fix, or feature release required work across several platforms.  Beyond development inefficiencies, subscription management was fragmented across six apps, creating friction in billing and analytics. This complexity limited the company’s ability to scale, slowed innovation, and created inconsistency in the user experience across platforms. ### Our Solution To overcome these barriers, V1 Sports partnered with [RevenueCat](https://www.revenuecat.com/), a subscription app monetization platform, and VGV to design a unified, modern architecture that would support scale and innovation. The core of the solution was migrating all six native apps to a single Flutter codebase, creating a cross-platform foundation for future growth. Alongside the migration, the project introduced robust tools, processes, and integrations to modernize V1’s development practices and improve business outcomes. Key elements included: - **Flutter Migration:** Rebuilt all six apps into a single Flutter codebase, ensuring consistent performance and experience across iOS and Android. - **RevenueCat Integration:** Implemented RevenueCat as a unified backend for subscriptions and in-app purchases, giving V1 a single source of truth for billing, analytics, and customer data. - **Architectural Foundations:** Leveraged VGV’s tooling (Very Good Start, Very Good Architecture) to ensure clean code, modular design, and long-term maintainability. - **Agile Development Process:** Partnered closely with V1’s product and engineering teams, using sprint planning and backlog prioritization to keep delivery focused and transparent. - **UI & Documentation:** Delivered a component gallery, design system, and thorough documentation so V1’s internal team could easily extend and maintain the apps. - **Quality & Testing:** Built a QA pipeline with TestFlight and Google Play Console, enabling early feedback, iterative testing, and smooth app store launches. - **Post-Launch Support:** Provided ongoing fixes, improvements, and knowledge transfer sessions to empower V1’s team to take ownership after launch. ### How We Did It Executing the migration required a thoughtful, collaborative process that balanced technical precision with business goals. VGV worked side by side with V1 Sports to ensure every step reduced risk, maintained product quality, and accelerated time-to-market. - **Discovery & Assessment:** Conducted a deep review of all six apps, documenting feature requirements, dependencies, and API endpoints to inform a unified roadmap. - **Foundational Setup:** Established the new Flutter project using VGV’s scaffold (Very Good Start) and architecture patterns, ensuring scalability from day one. - **Incremental Development:** Built and released features in prioritized sprints, focusing first on critical functionality to achieve parity with legacy apps before layering on improvements. - **Cross-Team Collaboration:** Coordinated daily with V1’s engineering and product teams to refine priorities, resolve blockers, and adapt to evolving needs. - **Design System Integration:** Built a shared UI component library and documentation hub to maintain visual consistency and speed up future development. - **Testing & Iteration:** Implemented a rigorous QA process, with internal testing, stakeholder reviews, and phased rollouts on TestFlight and Play Console. - **App Store Launch:** Managed the full submission and review cycle with Apple and Google, addressing feedback quickly to ensure timely approvals. - **Knowledge Transfer:** Delivered training sessions, documentation, and shadowing opportunities so V1’s internal team could confidently manage and extend the apps after launch. ### The Result **2x** Revenue YoY **300,000+** Grew user accounts to new highs **6-to-1 **Unified six codebases to one ## Stats ### Unified six code bases to one --- ### Doubled revenue year over year ### --- ### Grew users to more than 300,000 accounts ## What's Next Read our new eBook for best practices on [migrating from native to Flutter the VGV way](https://verygood.ventures/whitepaper/ebook-migrate-from-native-to-flutter-the-vgv-way/) ### AI-Powered Race Car Performance Optimization Source: https://verygood.ventures/success-stories/valkyrie-ai-race-performance/ ## About Valkyrie  Founded in 2017 in Austin, Texas, Valkyrie is a dynamic services company that melds science with data to tackle challenges, drive transformative impact, and create value. The Valkyrie team is made up of scientists and strategists aiming to advance the field of data science and deliver custom machine learning capabilities to solve complex challenges and drive organizational impact. Valkyrie empowers clients to make informed decisions and thrive in a data-driven landscape, shaping the future and ushering in a brighter tomorrow where science and data intersect seamlessly. ### The Challenge In motorsports, winning margins often come down to fractions of a second. To truly improve, drivers and teams must uncover the *why* behind those times: what happened on a specific corner, in changing conditions, or against particular competitors. Racing teams faced a clear need—a way to turn overwhelming streams of raw data into insights they could actually use to win. This included solving challenges for: - **Limited Visibility into Performance Patterns:** Standard timing systems provided lap times, but they didn’t reveal deeper insights like consistency, cornering efficiency, or speed variations across sectors. - **Difficulty Comparing Across Races & Tracks:** Each race generated mountains of data, yet there was no simple way to evaluate how a driver performed from one track to another, or against rivals on the same course. - **Overwhelming Data Without Context:** Raw numbers were hard to interpret in real time. Teams lacked a clear, visual method to connect data points to on-track actions, making it difficult to coach drivers effectively. - **Lost Opportunities for Strategy Refinement:** Without actionable analysis, small mistakes and inefficiencies went unnoticed—costing precious tenths of a second that often determine race outcomes. ### Our Solution  VGV partnered with [Valkyrie.ai](http://valkyrie.ai) to transform racing data into an intelligent, driver-first analytics platform. Rather than focusing on raw metrics, the solution delivered meaningful insights that drivers and teams could act on—bringing clarity to every lap and every turn. Together, we: - **Designed a Driver-Centric Application:** Our team built a platform tailored to the real-world needs of drivers and race engineers, ensuring usability in high-pressure, trackside environments. - **Enabled Powerful Lap-by-Lap Comparisons:** We created tools to easily evaluate performance across laps, races, and competitors—allowing drivers to pinpoint where they gained or lost time. - **Made Complex Analytics Intuitive:** The team translated advanced data science outputs into simple, digestible visualizations that highlighted actionable insights instead of overwhelming users. - **Delivered a Scalable Foundation:** We established a platform that could evolve alongside the team, supporting future data integrations and expanding beyond a single racing series. ### How We Did It  The solution came to life through a deep collaboration between VGV’s product and engineering teams and Valkyrie.ai’s machine learning experts. As a united team, we married advanced analytics with elegant design to deliver a product that drivers could trust and understand in real time. - **Applied Advanced AI & ML Modeling:** We processed raw telemetry and lap timing data with machine learning algorithms to identify patterns invisible to human analysis. - **Engineered with Flutter for Versatility:** The team built the application in Flutter, ensuring cross-platform consistency and high performance both on mobile devices and trackside systems. - **Integrated Intuitive Data Visualizations:** We created visual dashboards that surfaced trends in lap performance, speed variation, and consistency—turning abstract data into immediate insights. - **Collaborated Closely With Race Teams:** Together, we worked side by side with drivers and engineers, iterating quickly to ensure the product matched the unique demands of a racing environment. ### The Result - **Unlocked Actionable Performance Insights:** Provided drivers with clear, visual feedback that revealed where time was lost or gained on the track. - **Enhanced Cross-race & Competitor Benchmarking:** Enabled side-by-side comparisons across circuits, weather conditions, and rivals to identify strengths and weaknesses. - **Improved In-Race & Training Decision-Making:** Gave coaches and drivers the ability to make smarter adjustments in real time and more targeted training plans off the track. - **Created a Repeatable & Scalable Solution:** Built a platform that can be adopted by additional teams and racing organizations, extending impact beyond a single team’s success. ## Podcast Episodes ### The Future of Software Development: Embracing AI and Quality Source: https://verygood.ventures/podcasts/chris-sells-ai-and-software-quality/ Host Jorge Coca sits down with Chris Sells, CEO of Gas City and a software engineering veteran whose career spans Intel, Microsoft, Google, Meta, and Sourcegraph. At Google he spent three years on the Flutter team, helping carry it from version 1.0 to 3.0. The conversation opens with what Chris sees as the one thing AI hasn't changed: we still have software-shaped problems that demand genuinely high-quality solutions. What has changed is that agents, not humans, now write and sometimes even read the code. Chris draws a firm line between vibe coding, where you ask for something and get code that runs, and production software that has to hold up under load, meet latency targets, and slot cleanly into an existing enterprise system. Because agents can't sense a code smell, he argues engineering standards like DRY and SOLID have to be written down explicitly rather than left in engineers' heads. That thinking drives Gas City, his open source platform that orchestrates teams of AI agents into what he calls software factories, using primitives like Beads, formulas, and rigs. Gas City runs on Gas City, with roughly a hundred agents working around the clock across 23 repositories. He and Jorge also dig into where humans still matter: setting intent rather than syntax, and bringing the taste and agenda that agents lack. Chris shares how his small team uses named agentic teammates to help run the company, why he thinks software factories go mainstream within a couple of years, and the one Flutter package he wishes existed, AI-generated dynamic UI. ### Building Production-Quality Software with Minimal Code Source: https://verygood.ventures/podcasts/peter-trost-production-quality-minimal-code/ Jorge Coca talks with Peter Trost, a senior Flutter engineer at 1KOMMA5°, one of Germany's fastest-growing clean-energy companies, where he works on software for home energy systems. Peter came to app development from an unusual direction, studying computer science with a focus on AI and machine learning, and that background shapes how he thinks about what today's tools can and can't do. He explains why the slow feedback of machine learning research pushed him toward user-facing work, and why Flutter's hot reload, widget inspector, and the Dart language made it stick. The heart of the conversation is the property management startup Peter is building largely on his own with AI. He walks through features like voice-first inspection reports that transcribe a manager's spoken observations into structured work orders, and guided digital checklists that keep inspections consistent. He treats AI as a collaborator rather than an autocomplete, using agentic tools like Claude and LeanCode's Marionette, an MCP server that lets an agent drive a running Flutter app to verify its own work. Peter and Jorge unpack what minimal code really means: far less hand-written code, held to the same production bar through clear structure, tests, and runtime checks. Peter also reflects on introducing AI-assisted workflows inside a larger engineering organization at 1KOMMA5°, and the constant pull between focus and exploration that comes with running a startup on the side. ### Building Custom Figma Plugins: How a Designer Codes with AI Source: https://verygood.ventures/podcasts/murphy-orourke-custom-figma-plugins/ Jorge Coca sits down with Murphy O'Rourke, a designer at Very Good Ventures who taught himself to build custom Figma plugins with AI and has grown a whole ecosystem of them that the VGV design team now uses daily. Murphy describes himself as a more technical designer than most, with roots in front-end work and years spent sitting close to engineers on design systems, which gave him a clear view of the friction points around handoff and consistency. He breaks down what a Figma plugin actually is, a few files of JavaScript or TypeScript plus a manifest that can read and write design data, and traces his path from his first plugin, Very Good Flutter Styles, which exports Figma theme colors and text styles as clean Dart code. From there he walks through the tools he built to tame VGV's variable-heavy template files: Locals Only for remapping duplicated templates, Variable Calculator for bulk math on variables, Contrast Audit for accessibility checks, and Control R for renaming variables before Figma shipped its own version. The conversation returns to a theme VGV calls trust and taste. Murphy is candid that he leans on developers to judge whether his code belongs in an app, but AI lets ideas come from anywhere and get expressed in ways the whole team can build on. He details his shift from ChatGPT to Cursor and Claude Code, his use of plan mode with Opus and Sonnet, and how a proof of concept that once took a month now takes under an hour. ### Designing AI to Fail: What a Capture the Flag Game Reveals About Where LLMs Break Source: https://verygood.ventures/podcasts/andy-gibel-designing-ai-to-fail/ Jorge Coca talks with Andy Gibel, an engineering leader at Yum! Brands, the company behind KFC, Taco Bell, and Pizza Hut, about an AI-focused Capture the Flag game he designed to push his engineering peers past surface-level AI use. Borrowing the format from security, Andy set one rule for participants: use AI by any means necessary, including exploiting the APIs. His fear that someone would crack it in ten minutes led him to over-engineer it into something closer to an escape room, with seven progressively unlocked tiers, a live leaderboard, and a countdown clock. Andy explains the AI adoption curve he sees most teams stuck on, where people find a comfortable spot prompting a chatbot and stop pushing toward orchestration and multi-agent work. He designed challenges that target documented model weaknesses: the eagerness to please that produces confident wrong answers, the difficulty AI has with deception, which he tested with a lying-agent murder mystery, and a Magic Eye stereogram challenge that had agents grinding for hours because the models lack depth-based vision. He even ran an autonomous red team of agents against his own build to find and patch holes. The takeaway reaches past the offsite. Andy and Jorge discuss how securing agentic systems marries deterministic gatekeepers with non-deterministic models, and why, as production gets cheaper and faster, the bottleneck moves to review and the real skill becomes taste, reading unfamiliar code and reasoning about architecture. As Andy puts it, echoing Andrej Karpathy, you can outsource your thinking but not your understanding. ### The Art of Retro UI: Building an 8-Bit Design System with NES UI in Flutter Source: https://verygood.ventures/podcasts/erick-zanardo-8-bit-design-system/ Jorge Coca talks with Erick Zanardo, a Technical Delivery Lead at Very Good Ventures and a Google Developer Expert in Flutter and Dart. Erick is one of the engineers behind the Flame game engine and contributed to Google's I/O Pinball. The conversation centers on NES UI, the retro 8-bit design system he built for his own games and open sourced for the Flutter community. Erick offers a grounded definition of a design system as a set of rules for organizing how a design works, not so different from how developers organize code, with the end goal always being a comfortable, familiar experience for the user. He explains how NES UI grew out of his own game development, studying screenshots from titles like Final Fantasy 6 and web CSS libraries so he could stop rewriting buttons and cards for every project. The library sits on top of Flutter's Material, giving its structure a pixel-art skin. A highlight is Mini Sprite, a side project that encodes pixel-art icons as compressed strings, solving the problem that Dart packages can't ship binary assets, complete with an editor for drawing and copying new icons directly into code. Erick also talks about Cherry Bit Studios, where he ships games like Pixelado and Mine Cat Digger under a pricing model he considers fair, free with ads and a single purchase to remove them. He closes with practical advice for anyone building their first design system: learn atomic design for shared vocabulary, resist over-engineering one infinitely customizable widget, and lean on tools like Widgetbook. Building in the open, he says, is the whole point. ### From Dart to Flutter: The Evolution of a Language and a Framework Source: https://verygood.ventures/podcasts/brian-egan-evolution-of-dart-and-flutter/ Jorge Coca sits down with Brian Egan, Director of Engineering at Very Good Ventures, to trace Dart from its 2011 origins at Google to its role as the language behind Flutter. Brian shares his own early encounter with Dart while working at Zappos, installing an IDE plugin and starting to explore what the language could do at a time when it was pitched as a modern alternative to JavaScript for increasingly complex web apps. The conversation covers why Dart felt approachable, its C-like syntax welcomed developers coming from Java and JavaScript, and how the arrival of type safety proved especially valuable on team projects. Brian and Jorge revisit the early skepticism, when many in the community doubted Dart could coexist with, let alone challenge, JavaScript's dominance on the web. Brian describes the turning point when Flutter emerged and put Dart's strengths to work for mobile development. His own move to Flutter was driven by a search for faster iteration and better performance than native Android offered, with hot reload and hot restart winning him over. They close on how the language matured, from dropping the new keyword for cleaner code to gaining true sound type safety, alongside Flutter's expansion well beyond mobile. ### Building the Future of Web Development: The Journey of Jaspr Source: https://verygood.ventures/podcasts/kilian-schulte-jaspr-web-development/ Jorge Coca talks with Kilian Schulte, the Flutter developer who created Jaspr, a web framework for building websites in Dart. Kilian explains the core idea: Jaspr lets developers compose pages from components that look and feel like Flutter widgets, so it reads a lot like Flutter but works slightly differently. That familiarity makes it a natural bridge for Flutter developers moving into web work without having to write JavaScript. Kilian recounts starting the project nearly four years ago, after noticing there was no real Dart-native option for building websites, and prototyping the first version over a weekend. He credits the Dart community with much of Jaspr's momentum, pointing to an early Reddit post whose strong response motivated him to keep going and helped shape the framework around real user needs. A pivotal moment came when the Flutter and Dart teams approached him to rebuild their official websites with Jaspr, validating it for production-scale work. Kilian and Jorge discuss the features he built with Flutter developers in mind, including Markdown content generation and server-side rendering, and his guiding principle throughout: never abstract away the underlying concepts of the web, so developers still understand the platform they are building on. ### Fearless Refactoring: Why Flutter Is a Business Strategy, Not Just a Framework Source: https://verygood.ventures/podcasts/steven-stamps-fearless-refactoring-flutter-business-strategy/ Most conversations about modern software focus on tools — new frameworks, new languages, new trends. But the biggest challenges in software engineering have not changed in decades. In this episode of *Build to Succeed*, VGV CEO **David DeRemer** sits down with **Steven Stamps** — a veteran technologist whose career spans Fortran and mainframes through Flutter and AI — to unpack a deeper truth: the real challenge is not technology. It is people, process, and leadership. {% audio_embed url="https://open.spotify.com/embed/episode/2yUAKjFzPvBjemhQn6nrY6?utm_source=generator" /%} ## A Career That Spans the Entire Stack of Modern Tech Steven's journey through software engineering is unusually expansive. He started in the era of punch cards and IBM mainframes, proprietary systems with no shared standards, and dozens of incompatible platforms. Over time, he became an early adopter of nearly every major shift: Unix, relational databases, object-oriented programming, the internet, mobile, and now Flutter and AI. His approach is consistent — find where the industry is going, and move early. ## The Lessons Aviation Teaches About Software One of the most practical parts of the conversation comes from Steven's experience as a pilot. He draws a direct parallel between aviation and engineering. ### 1. Checklists Create Consistency High-performing teams do not rely on memory — they rely on systems. Every release, every ticket, every process should be repeatable and predictable. ### 2. Plan for Failure, Not Just Success Pilots do not just plan their destination — they plan alternates. Great engineering teams do the same: backup strategies, fallback paths, and edge-case thinking. ### 3. The Most Dangerous Moments Are the Transitions In aviation, takeoff and landing are where most failures happen. In software, it is the same. A poor project kickoff sets up long-term failure. A lack of focus at the end produces broken delivery. ### 4. Confess Problems Early > Confessing problems early can save the project. Teams fail not because problems happen, but because they are hidden too long. ## What Hasn't Changed in Software Engineering Despite massive technological progress, some things remain constant: resistance to change, organizational politics, "if it ain't broke, don't fix it" thinking, and team tribalism (iOS vs. Android, and so on). Steven calls out a key truth: > Technology evolves quickly. Human behavior does not. That gap is where most transformations fail. ## Fearless Refactoring: A Different Way to Build Teams One of the most powerful ideas in the episode is **fearless refactoring**. Instead of treating improvements as optional, high-performing teams capture every opportunity for improvement, add it to a structured backlog, and schedule it intentionally in future sprints. > A backlog of refactorings is a strategic asset. This creates a culture where innovation is continuous, technical debt is managed proactively, and teams always move forward. ## Innovation vs. Operations Isn't a Tradeoff A common belief in tech is that you must choose between stability (operations) and innovation (experimentation). Steven challenges that idea. With the right systems in place — feature flags, A/B testing, data-driven decision-making — teams can have both. Organizations can experiment safely, validate ideas quickly, and protect production systems. ## Why Flutter Is a Business Decision One of the biggest takeaways from the episode is that Flutter is not just a technical upgrade. It is a strategic business move. ### Cost Efficiency Maintaining separate iOS and Android teams is expensive. Flutter consolidates effort into one team. ### Speed to Market A single codebase means faster releases and quicker iteration. ### Eliminating Parity Debt No more waiting for features to catch up across platforms. ### Better ROI on Engineering Work Steven introduces a powerful metaphor: **code as inventory**. If features sit unused while waiting for another platform, that is wasted investment. Faster releases mean faster returns. ### Talent Density Instead of splitting teams across platforms, Flutter enables smaller teams, more senior engineers, and higher overall performance. ## The Hard Truth About Organizational Change Even when the benefits are clear, adoption is difficult. Why? Because people protect what they have built, leadership hesitates to disrupt, and politics slow progress. > Politics often hinder technological progress. That is why real change often requires top-down leadership alignment, clear business justification, and sometimes a willingness to act before consensus. ## The Real Focus: People Over Technology For all the talk about frameworks and systems, Steven ends with a simple perspective. The most important part of engineering is not code. It is people — building strong teams, supporting careers, and creating sustainable environments. That is what drives long-term success. ## Final Takeaway If there is one idea that defines this episode, it is this: the best teams do not just adopt new technology. They build systems — and cultures — that make continuous improvement inevitable. Flutter is part of that story. But leadership is what makes it work. ## Resources & Links - Connect with Steven on LinkedIn: [linkedin.com/in/stevendstamps](https://www.linkedin.com/in/stevendstamps/) - Learn more about Flutter: [flutter.dev](https://flutter.dev) ## Transcript **David DeRemer (00:01.912)** All right, welcome Stephen. Thrilled to have you on Build to Succeed. To kick us off, can you introduce yourself and the kind of work you do? **Steven Stamps (00:09.12)** I sure can. By the way, I'm really happy to be here with you, David, and love reaching out to the Flutter community. I think it's a super important community that needs to grow, and I'll do what I can to help. I do hybrid work from upstate New York, where I recently built our bespoke forever home on 26 acres of virgin forest. That's been a project. I live with my wife who's a retired medical doctor and we have a three-year-old little boy named Wyatt. I have a lot of interests outside of work. I like to compose music, play a lot of musical instruments. collect cars. I enjoy long cycling trips and aviation is a hobby, both as a private instrumented pilot and as an owner of several airplanes. And in another context, any one of these would be a podcast all by itself. **David DeRemer (01:07.726)** That's great. Well, you already teed me up because one of the ways I like to start is by asking for things you do in your personal life that bring you back into technology where you kind of get better at work as a result of the things you do outside of work. And I know you and I caught up a little bit about aviation and I'm curious if there were things from aviation that you found have really worked to improve your approach to management, engineering, technology, design, etc. **Steven Stamps (01:33.742)** Excellent question. Aviation has been a very important part of my life, not professionally, but both as a hobby and I own my own consulting company for 18 years and had my own airplane to travel around to clients. It's been important and informative for me. for a couple of reasons. There's a checklist for every aspect of aviation. If you're a good pilot, you don't do anything without following a checklist and confirming it. just like Santa, checking it twice, making sure that you haven't missed anything. The checklist is a highly efficient and low ceremony habit. So, you know, we're not talking about overloading your processes with high ceremony documentation. And this maps to how I like to lead teams. So every ticket has checklists, every release has checklists, and it just helps to make sure that everyone's on the same page and that we have consistent quality and that we're all safe and our users are safe when you deploy. new apps. know, with aviation, have flight planning and that flight planning has options. So you not only plan the flight of the destination airport where we'd like to go, but alternate airports if that airport gets snowed in or has bad weather. And even in that airport, alternate approaches and procedures. Personally, I also, before every journey and a lot of them included my family, I had an FAA certified flight simulator in the basement and I would fly all those approaches and all those alternates before every trip so that I would be prepared. And those are also the kinds of habits that I instill in my teams. And then the last, well, actually not last. **Steven Stamps (03:54.286)** The third part of that is key transitions. In aviation, the key transitions are takeoff and landing. Takeoff is where things can go badly and it's really difficult to recover. And so you need to do a lot of preparation before takeoff to make sure that your equipment and the runway and the weather are going to support that takeoff successfully. And then the other transition that's critical is landing. a lot of accidents happen there unnecessarily because the pilot gets the runway in sight. They've just come through a tough trip. They've had ice, they've had weather, and so they relax. And then they end up not having a good landing, tipping the plane over. And these are lessons that I take directly into leading projects. Getting a project started properly, the right people on the bus, if you will, the right scope, the right KPIs and metrics for measuring success. If you don't get those right very early on in the project, the rest of the project is going to suffer and may not even succeed. And then the landing metaphor, you know, a project might be going really well, it's a long-term project, and then the leaders take their eyes off the ball and the project wanders and loses focus and isn't successful. So, and then the last piece from aviation I would share that's critical is confess. When you're a pilot and when you're up in the air and when things are going poorly, equipment, bad decisions on your part, most important thing to do is to confess to air traffic control so that you can get advice, get help, and address the situation. But what happens too frequently is that pilots do not confess that there's a problem. They try to solve things themselves, problems pile up on each other. And then by the time they do confess, it's too late to save the airplane and save the occupants. Exact same metaphor with projects is when that project starts going sideways, **Steven Stamps (06:17.432)** there's risk, you need to confess immediately. You need to talk to your team. You need to talk to your organization. And if you, Talk to people early and, leverage the wisdom of teams. there are very few situations you can't overcome and get back on track. yeah, aviation has been a big part of my life. Wow. **David DeRemer (06:40.802)** Well, and so many good parallels there. mean, just every one of those, we could probably do an episode on every single one of those points you just made and deep dive into those and how to actually do that. Sounds like aspiring product leaders and engineers might want to consider getting some, some flight training. It seems like it would be a good, good skill to add to the, to what they're able to do. **Steven Stamps (07:00.686)** Well, I mentioned I have a three-year-old little boy. I can assure you he will be getting his Pilots license when he turns 16. I think it's great training. **David DeRemer (07:08.364)** Amazing. Yeah, it's wonderful. You I also know that you've had an incredible career and you know, is in our world of software engineering. A lot of people these days are focused on these thin layers of the stack, right? You're building front end apps or you're building websites or things on those lines. And I know if I'm correct, you got your start in the late 70s working with Fortran even. Can you tell us a little bit about that journey all the way from those days all the way to where we are now with AI and all the things happening? **Steven Stamps (07:39.318)** I can, and I might surprise you a little bit with the prologue to the story. So my mother passed away this year at the age of 100. Not a sad thing. She had an incredibly rich life. And when I look at her life, she was a teacher. She started her career in a one-room schoolhouse, riding a horse to work, building a fire to keep the kids warm in the wintertime. And then... Through the 60s, she was part of a NASA program to learn all about science and space and bring that back to her science classrooms. And then right up until she passed away, she could use a computer and a phone better than a lot of people. And I look at myself and when I first started in this profession, I was using Fortran 4, which had no subroutines. and it was on an IBM 360 using punch cards with JCL. So that was my beginning and then I... And I also, at the very beginning, we lived in a very different world. There wasn't UNIX, there wasn't open source. And so every computer company had their own unique bespoke hardware, their own unique bespoke operating system, their own languages. And I was counting up the other night, David, and I counted 12 different systems. that I had to use and learned in my first four years. IBM 360, DEC PDP, Wang, Univac, Burroughs, NCR controlled data, Honeywell, Data General, Prime, GE, and MacroData reality with the pick operating system. Every one of those had three lineal feet of technical documentation because there was no online docs like we're used to today. And so every time you needed to interface with the system, you had to break out. **Steven Stamps (09:47.214)** the docs and figure out how to use these unique metaphors and this unique approach. I got pretty tired of that pretty quickly. And so I... introduced the remaining phase of my career where I was an early adopter and advocate for new innovations. I quickly developed a special skill to identify where the industry and profession needed to go, what technologies would most likely take us there, and then would become an early adopter and advocate. for those technologies. And that started with the original AT &T Unix System 3. That's how I escaped all these proprietary operating systems. I built one of the first Unix data centers on Wall Street in World Trade Center 1 on the 76th floor using a PDP, a deck PDP piece of hardware, mini computer, and a nine track tape from AT &T Bell Labs with Unix System 3 on it. basically, I received a crate full of hardware from DEC with no operating system and a nine-track tape. had to write a C program bootloader to bootload Unix from the nine track tape onto the hardware. And all this arrived on a Friday afternoon and I had to have it up and running by Monday morning because I had new employees starting that were going to start writing C code. So that was the beginning. And then the other evening as I was thinking about this podcast, I put together a list of the major **Steven Stamps (11:47.138)** innovations that I adopted. The next was relational databases. Believe it or not, that was a new wonderful thing. Object-oriented programming. A side note there, before object-oriented programming in C, I was actually creating C structures that included data and function pointers. So I was essentially doing object-oriented then. That's how I organize these very large applications that I was building for Chase and the financial industry. Then it was Sun Workstations. I actually bought Sun Workstations from Scott McNeely and Bill Joy from the back of a station wagon on Wall Street in the very early days. Then there was PCs. They were invented after I started. Then the browser and the internet. Java was a huge step forward. REST and JSON. then Linux and the LAMP stack, cloud computing and virtualization, NoSQL, mobile and Android. I was very early with Android, very early with Kotlin. What a lovely language. Then Flutter and Dart. I have to confess I was initially disappointed that it wasn't Flutter and Kotlin, because I had already fallen in love with the language. But it's worked out well and I get along just fine. Recently, it's AI and machine learning. And currently, I'm reaching out to some folks in Silicon Valley because I'd like to get involved in home-based humanoid robots and using Flutter to build the mobile apps that consumers would use to monitor and manage and... provide some extra safety features for home humanoid robots. So that's a pretty good arc of my career that kind of matches my mom's going to work on a horse and then flying out to NASA to learn all about science. **David DeRemer (13:57.642)** It's it's super amazing and I'm humbled to be here chatting with you with your experience that you've spanned such an incredible arc of technology. forget you might know there's a famous computer science book that name is escaping me and like in the beginning it talks about how we're just kind of rolling around on the top of a stack of mattresses. The modern developer and I think they wrote that like 20 years ago and I think it's more so now where it's just these all these layers stacked up where the abstraction is so great that here we are writing. Dart and Flutter code and building all these applications. And you just took us through the history of like modern software engineering, you know, and you got a chance to see all of that with such incredible perspective. What are are there like some constants that you see have been consistent themes throughout that journey that even though we're dealing with AI and can deploy massive, you know, infrastructure with a command line sentence, right? Are there things that have held true throughout your career? **Steven Stamps (14:55.049)** There are... **Steven Stamps (15:02.56)** Let's. I think we all know that it's the people that have not changed. And I actually made a few notes here. Let me find because... **Steven Stamps (15:20.737)** since we have a moment. **Steven Stamps (15:36.13)** There we go. Save Dustin some time. So. **Steven Stamps (15:46.51)** The main thing to focus on as a leader and as an individual contributor is what has not changed. So I'm glad you brought that question to the discussion, David. **Steven Stamps (16:04.152)** People and organizations have not changed. One of the issues that's been universal, it's kind of regional, and I'll talk about that in a minute, but the culture of if it ain't broke, don't fix it. I've worked in and lived for extensive time in Chicago, New York City, and San Francisco. loved all those cities, loved the cultures, but they're very different. When I was in Chicago, it was very frustrating because the dominant culture in the Midwest is if it ain't broke, really bad, don't fix it. And for someone like me, who is a perpetual, virtual, fearless, refactoring leader, that was very frustrating. But that's... That's something that has always been there and has never changed. Another way it's expressed is good is the enemy of great. That's a common problem. What will happen is you'll build a world-class team of engineers and product managers and they want to build an amazing product for the organization and for the users, but something is in place that's good enough. It may even be object-oriented cobalt, just to have some fun there. But the engineers know and the product managers know that we need to evolve and move ahead. organization and many of the leaders there follow the path of least resistance. So that's number one. Number two, the people who are resisting change are often the people who built their career on the existing way of doing things. We, meaning you and I and our Flutter community, deal with that every time we try to get an organization to pivot to Flutter. **Steven Stamps (18:25.174)** You've got, in the case of Disney, I had 60 Android engineers building the ESPN app and ESPN Plus and a number of other Disney apps. There were equally 60 iOS engineers. They each had their own organization of leaders and hierarchy and politics. And to this day, I have not been able to get them to address the fact that they could be saving tens of millions of dollars every year and addressing a lot of those other issues you and I have discussed, if it weren't for the fact that the people that currently populate and lead those groups are resistant to that change. The third item I would point out relative to people and organizations is that technology changes very quickly relative to human tribalism. which hasn't changed for 10,000 years. you know, people, and in the case of our Flutter world, you've got the iOS tribe, you've got the Android tribe, and that's an ingrained part of human nature and is always a challenge to overcome and pull those teams together. Although to be very positive, I have done that multiple times. So I can assure you that it's very possible, but it takes a lot of work and strong political will from the leadership. **David DeRemer (20:03.49)** Yeah, I'd love to dig into that actually, if you don't mind, which is as someone who's been an innovator, you've ridden the wave of technology and have been able to adapt to the next one as a as opposed to just riding the one you're on in. And then you got to swim back out again and figure that out. You just stay out there, keep riding the next one. How what are some strategies or approaches you've found to help organizations and teams adapt and kind of make those transitions effectively? Because I think you're right. The human tribalism, the You know, maybe there's even genetic, right? And ingrained deep human behavior that makes us resistant to change. How do you get teams to switch over? How do you get teams to be more innovative and adopt new technologies as it comes along? **Steven Stamps (20:47.854)** So there's two different scenarios to talk about here. One is the Greenfield scenario, like we were discussing at ESPN as an example, where they're not using Flutter, they're not thinking about Flutter. In that situation, you can't do it from the bottom up. It's got to be from the top down. You've got to establish a trusting relationship with the senior leaders. You need to speak in terms of senior leadership value systems, aligning the technical issues with the balance sheet issues. How is this going to help us save money? Not only is it going to help us save money, how is it going to help us generate more revenue? or accelerate our delivery value to the market. So in that scenario, the only way in is from the top down. It's just not going to work otherwise. But the second scenario, which I enjoy a lot, is you have an application, you have a tech stack that at one point in the not too distant future, was pretty modern. And then my approach is to create a culture of perpetual refactoring. And I call it fearless refactoring. This happens a couple different ways. One is just about every time an engineer goes in to enhance a feature, or to add a feature, they see things that aren't quite right or that could be better. The way I run my teams is we immediately create a ticket for all of those. We immediately identify at what point in the future we want to work on that ticket. **Steven Stamps (23:10.551)** and we proactively schedule it for five sprints out, six sprints out, eight sprints out. And this guarantees number one, that you capture the idea. One of the metaphors, and this could be a separate podcast also, one of the metaphors I use with my teams is that we've all heard you live in a knowledge economy, but most people have no idea what that means. In my view, what that means is A knowledge economy is about having great ideas, resourcing those ideas to implement them and then deliver it to the marketplace for value. So the raw material in the knowledge economy is new ideas. So if my team is capturing every ideas and we've got a hundred great refactories in the backlog, your team doesn't do that. And then you get in the fourth quarter of the year, let's say, and people say, well, gee, what should we do in 2026? And then you start scrambling around for really good ideas. Whereas my team, we already have a backlog of hundred refactorings with identifying what the value is to the market, what the value is to the business. And we've also captured a lot of detail in that ticket so that we're much more successful in the knowledge economy because we have more raw resources and they're higher quality raw resources to build out the features and the opportunities for the business. that's one of the ways. Item number two, as a leader and especially as a senior leader, I choose to beg for forgiveness. So, **Steven Stamps (25:18.25)** I'll do this in one of two ways. One of the more transparent ways is I will schedule a POC. But in the back of my mind, I'm designing a POC that is a minimum viable product that I could deploy. And this is for two reasons. Number one, A lot of times, if you have a successful POC, what happens? The executive team says, hey, can you put that into production next week? And so I just anticipate that's going to happen and make sure at least that the metaphors and the abstractions are going to support a longer term production ready solution. But if that doesn't happen, what I may do **David DeRemer (25:53.57)** Mm-hmm. **Steven Stamps (26:15.074)** is go ahead and slip that into production anyway. and everything my team does in production is behind feature flags and everything is behind A-B tests. so slip it into production. Of course, it's behind a feature flag. It's also behind an A-B test with a staged rollout, but very, very quickly, I captured data. So then what happens in this scenario is I get found out, know, Steven, why did you deploy blah, blah, blah, whatever. And I'm like, well, I was exhibiting a executive leadership initiative. And by the way, here's the data. It's built, it's deployed. And here's the data that says we're getting X percent more revenue or X percent. fewer crashes. So do you want me to pull it out of production and lose that opportunity? So those are some of my techniques. They're not favorite techniques of some of my very senior leaders. But at the end of the day, that in some cases seems to be the only way I can keep my organization moving ahead and moving modern. I did that by the way with AI. We'll talk about that a little bit. later on, but that's exactly the approach I took with AI and previous employers. **David DeRemer (27:48.428)** I love the idea just going in saying like I'm gonna I'm gonna beg for forgiveness if you're the innovator if you're the person is gonna push and make change and like you're saying about just sort of the tribalism all the resistance to change all the all the things that are stacked against taking bold risky steps. If you ask for permission you're never gonna get it you know in those types of environments and cultures sometimes the only way to do it is to force it through I remember I think might have been another podcast but I listened to something once where they were talking about the. like entrepreneurial ventures. Usually there's like a person or team that starts it, you know, and they're just moving really fast, trying a bunch of things, taking a lot of risks. And then eventually that company gets serious enough where there's something to protect, you know, they've they're getting enough revenue, they got big customers, there's risk there. And then they hire like a CEO or something, you know, and they were describing that at this moment, you're introducing this tension in the business where innovation and operations are like exactly opposed to each other because innovation you want to try 10 things through nine of them away and keep the one that works. Operations it's like no, no, no, I want to reduce everything that is that I know is not going to work and I want to focus just on the stability safe stuff. like instantaneously you have this kind of competitive dynamic between safety and risk taking innovation. And I think that that's **Steven Stamps (29:08.142)** Sorry to interrupt David, but I'm intimately familiar with that scenario, but I contend that those two priorities are not mutually exclusive. I think they can be fully supportive of each other with the right culture, with the right processes. know, earlier we talked about checklists. and process discipline. You know, there's nothing that happens on my teams that isn't a thoroughly defined process. That's a process muscle that's been exercised hundreds of times throughout the year with checklists, with historical KPIs to track how we do that. So you take that along with a culture of Feature flagging and A-B testing, absolutely everything that goes into production. And now you can protect your company, protect your revenue, protect your assets while still being a fearless refactor, a fearless innovator, because those are behind feature flags, those are behind A-B tests, so that you've got data. Which the other side point there is that most teams product teams, engineering teams, they go on intuition. Whether you're triaging or deciding how to optimize the performance of a stack or whether you're ideating on how to optimize a user experience and user engagement, most teams are not aggressive about everything being driven by data. If you don't do that, if everything isn't behind a feature flag, if everything isn't behind an A-B test or an A-B-C test, then you're likely not spending your time and resources on the right things. **David DeRemer (31:20.078)** Yeah, well, if you're all prepared and your model is to beg for forgiveness, but you've done all your checklist to make sure that the likelihood you're going to have to ask for forgiveness is pretty low, that's going to work out pretty good for you, I think. **Steven Stamps (31:32.686)** It usually does. I'll use AI as an example and I won't go into any great details. But the other problem with innovation is the organizational politics. And I did a lot of AI work at my previous employer because the AI team in another part of the organization you know, after two years still hadn't delivered. **Steven Stamps (32:08.654)** a high quality product. And I personally had the expertise and I had the expertise of my team to be able to build out a lot of this. And it was extremely successful. But that was one of the rare cases where politically it was not successful because politics were just too big, too high, and it was about the politics, it was about power, it wasn't about delivering the best solution, most cost effectively and as quickly as possible. So, you know, we don't live in a perfect world and there are going to be times where the organization is just not going to make sense. **David DeRemer (32:58.002)** Yeah, this is something I think we talked to junior engineers a lot or you know, those of us that have been doing this for a long time. You learn that you know the engineering, knowing the SDK is knowing how to architect a code system, how to work with other engineers through the tools and everything. As you progress in your career, so much of it is about managing the people, not just the code communication, how to sell ideas, how to manage different personalities and knowing when to ask for permission or. beg forgiveness and all of these different things. And it becomes such a crucial skill that you have to develop in teams. Because you're right, no matter what we do, we can't get around that. But you've had a lot of successes in your career doing exactly this, like getting technology off the ground. And I know we've connected, and I'm fortunate enough to get to know you and meet you as a result of your involvement in the Flir community. Can you dig into that a little bit for us around? How did you find Flutter and how did you manage getting that in and kind of like leading a transformation to build Flutter apps in your previous roles? **Steven Stamps (33:59.084)** You bet. So as I had mentioned earlier, I led a team of about 60 engineers and product managers at Disney for the ESPN app and some of the other apps that I was responsible for. And I was extremely frustrated with the politics involved and the inefficiency evolved there. And so I was recruited away from Disney by Amazon, AWS, and in that case did not follow the advice I've given so many young engineers, and that is do not make career decisions based on compensation. So, know, AWS was paying me more money than God, but the culture and... The technology in the area that I was in was broken beyond my ability to be able to fix it. So, and it wasn't mobile. So, and at that time I realized two things. Number one, that I have a huge passion for mobile. The reason for that is because of the intimate relationship with the user. It's an entire data center in the pocket. of each of your hundreds of thousands of users and more powerful than the servers that I was using in the two thousands to manage massive transaction volumes for banks and brokerages. So very exciting. But I hated the politics and the technical stove piping of iOS versus Android. **Steven Stamps (36:11.32)** You know, there's been a couple of other efforts before Flutter to do cross-platform mobile, and none of them were satisfying. I thought I would take a swing at Flutter, and I used Flutter at a startup, a fintech startup, a new bank called Enzo, to build a banking app. Greenfield for a startup with Flutter. there I had done some research before that, but that was where I proved to myself that there wasn't anything that I wanted to do on a mobile device that I couldn't do very well with Flutter. so that was where I started. That startup, it's funny, when I was out on, when I lived in San Francisco, I can't tell you how many engineers I hired and worked with who had worked for 10 startups already in their career. And I experienced that with Enzo, with this FinTech startup. They were underfunded and weekly led as far as the product vision. And so I had, unlike today, I'd put an open for work flag in my LinkedIn. And that afternoon I had an offer from NGM, a senior VP that I had worked with at Disney on the ESPN app, reached out to me and said, Hey, things are a mess over here with our mobile. And. I need you to come in and... **Steven Stamps (38:13.614)** clean things up. When I went on board, they had one full-time employee on that team. The app had been built by a range of contractors and with no leadership from MGM engineering leaders. And one of the things I will say, a contractor cannot be terribly successful without full engagement and, leadership, from their client. So otherwise you're just caught, you know, guessing what's going to work or what's going to be successful. And, it's unfair to you and it doesn't provide the results. So that was what it. going on there. So I, I, built out a world-class team of Flutter engineers, shout out to every one of them, amazing team, and, established this culture of, fearless refactoring, established a culture of no one swims alone. you know, every feature is owned by the entire team. Every problem is owned by the entire team. established a core architecture team, which were essentially the strongest and most innovative engineers who met every morning for an hour at 730, bless their heart. But that was 730 Pacific time. The reason for that time was that was the one time that my boss and other C-level executives wouldn't schedule a meeting over the top of our meetings. So that guaranteed every day we had an hour to talk about what it was not a stand up. It was let's talk about, you know, what did we learn? You know, you know, what are our challenges? What are the opportunities? **Steven Stamps (40:31.138)** those kinds of high, high quality engineering discussions happening every day with a loose agenda. Basically, in any 24 hour period, this also helped us, by the way, dramatically cut back on meetings. Because instead of either me or other team members calling a meeting for every important topic, you just add that topic to the agenda for tomorrow's standup. It was called MCOR for Mobile Corps. And the philosophy was on average, you'd only have to wait 12 hours to get answers and get help. And so just wait until tomorrow morning and then you've got the entire team there and attendance was required. So everyone had to be there. and had to be engaged. And it was a phenomenal way to lift up the entire team. Everyone learned from everyone and. It was a really exciting meeting and exciting time. **David DeRemer (41:45.454)** And you managed to convince the leadership there to rebuild with Flutter. And so what was that process? **Steven Stamps (41:54.466)** They are, and in fairness, had started, but there was, but at the time I was recruited, there was question, you know, was the flutter decision the right decision, you know, because they weren't getting what they, what they wanted to, get out of their mobile app and out of their mobile team. And it wasn't a flutter issue. It was a, it was a process issue or culture issue. **David DeRemer (41:57.39)** Okay, cool. Nice. **David DeRemer (42:24.95)** Yeah, we've seen that as you can imagine many, many times where you read the label of Flutter and all those great benefits, know, 50 % or more reduction in cost and doubling speed and quality improvements and all this stuff. And then you don't see it and you're like, what's going on? And it's very easy to blame the tool, especially if people have built their careers on expertise and things they don't want to say, no, it's because we implemented it incorrectly or something like that. But you know, it is just a tool. And a lot of it is the architecture, the processes, the practices of the teams and the things that go into it. And it's actually interesting, right? And I'm sure like you observed this with your team. You can come in and you can, once you fix those things, it's amazing how much progress teams can make. when you just start aligning on doing things consistently, repetitively, like the meetings being mandatory and those sorts of things, just everyone's going to write tests, you know, we're going to AB things, we're going to do feature flags. We're going to set up these best practices that we're all gonna follow swimming together. Were there specific things like that that you came in that like, what were the highest impact kind of changes you made pretty quickly? **Steven Stamps (43:32.758)** So before I jump into that, bring me back around. But I want to point out that those are all critical and important, but that's not enough. What you also have to establish is an end-to-end clear architectural vision of where you want to take this technology and where you want to take this app. And the vision that I introduced into this team was an end-to-end event-driven architecture. We'll talk about that a little bit more later, but it's really critical that both the product people and the engineers also have a clear vision architecturally. what the theme and what the objectives and what the benefits are of this strategic architecture that you're pursuing. And also, spent a lot of time educating product managers on the architectural vision and why it was important and why they needed to thoroughly understand it and incorporate it into every ticket and every ideation of new and improved features. Yes. So let's roll back to your original question. When I came on board, there was a new mobile release roughly about every seven weeks. Unpredictable. The release would go out with more than 100 bugs, and it was a mess. One of the first things I did was create a. **Steven Stamps (45:34.456)** worked with the team to create a very thoroughly defined release candidate and release process with KPIs that would be followed religiously for every release. And initially we went to releases every four weeks, then we tightened it down to every three weeks. and then in less than Five months, we were down to every two weeks, a new release, and the number of bugs was single digit, and none of them were P1s or P2s. **Steven Stamps (46:24.664)** That did a couple of things. It allowed us to publish a release calendar a year in advance. Why was that important? Because when stakeholders or technology partners would come to us and... talk about when they wanted to deliver something, my questions would always direct them back to the release calendar of when do you think you want to release this. Now, we'll get in a little bit to my perspective as a senior leader. I spent a lot of time in senior leadership meetings looking at PowerPoint presentations that were purely aspirational. There would be a slide with a Gantt chart showing we're going to deliver this in Q2, deliver this in Q3, deliver this in Q4. **Steven Stamps (47:41.824)** So one of the benefits in those meetings, I would pull up the mobile release calendar and I would say, so you said Q4, do you mean this release at the end of December? And by the way, we have no releases at the end of December because I don't do releases after Thanksgiving. The last release, if it's a Q4, the last release is scheduled for November 11th or whatever the date happens to be. Is that the one you're talking about? They would say yes. say, well, so if that's when you plan on releasing that feature, which is being driven by this back-end service, then we'll need a sprint to incorporate to to test and validate that significant new service. We'll need a sprint before that to integrate that API and that UX into the app. We'll need a sprint before that to get a UX design from the design team, et cetera, et cetera. So that means that you are gonna have to be done with building your service by September. whatever. And then they'll be like, whoa, no, no, no, no, we'll, we'll, we'll finish our code in December. And then you start having a realistic conversation about, okay, so we're talking about, this is going to be in release 26.3 in February of 2026. And then now we talked before from an aviation perspective about, Takeoffs are high risk and if you don't prepare properly for a takeoff with the right equipment and the right preparation, bad things can happen that doom the rest of the flight. This is a parallel with that metaphor is with this technique, I make sure in that aspirational sea level meeting that we get a realistic target date for delivering that feature. If I don't do that, then what happens **Steven Stamps (50:02.046)** is I sit there, let this happen. Commitments are now made to the board of directors and to business partners external to the company that we're going to deliver something on December or whatever. We've all been there before. What happens? Nights, weekends, we don't care about quality. We're just throwing doodoo against the wall, see what sticks and we ship it. So. This is an example, one of many of techniques that I use to get ahead of things. But not be sitting there in that meeting as a negative Nancy, not be sitting there saying, we can't do it, we can't do it, but sitting there and walking the team through what the realistic opportunity is to build and deploy this. **David DeRemer (50:58.382)** I want to also dig in with what time we have left around recently you put something on LinkedIn that you called Flutter as a business imperative. And so now that you've kind of worked with it for quite some time and I've seen the actual impacts of this change once you get teams on board and you get everybody working to get the impact, you kind of have been advocating for Flutter, not just as a tech choice, but really as a business strategy. And I'm curious if you could explain some of your ideas that went into that. that document you prepared and why you think Flutter and tools like it, right? There may be others and things in the future are a valuable part of your modern technology approach. **Steven Stamps (51:40.194)** Well, the reason I wrote that article is, and I'm actually gonna look it up to make sure we. **Steven Stamps (51:53.944)** There we go. We hit all the points. The reason I wrote that article is I think this is a pivotal moment in time for the Flutter community and for leaders such as yourself in that community to lean in to the opportunity that we have. due to the changes in the economy. We've all witnessed companies are laying off tens of thousands of engineers and other employees. The reason they're doing that is in a lot of industries like hospitality, where I was previously, The profitability has, the revenue has dropped. The profitability of that revenue has dropped. Expenses have increased. And so very senior leadership are cutting employees and cutting contractors so that they can maintain a certain level of profitability. **Steven Stamps (53:20.302)** This is our moment because if you wanted to save a big chunk of money at Disney ESPN or at any other company that is currently maintaining a separate iOS and Android code base, there is an easy 30 to 50 % or even more cost savings available right out of the box. **Steven Stamps (53:51.778)** that's consistent with the current culture of pruning back the organization to cut expenses, but also is a, unlike a lot of those other decisions, would be a very smart strategic move to position themselves to perpetually save $10 million a year on staff or on contractors. But The cost savings from a strategic standpoint is only, in my view, a very small part of this. One of the big issues that I encountered at Disney was the issue of parity and feature parity and parity debt. Either the Android or the iOS team would rush ahead with the feature, get it done before the other platform and deploy it, or they would rush ahead and get it done and then have to sit on it for two months waiting for the other platform to finish and test their feature. That is hugely expensive from a business standpoint if you think of it in terms of inventory and inventory turns. Back in the 90s, there was a big push on just-in-time delivery of raw materials. The idea there was, know, an auto manufacturer doesn't need a million bunkers piled up at the beginning of the production line and paying for all that inventory and the space to store it when they're only going to create 10,000 cars a day. Well, very few people think of it this way, but all of that code and all those PRs that you've reviewed and merged and are ready to go into a release candidate and are sitting after, that's inventory. You've paid for those developers, you've paid for that processing time. Those are finished goods, but they're sitting back in the warehouse and the company is not benefiting from those benefits. **Steven Stamps (56:18.378)** And so you need to focus on what a business is referred to as inventory turns. As soon as an engineer and a product manager works on a problem or a new feature, that needs to quickly be turned into a release candidate and deployed into production. And the quicker you do that, the quicker you get a return on investment. So, debt is a huge, huge issue in my book that nobody talks about. Also future-proofing the asset. Every time you write a line of code, you're creating an asset for the company. If that asset is in flutter, **David DeRemer (56:48.942)** Fantastic metaphor. **Steven Stamps (57:11.222)** then not only can you deploy it to iOS and Android, but we did this at MGM. We deploy the same Flutter code to Windows, to Linux, and to the web. So now to be able, basically it's like a tangible goods company being able to sell the exact same product. to three different customers and make that revenue three times over. So that's something else that people don't talk much about that in my view is hugely valuable. The other topic is the other driver is velocity and market agility is when now instead of having resources spread across different platforms, Android, iOS, Windows, web, all your best people are distilled down into one team that can move much more quickly with much higher quality to address new opportunities. And one of the things that I've achieved there and that companies can achieve there that also isn't discussed is something called talent density. So rather than having these three or four teams across, know, Web iOS and Android with one or two star players holding up each of those teams, if you will, now you can have a single team with dominated by senior engineers, principal engineers, and the talent density is tremendous. The reason that's important is I contend that you are essentially equivalent, you are the average of the five people that you most commonly work with every day. And so, as an athlete, if you just played with world-class tennis players, you'd be a much better tennis player than just playing with your buddies down the street. It's the same thing with talent density. When I create those teams and I did this at MGM, everybody lifts everybody up. **Steven Stamps (59:37.582)** to new levels that were unimaginable. And that results in velocity and market agility. The last point I would make that's a driver for this is risk and brand consistency. This is the way to make sure that **Steven Stamps (01:00:03.948)** Your digital storefront is identical and the same high quality regardless of the platform, web, iOS, Android, Windows. And now because you have talent density, not only on engineering and product, but on QA, you can also have even higher quality. deliveries on your release candidates and on your deployments. So there are so many reasons more that we haven't really talked about here, but so many reasons why now is the time for companies that are maintaining a separate iOS and Android team to pivot to Flutter, create a single team with tremendous talent density, and position themselves strategically for the future. **David DeRemer (01:00:55.854)** That's those are all incredible points. And I love the labels that you have for those talent density and all those. Those are amazing. We'll definitely need to help you tell the story and get it out there. All the things you're pointing at. We also believe and agree with and you've done a really fantastic job of summarizing those. So I mean, we could we could be we could keep talking all day long, Steven. I think in fact, we might have to do more because I think we're just scratching the surface on probably 20 different things we could each spend an hour on. But I think to close out this one, one question I've really enjoyed asking and you have so many unique experiences. I'm curious what yours would be. If someone wrote a biography of you or you wrote a memoir one day, what do think the title would be and what would it be about? **Steven Stamps (01:01:42.27)** That Stephen cared deeply about his team and each team member, that he cared deeply about the consumers of his technology and that he was a fearless refactor. Yes. **David DeRemer (01:02:07.244)** maybe fearless refactoring. **Steven Stamps (01:02:09.646)** Fairly pursuing new technology, new innovation. So those are probably the three areas. But we've talked a lot today, David, about technology. But the bottom line for me, I love the technology, but it's the people I really have a passion for. if you were to ask me what I'm most proud of, I can... point out people across a range of industries in extremely senior positions that started with me out of college or that worked on one of my teams. And the impact I was able to have on their lives and on their careers is something I'm really proud of. And not only over the arc of their career, but I worked very hard to make sure that I position my teams to win. So from our earlier discussion, I make sure they don't get committed to unrealistic deadlines, unrealistic deliverables. I make sure that they're not living in a death march, that their weekends and their evenings are theirs to enjoy with family and friends and hobbies. Those are the things I'm most proud of. Huge passion for the technology, huge passion for the next chapter. I'm really hoping to have an opportunity to get involved in the home robotics industry. I'm looking at some opportunities there, but I would be delighted to be in any scenario where I can provide leadership to a company that wants to pivot to Flutter or wants to incorporate embedded AI, which we haven't talked about and is a whole podcast. embedding tiny language models in Flutter apps, building narrow language models to deploy as part of services in the cloud. **Steven Stamps (01:04:32.194)** Those are all things that excite me, but at the end of the day, it's the people. **David DeRemer (01:04:39.084)** Hmm. Wonderful, especially in today's world where there's so much talk about replacing people with technology. It's wonderful to hear that sentiment and I tend to agree with you. Without the people, what fun is it even? know? Well, thanks, Steven. This is amazing. We're definitely have to do it again sometime, because there's other things we could definitely talk about for quite some time. If people want to connect with you, find you, what's the best way to get in touch? **Steven Stamps (01:04:52.075)** Exactly, exactly. **Steven Stamps (01:05:06.018)** The best way would be a reach out to me on LinkedIn with a connection request. And specifically, I would be interested if you want to connect relative to transitioning your organization to Flutter, if you want to connect on implementing a multi-tier AI machine learning architecture. I've done a lot of work there. If you want to reach out. because you're part of a humanoid robotics company and are interested in my ideas there for a mobile app for your customers to use. Those are the topics that I would be very excited to hear from this audience. And as always, send all my love to the Flutter community. It's very important that you're successful. And there's nothing I wouldn't do to support that success. **David DeRemer (01:06:09.642)** Yeah, I tend to agree with you. Well, thanks so much. Appreciate your time today and for being a guest on our show. And there will be some links in the bios for finding you and all the things you've been talking about. So thanks so much. **Steven Stamps (01:06:22.2)** That sounds great. Take care. ### Building a Local AI from Scratch: The Story Behind Cow Source: https://verygood.ventures/podcasts/joanna-may-local-ai-from-scratch/ On the inaugural episode of Very Good Engineering, Jorge Coca is joined by Joanna May, an Engineering Manager at Very Good Ventures, to dig into her pet project Cow, a local AI terminal companion built in Dart. Joanna explains that Cow lets you interact with a local AI model from the command line, similar to cloud coding assistants, but running entirely on your own machine rather than relying on an external service. The idea grew out of frustration with the high costs of cloud-based AI tools like ChatGPT, and a desire for a personal, accessible alternative. Joanna is candid that building a local AI turned out to be genuinely hard, with much of the difficulty coming from integrating local AI models and wrestling with native bindings and package compatibility. She built Cow's terminal interface using the Nocturne library, describing the experience as just like using Flutter, but for the terminal. Beyond the technical work, Joanna and Jorge talk about the personality she gave the project, including an ASCII art cow mascot she crafted by hand. Cow reflects her mix of Flutter and game development experience, and the conversation lands on a recurring theme: the value of accessible, local AI and the simple joy of building something uniquely your own. ### From Self-Taught to Google Engineer: Navigating the Flutter Ecosystem Source: https://verygood.ventures/podcasts/rody-davis-self-taught-to-google/ Jorge Coca talks with Rody Davis, a Senior Developer Advocate at Google, about a coding journey that started far from any traditional path. Growing up without developers around him, Rody found his way in while stuck with a tedious manual process at work, discovering Google Apps Script and using it to automate calendar events and forms. That first taste of solving real problems with code set everything else in motion. Rody describes teaching himself through books and online tutorials, then moving into mobile development out of a desire to build music apps. Inspired by an Apple keynote featuring a self-taught coder, he learned iOS development with Swift before frustration with maintaining separate iOS and Android codebases led him to Flutter. His first Flutter project, a music app prototype, came together in about six weeks, far faster than his native work, and grew into Pocket Piano, which earned him recognition in the Flutter Create contest. The conversation turns to Rody's current work on Antigravity, Google's multi-agent development platform. He explains what makes it distinct: an intuitive IDE that feels familiar to VS Code users, the ability to manage multiple agents working on different tasks at once, and tight integration with Google services. Throughout, Rody returns to the themes that carried his career, self-education, persistence, and staying engaged with the developer community. ### Fluttering Forward: Innovation and Community in Tech Source: https://verygood.ventures/podcasts/abdallah-shaban-google-fluttering-forward/ In this episode of Build to Succeed, Abdallah Shaban, an Outbound Product Manager at Google, shares his journey through product management and the open-source ecosystems around Flutter and Dart, including how the outbound role centers on customer insight gathering, cross-functional collaboration, and active community engagement. He reflects on a career path that spans launching Jordan’s first 4G network, earning an MBA in California, and building experience at Petco, Amazon, and Y Combinator, with a consistent focus on developer tooling and advocacy. Abdallah also emphasizes the importance of a well-rounded life, describing how music and Latin dance reinforce the adaptability and improvisation that strong product management requires, and he closes with lessons on staying cohesive in messaging while championing both the product and the people who use it. {% audio_embed url="https://open.spotify.com/embed/episode/53DpUBMNtSGFCcMR7ujmk4?utm_source=generator" /%} ## Transcript **David DeRemer (00:00.652)** All right, Abdallah, welcome to the podcast. Thank you for having me. Super excited to be chatting with you today. Let us get started by why don't you introduce yourself to the audience? Yeah, absolutely. My name is Abdallah Shaban. I am an outbound product manager working with the Flutter team. Amazing. so I like to start to get us going by what's something you do kind of outside of work, you know, obviously we'll talk a lot about work and our professional world, but what's something you do outside of work that helps you get better at your actual. **Abdallah Shaban (00:33.134)** You know what? I actually want to take a step back and like, can we start over? And the reason why is because my role just changed. really? actually want to talk a little bit about that. Do you mind if we start over? Okay, let's do that. Absolutely. No, of course, yeah. Your role just changed? Yes. So basically, what's happening is that right now I joined, I was working with the Flutter team, and I was also working with the Go team as an album product manager. And now just recently, I'm also going to be working with the Genkit team as well. wow. So I'm going to be working on a lot of our open source products, a lot of our frameworks and languages products here at Google. So I'm really, really excited about Awesome. All right. Well, so we'll go start and we'll go back to zero. So Luke, can you just make sure that we reset? **David DeRemer (01:28.91)** Okay cool. **David DeRemer (01:33.518)** All right, Abdallah welcome to the podcast. Thank you so much. It's really, really great to be chatting with you today. Likewise, can you introduce yourself? Yeah, absolutely. My name is Abdallah Shaban. I am an Outbound Product Manager at Google. And I'm currently working with a lot of our open source framework and languages teams, which include Flutter, Dart, and then also Go and Genkit. Amazing. What an awesome set of technologies to work on. Definitely going to dig into that heavily to get us going. I like to start with something that's actually not work related. what's something about you, something you do in your personal time that you think actually makes you better at your day job. **Abdallah Shaban (02:14.808)** Yeah, great question. So I try to keep my life pretty well rounded in terms of my interests. One of my favorite things to do is I'm a musician. I play guitar, I play keyboards. I try to sing horribly, but I still do it. And that's one of my favorite things to do. And I've also just recently as well. Well, not recently, it's been a while now. It's been like five years or six years. I've been getting into Latin dance. My wife's family's from Ecuador. Latin dance was a really big part of her life. And she made it pretty clear that if we were to be together, that I needed to learn how to dance. So I've been doing a lot of salsa and bachata. So those are, I would say like the two things that I like doing the most, like lots of music, lots of dancing. That's awesome. Were you an easy convert to the dancing or did it take a little convincing? It really helps being a musician, I think. Like, you know your times. Like for me, it's just like, okay, how do I slot into the rhythm? So it really, really helped. I definitely like, I'm working on my vocabulary now, like, like just like having enough moves and like being able to not think about it as much because that's when my analytical brain comes in and I'm like, okay, I need to make sure I'm doing it in this sequence and stuff. And I like, I just need to do it frequently enough for me to like not have to think. So like music and dance, there's definitely like a, there's like a code to it, right? Cause there's sheet music and there's like technical aspects, but then there's all the improv improv that comes with it. Do you, does that come to play in your, in your work? **Abdallah Shaban (03:52.172)** Yeah, absolutely. I'm very much, would say somebody that likes to build a lot of frameworks for how I work. I like to make sure that if I'm doing things and I'm doing them often, that there's a repeatable way for me to be able to do them. So that's where it's kind of like your sheet music, like having your sheet music and making sure that you're following the recipe for how to play the song right or how to dance to the song right. But then There's also feel, like you also need to feel the music, you need to feel the dance. And that's where I'm very adaptable as well. Like, yes, I care about frameworks, I care about processes, but it's also very important to be adaptable, especially in this crazy world that we live in right now, where every other day there's a new announcement about a new tool or a new AI thing that has changed the world. So you definitely have to be very, very adaptable. I love that insight. That's, that's amazing. So tell me about your role. So you're a product manager at Google, but also kind of a flavor. That's this outbound product manager role, right? So can you explain what that is exactly? Yeah, so I'm actually the first of my kind for frameworks and languages, which is really interesting. the Outbound Product Manager role is, feels to me that the, it's pretty unique to Google because it is actually a role that combines what a typical product manager does in terms of like talking to customers, gathering insights, helping with strategy, where should the product go? And that's where, as a part of my role, that's where I partner with our product teams and be like, hey, like, here's what I'm hearing from the community. Here's what I'm seeing. And then ultimately, they're the decision makers there about where to take the product. And then the second part of my role is working with a lot of our dev role and marketing teams in terms of we're going to market with a new feature. We have this new release coming up. There's interesting moments that we want to talk about. How do we make sure **Abdallah Shaban (05:56.012)** that we're doing this in a way that's like cohesive across all of the different types of content that we're putting out. And then the third part of my role is really around like truly being a part of the communities that I'm supporting and really being like an outward facing participant in that community. So like being on socials, like talking, building up relationships with customers and partners like yourself. So I would say those are the three pillars that make up what an Avon product manager is. Interesting. So would you say it's kind of like DevRel is this like you're an engineer, you understand like how to do things and you're helping, you're actually kind of communicating externally to help people understand what to do, but also getting feedback. It's kind of like the equivalent of that to the product side. It sounds like. It actually is a very good way of putting it. I definitely think of myself as an advocate internally and externally for the product. And I partner very, very closely with both product and developer relations and our marketing team and help connect pieces or and just make sure that we're all using the same narrative, the same language and making sure that we all have the same message. Yeah, I love it. I think, honestly, if I can say since you've joined the team, I feel like it's been really exciting to see sort of the impact you've already had in terms of like connecting the community and kind of really making sure that it's, you know, the community's pulled in the customers, the people actually using it, building with it every day and and also kind of, you know, getting their feedback and engaging them and just helping build the overall story. How did you get into this? Let's walk through your career journey. What brought you all the way to Google? **Abdallah Shaban (07:38.434)** Yeah. Well, let's see. Let's start from the beginning. So my family is originally from Palestine, but I grew up in Jordan and I was born in Jordan. Lived there for the majority of my life. But then I was also my time was split between Jordan and the United Arab Emirates. was grew up in both of those countries, but mostly in Jordan. And then I did my undergrad in computer science. I worked in telecom when I was in Jordan. And one of my proudest projects actually was I launched the first 4G network in Jordan, which was really, really cool. That was one of my babies, like all the way from like getting the hardware, communicating with people in terms of like securing the right deals and which protocols we should be using in our billing and charging systems and fantastic experience. And then from there, I really felt that I wanted to, I felt like I was, like my world was really, really small and I wanted to just have like a much wider view of the world. wanted to, I was starting to get jealous of all of my friends that were leaving Jordan and living in all of these amazing places, telling me all these crazy stories. I was like, okay, I think I want to leave Jordan and go somewhere else. And I actually thought I was going to go somewhere in Europe because they pay for your school. But ended up happening with me was that my mother had actually moved to California and she got married and moved there and she was like, you have to come to California. This is the place that you need to be. You want to work in tech. Like California is tech. Like you have to come here and I'll help you. We'll figure it out together. So that really is what made me go to, to California and believe it or not, like you're, you're not going to believe it, but literally the words that she used to convince me was you love Google so much. The best chance for you to work at Google is for you to be at California. And this was eight years ago. So I went to I was living in San Diego at the time. I did my MBA there. And while I was doing my MBA, I got an internship at Petco, the animal supplies company, expecting to go in for three months, do my internship and leave, ended up finding a lot of really interesting problems to solve that I was able to solve pretty quickly. And **Abdallah Shaban (10:01.878)** A VP reached out to me and was like, Hey, you seem like you really want to do a lot of great work here and you've been doing a lot of great work here. What do you want to do? And I was like, well, I came to California because I wanted to be a product manager. And she was like, okay, you're officially our first product manager. Like she literally like low key knighted me there. And I was like, okay, but we don't have product managers here. She's like, okay. Yeah. We've been thinking about creating that function. You'll be our first one. So I was the first product manager at Petco as an intern, which is really interesting. It's awesome. **David DeRemer (10:31.672)** pressure there to figure that out. Yeah. And then they built up like a whole network of product managers and I was a part of that organization for a while and I really got a chance to grow there. I was there for about four years and I became the product manager that owned a lot of our supply chain tech, our last mile delivery tech, in-store operations, call center operations. So really ops heavy role, which from my way of operating as somebody that likes process and frameworks was like a great fit. So I was able to really do a lot of great work there. Loved it, but I really wanted to work in tech. So Amazon reached out and they were like, hey, you're a technical product manager that has Flutter on your resume. We'd love to have a conversation with you because we have this Flutter SDK that we're starting up and it sounds like you would be great as our first product manager there. So I went and basically interviewed and got in and I was working at AWS Amplify for a couple of years and I was a product manager there owning the AWS Amplify Flutter SDK. And then I also ended up owning the JavaScript Task Script SDK, our authentications features, and then notifications and analytics as well. Those were my primary roles. But the moment that I joined, the primary thing that I was looking at was the Flutter SDK. And I noticed that the Flutter SDKs were written as wrappers on top of our iOS and Android implementation. And as somebody that had... been watching Flutter since 2017, since the alpha days, since the moment that Flutter had gone out, I had been a part of the community. I've been watching Flutter grow. was always watching every single keynote when Tim Sneath would talk about all of the new things that Flutter is doing, when Eric Seidel would have all of his deep dives. So was very much in the community. And when I joined the Amplify team, I was immediately like, no, no, no, we're going to make these Dart SDKs. **Abdallah Shaban (12:29.674)** Absolutely not. We need to get web support. We need to get desktop support. Luckily, there was already a lot of those conversations happening internally. So I was able to sort of like jump on and be able to like really help drive that work. And I'm really proud of the fact that we were able to actually get those SDKs out. And we were the first major cloud provider that provided like SDKs that were fully Dart native, which I think was a great thing for the community. Yeah, yeah. No, absolutely. I mean, we work together. It was amazing. Thank you. **Abdallah Shaban (12:58.926)** a lot like during that time to like just like talk about it, to like make sure that like forcing the community understand that this is something that's available. So that's really was kind of like my first entry into developer tooling, like professionally speaking. And while I was there, I was seeing the success that we were getting with our full Dart story. And I really wanted to grow that. really wanted to, a lot of what I was starting to hear from developers is I wish I can build every part of my stack with Dart. I love Dart's tooling. I love the type safety. I love the null safety. I love everything about the IDE tooling that exists. I wish I could use it everywhere in my stack. So I started pitching internally Amazon. It's like, Hey, we could do this, blah, blah, blah. And there just was not a strong appetite for it. So I decided to leave and I, when I left me and one of the engineers that was on the Flutter SDK team, AWS Amplify, we started a company called Celeste and we got into Y Combinator, which was amazing. probably one of the most pivotal moments of my life, the amount of things that I've learned in the, you know, three months, four months that I was a part of Celeste was just unbelievable. just, it was a lot of deep programming, actually, startups. I thought were a lot more complicated than what they ended up actually being. When you actually learn from people that have seen thousands of startups go through their pipeline, they teach you a lot in Y Combinator about how to. Like I would go in and I'm like, I would have this complicated proposal about how we can get partners to work on Celeste with us, blah, blah, blah. And my YC partner, Dalton Caldwell, he basically told me, it took you five minutes to explain to me what it is that you want to do here. So by default, what you're doing is incorrect at the stage that your company is at. This is no, you need to be scrappier. You need to be faster. You need to be. So it really like helped reshape the way that I think about building products, which was really cool. I decided to step away from Celeste and then I was working in fintechs with specifically in fraud and compliance for fintechs and banks did that for about nine months. And right before I should join that company, Google had reached out and they were like, Hey, my friend submitted a referral for me. And he was like, Hey, like there's this organization that's interested in hiring you. So I went through the interview process, but I wasn't super excited about that role. **Abdallah Shaban (15:27.17)** So I decided to not take it. But then I joined this other company, worked there for about nine months and then Google reached out again and they're like, Hey, you weren't interested in this other role, but there's this role. it's a newish role. It's Flutter, Firebase. was like, say no more. I don't know what it is. I'm in like, we'll figure it out. Like, let's just get me in there. And, and that's, that's how I made it there. So it was like very much, I would say a coincidence that I happened to land in the Flutter team because I had. been in their system and they were looking for folks and I just happened to exactly match what they were looking for. So I'm very, very grateful, very grateful. What an incredible story. mean, there's so much to unpack in there from your early days all the way through and the building of that all the way from building 4G networks, getting frameworks, getting really operations oriented operations roles into frameworks on the other side. And I think that's really awesome. And I will say it again, thank you for all the effort you and the team at AWS put in, because I think that really helped validate like when AWS is doing that kind of work, committing to dark, committing to supporting Flutter at the time, I think that was really important and strong signal and it's awesome to see you also get personally the results by the opportunity to go through by Combinator the opportunity to work at Google. So thanks for everything that you've done and what an amazing story. I wanted to ask you about product manager the role of that. You you were you said you wanted to do it. You knew what it was or had an idea what it was at Petco right was Petco. Yes, it was. I knew about it even before, honestly. you're going to think I'm just saying this because I'm on this podcast, but like the person that made me realize I wanted to be a product manager was Tim Sneath. Watching Tim Sneath, the energy, the passion he had for Flutter, the way he would engage in the community, the way he would communicate his vision about where Flutter needs to be, like this like sense of joy, the fact that he was able to **Abdallah Shaban (17:25.879)** be a big part of building the community that Flutter eventually was. I looked at him doing that and I was like, I want to be that. Like whatever that is, I want to be that. So, so it's, it's very insane to actually be on the team that inspired me to go on this career path. It's, it's very, it's insane. That's fantastic. Well, you're in the right spot. Like when you think about product manager, now you at Google, they've kind of created this outbound. You've got, mentioned sort of a technical product manager. for people that maybe aren't in tech or, or, you know, they kind of encountered a product manager, but they're not maybe working with them all the time. How do you define the role of product manager? And in particular, those, the different flavors, like sort of just normal product manager, technical product manager, maybe outbound. I don't even think there's a really good answer there, to be honest. I think, and the reason why I say that is because I definitely think that the product manager role is very malleable. And it is a role that actually needs to adapt to the needs of the situation and the product that you're on. And I can say that very confidently because I've worked across a lot of different industries and my role was very different even though I had the title of product manager. So, but I would say typically, its, let's say like base foundations of what a product manager does is you're really advocating on behalf of your customers. That's like at the core heart of it. I mean, you were a product manager before and you know how important that is. So it really is like having a close understanding and empathy to your customers. What do your customers actually want? What are some of the missing gaps in your product that's making customers feel that they're unable to use it in the way that they need to? What are ergonomical things that you can do to help make your product more successful? That's, I would say, like fundamental pieces. But then around that, it's how can I build an ecosystem for my product to be able to thrive? How can I partner with other cross-functional teams to make sure that my product is successful? So a lot of it is, I would say, a big, big part of it is actually a lot of soft skills. It's a lot of **Abdallah Shaban (19:38.656)** Empathy, it's a lot of listening. It's a lot of being able to like really digest a humongous amount of information that you're getting, whether it is from product feedback, from customer feedback, from support requests, and being able to like distill all of that into where are we going? What is the thing that we need to build next? What is the roadmap? What is the vision? Like, what are we, where are we trying to go? I would say like at its core foundations, That is what a product manager does, regardless of whether you're an outbound PM, whether you're like an internal, like a service PM, or whether you're like a technical PM, that's at the heart of it, what you do as a product manager. Love that. That's great. If someone was kind of up and coming and they were interested in getting into this career, what advice would you have for them? What are the things or skills they should develop or how do you develop those skills? Yeah, I've, I work with so many people on a weekly basis that reach out and have exactly that question. and one of the best things that I have seen is you have to find ways in which you can practice those skills in your existing role. That has been the thing that I have seen be the most successful for people that were trying to break into product management. because one of the things that there's a saying that like, I'm not sure you've heard this before. One of the most difficult things about product management is breaking into product management. And I definitely think it's like a, it's, it's, it's a bit of a chicken and egg situation. It's like, need to have a product experience to be a product manager, but then how do you get the experience? So one of the things that I tell people all the time is there must be in your current role, a problem that exists that you can truly take on and own full stack end to end. **Abdallah Shaban (21:29.208)** from defining what the problem is, from advocating for this problem to be solved, from finding how to staff it and resource it, to shipping it, and then actually demonstrating how was this able to deliver a value for you. Find ways in which you can do that in your own role. And no matter what your role is, there most definitely is a problem that is maybe something that you can then take on and then be able to truly be the product manager for that problem. Yeah. Very cool. I love that. That's a good way to think about it. It is full stack, that product manager role. It's, you know, problem definition. And I like how you said, even making sure you're, you're advocating for the problem, you know, cause part of it is getting everyone else to agree that it's a problem to solve. 100%. I think that's one of the biggest things is as a product manager, you're really doing a lot of selling all the time, all the time. And I noticed that because it really helped me in my startup, actually. Because I had that muscle. I was constantly pitching my products, my ideas everywhere that I was. having, at the heart of it, a big part of it is you do really need to know how to sell. yeah, so advocating for the problem, 100%. It's so, so important. Even if you do great work, if you don't have the right people bought in, your work is going to be really hard for it to actually matter for the right people. If it's going to be good for your career, that's going to be good for your product area. It's very, very difficult. **David DeRemer (22:58.828)** That's amazing. So now you're at Google and you're working with Flutter and Go and Genkit in the company where, know, the model you were following Tim Sneath helped put Flutter on the map and you've been working Flutter for a long time. Now that you're here, what's the big job? You're telling the story of Flutter and you're engaging with the customers of Flutter, right? Not necessarily... Developers as much as DevRel, but more the customers and the companies working with Flutter. Is that right? Absolutely. I mean, Flutter's DevRel team is legendary. mean, they're celebrities in my eyes, right? Working with them every day is just like, my god, this is crazy. But yes, you're absolutely right. Like, working with individual, like communicating to individual developers and building up that community at that level, I 100 % completely lean on the DevRel team there. I definitely help in terms of narratives and like, Helping with some of the admin, kind of like coordination stuff. But in terms of like that's not my primary job to be done. My primary job to be done is really focusing on our customers, focusing on an enterprise story, focusing on our customers when they scale. What are things that we can do in order for us to be able to really demonstrate how our customers have been able to scale with Flutter and then be able to share these best practices, be able to provide these guides, be able to even elevate up guides that folks like you at VGV or for example, Linkode or Insomnio, like all of these great companies that are been like pillars really in the ecosystem have been, you've been sharing a lot of best practices. So how can we find ways in which we can get the community's hands on them as well? Eric said, I'll use these words for me. He's like, you have a big mic, you better be using that mic. So like I'm really taking that to heart. Like I'm finding ways in which we can. **Abdallah Shaban (24:53.836)** like really use our big social presence, all of the channels that Google gives us in order for us to be able to like really tell that story of Flutter at scale. Yeah. And so needed and your role. I'm so happy that Google identified this and made the opportunity for you to come in and help in this area. Cause I think it is really critical to flutters next phase. When you think about telling that story, the flutter at scale story, the enterprise story, what do think the current story is for flutter? Like where are we in the life cycle? And what is the story that you find yourself telling people the most these days about flutter? Yeah, so I joined and I already had worked with a lot of enterprise customers, even at my time at Amplify, even when I had my startup. I knew Flutter is being used by enterprise customers. That was, to me, undeniable, because that was my job, is talking to all of these customers. I think there's definitely this constant fud that happens in the Flutter ecosystem about, well, is Google actually investing? So I really want to just completely shift this conversation because it's just not productive. It's not reality, clearly. mean, Flutter is actually being set up in such a good way for us to be able to really be at the forefront of this next generation of app builders and what these AI-powered experiences are going to be in the future. Flutter is really at the heart and core of it. So I really would just want to focus now the conversation on all the great wins of all of these customers that I'm establishing these relationships with. to really provide that as like really clear validation that I don't have to convince you, I just have to show you the evidence. So that's really like, that's I think I feel like a big part of where the community is right now is just like needing to have that reassurance and we're doing the best job that like we possibly can to just make sure that we're giving them all the evidence possible that Flutter is growing, our numbers show it, our adoption numbers show it. it's... **Abdallah Shaban (26:55.963)** We just need to be much louder about telling that story. Yeah, and get more people involved, like you're doing to tell their stories, because it's one thing for Google to say it or coming like BGV to say it's totally other thing when enterprise are out there participating and talking about it. Why do you think when you look at your own experience all the way from when you first found Flutter and we're working at AWS through to Google now, what do think it is about Flutter that's made it successful in the enterprise environment specifically? And why do you think maybe those successes haven't been as visible maybe as they could be? Yeah, so I think definitely the biggest thing that has made Flutter successful in an enterprise setting is that when you look at it on paper, the cost savings are ridiculous. Like the amount of cost savings that I hear from our enterprise customers telling us about how fast they're able to ship their products, how much they're able to just like not have to think about platform dependencies. The fact that they're able to really make their teams more cohesive and turn their engineers from platform engineers into product engineers, like how powerful that is for them. Like that in an enterprise setting is so powerful, like so, so, powerful. Like if I'm a decision maker, the first thing that I'm thinking of is how can I run my business unit in the most efficient way possible? How can I have the least amount of people to get the biggest amount of value? How can I have the least amount of dependencies in terms of my tech stack? **Abdallah Shaban (28:27.798)** And that's where Flutter really, really, really shines. And not only are you like getting like, okay, I'm building my, using the same code base I'm able to deploy to iOS, Android, web and desktop. But on top of that, the tooling is delightful to use. Like hot reload is, was revolutionary, right? Like in the space of mobile development. And then Dart is a phenomenal language to use. And it's actually a really easy language to upscale people on. So a lot of the enterprise customers that I talk to, a big question that I ask them is, did you have to rehire your teams? And they're like, no, we just gave them two weeks to go build POCs. And after that, they're like, yep, we get it. Yes, at scale, they start running into problems. I mean, they need to get deeper expertise, but in order for them to start adopting the technology and actually start building with it, it's very easy, very approachable. Yeah, I totally agree. That's definitely been our experience and tracks very, very well. You know, I Flutter has right now the things that people want, what companies are looking for that they're spending money on or that are investing in is two things in this current market. One cost savings and efficiencies. The other is AI. And I think actually Flutter does both of those things because there's the whole point of it is efficiency. And I think sometimes we're a little hesitant to talk about. You know, the cost efficiency of flutter, because it's, it sort of implies like a buy one, get one free or like maybe, maybe it means like, I'm going to reduce my engineering head count or something. But in our experience, that's not ever really what happens. what happens is that you actually just like with the resources you have, you're doing twice as much or more. and the other thing is that with AI. It's moving so fast. It's like changing so quickly. The things you can do with it are changing so quickly that you need to be extremely flexible and adaptable and fast with your ability to like get your experiences out the door. And you don't want the technology or the platform or how you've implemented it to be the thing that is the bottleneck. And so Flutter is super good at that. So I think it's like, like you were saying before, like right in the sweet spot of what the like AI empowered future could be. **Abdallah Shaban (30:39.054)** 100 % because it's like you need speed, but Dart's language, Dart as a language and Flutter's tooling as well, there's so many guardrails that help protect you as you're moving fast. That's what's really, really great. Like just to kind of give you an example, like one of the things that we've been doing a lot of is really trying to completely vibe code using, you know, any gravity or Gemini CLI and doing that. just with using Flutter apps and with building also some Dart apps as well. And the fact that you get your agents that can get access to the Linguist server and you can analyze and check the format of your code and do all of that with the tooling that Dart has, and the fact that you have the extremely sound type system, all of these things together provide you with a really good guardrails when you're building with agents. It makes it so much easier for you to be able to validate that your code actually is going to run the way that you expect it to. The edge cases of your code not running the way that you expect it to, it's like you just have significantly better tooling. So that's something that we've been really noticing is Flutter is so well positioned for you to be able to really in this age of you need to be able to move faster to really give you the right guardrails to help you keep moving faster and in the safest way possible. Yeah, we're in an environment now where we have a lot of choice, which is really cool, right? Like when you think about native, mean, native is like as long as you can coordinate things and you have the resources, it's like you can still make you make amazing things with native. You got react native, you got flutter. The cool thing about today is you actually have a lot of choices for what you want to pick in order to make these things. I would say, you know, 10 years ago, He kind of, if you wanted to make anything really good, seriously, you kind of had to do native. Like, you know, the other options weren't really good enough with their phone gap and stuff back in the day. Um, but now it was. Yeah. Right. And I mean, at the time, like you could still do, it was cool. The speed and the accessibility, but it didn't really have the performance. But now I think the average user, if you're using a native app or react native app or Flutter app, the average consumer is probably not able to tell the difference. know? Yep. Um, **Abdallah Shaban (32:39.768)** Fungap and Cordova user myself. **David DeRemer (32:58.092)** the developers might, you know, and the people who are paying close attention and people who have, you know, sort of turf wars around these things or, or, get sort of wholly about, you know, a technology or something. But when you think about that in your role and you, know, one of the things I assume you guys have to look at is like, what is our competitive set to, right? Like who else are we up against and what are the advantages we have over something or other? How do you, how do you see the current ecosystem of like different tools that are out there and flutters like unique positioning, or, sort of uniqueness relative to some of the other options? Yeah. I would say that the biggest, the biggest value that you get from using Flutter is you have a fully integrated stack all the way from how, like how your actual UI is being rendered on the canvas all the way down to all the language tooling. It all is one completely unified stack that is completely managed and supported by Google through all of our tooling as well. So like that, that is a huge benefit. So that means If you're going and you're building an app, a lot of what you need in order for you to be able to build your application is already a part of the framework. So that's a huge, huge benefit. The fact that we provide you with a design library out of the box as well that enables you to be able to iterate very, very quickly makes you breaking into Flutter significantly more approachable. That's also a huge competitive advantage. I would say also that the thing that I consistently hear is in comparison to all of the other tooling out there, if you are in the business of wanting to have truly custom designs that completely match what your brand's designs are, then Flutter really is best in class when it comes to giving you that experience. And I think that is the angle that we're really leaning into. If that is your desired outcome, then Flutter is the best choice out there right now. That's how I think about it. I like that a lot. mean, one of my phrases over the years, a little bit tongue in cheek has been like the problem with react native is the native part, right? Because, you know, one of the things people point to as a benefit is like, well, when there's a new version of iOS or Android, I can automatically get the new, you know, widgets, the new, the new UI elements, because it's native UI. But there's a cost to that because now if I'm using that and I'm, I have a lot of customers and there's somebody who's on a super old version of iOS or Android. **David DeRemer (35:21.44)** Now when I ship an update and it's using the native components, I gotta go back and I gotta check that and be like, how does that look? You know, does it still work? Does the UI still hold up with flutter? The cool thing is since the rendering engine shipping with the app, it's going to work and be beautiful and consistent in how you want it as a brand on all of those variants of the operating systems. It's not going to change based on the current UI that was, you know, in the, in the OS, on whatever version you're running. And I think when you're building that custom thing, that is massive in terms of control of your brand, but also just in terms of effort to test and validate that your experience is consistent across all those different variations. Yeah, I completely agree. I mean, in my time at AWS Amplify, I also was supporting React Native customers because I owned the JavaScript and TypeScript SDK. And I completely agree with you. That was consistently something that we had to figure out how do we handle for our customers because we also had this notion of these components that we give you that implement specific opinionated feature sets, like for example, authentication. or a data table. And it's sort of like a drag and drop, or like a, yeah, exactly, like a drag and drop solution that you can drop into your application. And then now you have like an authentication screen fully wired in. And for React Native, there were a lot of really difficult conversations about, how do we do this? Do we do this in such a way where we just rely on the native underlying components and then have it just like render natively? Or do we... basically opt out of that and then maybe use React Native Schema or do we do something else? There were a lot of conversations and decisions that we needed to make there that really slowed us down. What I really liked about Flutter is absolutely, you don't have to think about that. **David DeRemer (37:08.822)** Yeah, and I do think Flutter for the AI world is an incredible tool because it runs on so many platforms. It's truly multi-platform, which a lot of the other tools and solutions are not. If you get down the Swift, at least right now, the Swift, you're kind of locked into iOS or Apple sort of devices for the most part. And there's a lot of different ways you can build things. And I think sometimes in the AI world, are like, well, AI will write the code. The cost of writing code will be super cheap. maybe we'll just write all this code for all these different platforms, right? And it's like, well, yeah, but AI is non-deterministic. So somebody needs to babysit that and check everything and make sure it works. Do you still, do do you need, do you want that many babysitters across every platform? What if you just had one plot, like one code base, AI is helping you write and you check it and it works everywhere. That still seems like the better choice, even if AI can write all the different platforms pretty easily, you know, not to mention environmental costs and costs to run all those LLM. So. If you look at the space right now, Swift just recently announced a Swift for Android, believe. Then you have Kotlin Multiplatform. I think there was for a really long time, being in this space, was this notion of, no, native devs will never use anything cross-platform. The tooling that we have is because we want to get the best level of fidelity for the platforms that we support. cross-platform is this other thing that like, th that's just not as pure and as good as using native. But then I look at all of these tools and I'm like, all of these tools are starting move towards cross-platform. Like clearly this notion of building the same exact thing multiple times for each platform is just not efficient. It's just not efficient. Absolutely. If are there instances where you might need to actually do completely native things? doesn't make any sense. **Abdallah Shaban (39:00.408)** Perhaps, yes, there might be certain situations where that's absolutely something that you need. We cannot dismiss that. But in reality, the majority of apps could be built using cross-platform frameworks, and you would get excellent experiences with that. Yeah, I agree. And you know what, at the end of the day, we are very fortunate to be in this modern time where we have so much choice, so many great tools, so many new things coming out. Yeah, I agree. that's one of the things that I've actually been trying to make sure that I say it out more loudly in the community. So one of the things that I make sure I made sure to reread this. So Philip and Emily, original OG DevRel team for Flutter. When they left, one of the things that they did was they created a blog post that was about what would we want? to remain as part of the spirit of the Flutter team. And one of the things that they talk about is we shouldn't be shoving Flutter down people's throats. Like there are certain situations where Flutter is not the right choice and that's okay. And we should really focus on where it is that we actually are able to solve a problem. Where is it that we actually make sense for customers and developers and focus on that. And as a community, we should be open to the idea that there are choices out there and choice is good. because choice breeds innovation. Choice also brings inspiration. Like one of the things that we have done internally is like our iOS and Android engineering lead basically has set up an Android and Kotlin multi-platform and a Compose multi-platform training for the Flutter team so that they get the chance to experience what it's like to be a Compose developer, a Kotlin multi-platform developer, an Android developer. **Abdallah Shaban (40:54.572)** That's great. Like now we have so much that inspired us from there to be able to be like, okay, these are interesting ideas. These are interesting things that we can add to the Flutter frameworks. I think everybody wins when there's more choice. Yeah, I totally totally agree. Where do think this all goes? What's what's the what's the next five years look like you think? Yeah, mean, so Seth Ladd and I have been talking a lot about this, about where should Flutter go next. And I've really, really, really excited about GenUI. I'm really excited about what that means for the future of Flutter. I really think that Flutter is so well positioned to truly be the experience that powers any sort of ephemeral or generative UI experience or any dynamic UI experience in the future. on any surface. I'm really, really excited about that future. And I really think we're building the right foundational steps to get there. And we have a lot of internal support to really be able to realize that future. And it seems like headwinds are really moving in the right direction for us. So that piece I'm really, excited about. That's excellent. It's been very cool to see that and the Genui thing. I think we're just in the early days of what that is even means. Sorry, go ahead. **Abdallah Shaban (42:12.717)** Yeah, absolutely. **Abdallah Shaban (42:16.662)** yeah, and one of the things that was really interesting to me is, so we did a study about how would people use generative UI. And one of the things that we were concerned about is how is this going to come across with the community? Are folks going to feel like, this is going to replace my job because I actually don't need to build the UI that GenUI is generating. And all of the... All of the evidence that we're seeing is actually no, it just shifts a lot of the thinking that you have to do a lot earlier in the process of when you're actually wiring up and creating your GenUI experiences. Because now you actually need to think a lot more about what are all of the ways that somebody is going to be engaging with an agent that is then serving all of the generative UI experiences and really thinking deeply about all of the possible user journeys and then making sure that you have the right components that are all bright. brand compliant that can then potentially serve all of the different ways in which these experiences could be presented for your users. So it was very, very interesting. It's actually a lot more thought process. It just disrupts the order of this process. So I thought that was very fascinating. I actually did not think of it that way. So I don't think generative UI is going to be a thing that replaces engineers. I actually think it's just going to disrupt the way that we work and just have us think of different ways in which these generative UI experiences change our workflows. And one of the really interesting things is that it actually was a source of inspiration for people that were using it. It's like, I didn't realize that when you asked the AI this question, it wants to generate this type of UI. if I give it this additional component, it's going to make the way that it generated this specific screen. significantly more cohesive. So I'm going to go back and update my prompts, update my catalog. So yeah, very, very interesting. **David DeRemer (44:09.858)** Yeah, Yeah, it's an exciting new moment and I think this is the kind of stuff that kind of gets us back excited as people who are building these user interface type products where, you know, it changes the story of like, no, no, no, no, no, no, no, but just how we actually do it and execute is a little different, you know? And I think that actually creates a lot of opportunity for us to think about the what and the why something exists and how it should work for someone and kind of like let the LLM help with all the various edge cases and use cases and all the different things that you can do. One idea that someone said that I really liked was like, you know, when you build a product, a lot of times it'll be like an A-B test or something like that. And you tend to pick the things that the majority of people prefer. But if you run an A-B test and it's like, let's say 80 % of people prefer A and 20 % prefer B, well, you pick A, but that means 20 % of people picked B, you know? And you're not giving them what they want. And I think GenUI will give us more opportunity to serve so many more people and so many more unique needs, which is really, really exciting. Yeah, I totally agree. Totally agree. I want to change a tactics or direction a little bit. You're being that your role is on a product team as a product manager. When you think about how to bring together designers, engineers on the Flutter team, like the dev rel people and managers and leadership and stuff, what are some sort of things you've seen that are the hallmarks of like really high performing technology product teams? Like what are some of the rituals or **David DeRemer (45:56.716)** or philosophies or ways of interacting that people have that you've seen be really correlated with success. Yeah, I really think it starts with hiring. You really need to hire people that... that really thrive in collaborative environments. I would say that's like number one, like that's the most important thing. And the Flutter team is a honestly fantastic example of that. Like I joined, I have this really vague role that's very new. I am only successful if people pull me in and I start setting up meetings with people and I have my first leads meeting and they're like, you're here to help? Here's a hundred things we'd like you to do for us. And that was fantastic. Just to like immediately be like, no, let's pull you in closer. Let's work together. There's a lot of things we want to do. There's a lot of things that you can help us with. So like starting off with that, like, like having like that sense of collaboration, I would say that's one. what I have seen like throughout my career as well, what's like really good sign of like high performing teams is repeatable rhythms, like having a rhythm of an operational mode in which the team runs. Like for, for. Like for people that want to concentrate on building, not having to think about process because you live it day by day and it just happens around you and you're just a part of it is actually very energizing. And it's very counterintuitive to what I've worked a lot of engineering teams that were very, very hesitant to have process in place. And I'm like, I promise you the moment we put it in place, you don't have to think about it. I'll run, I'll run it around you so that it works for you. **Abdallah Shaban (47:40.794)** And I think really good high performing engineering teams have that. Like they have a rhythm that is very, very, very predictable. And they have a way in which you get inputs, something happens in the middle, and then there's good output that happens at the end. I would say that's like number two. And then the third thing is you have people that are able to like have the right channels to escalate when things are not going well. And like having the safety for people to feel that they can voice that, hey, like these are... things that are not working well, here's examples of them and have it be without blame. But like, what can we do to fix these problems and have like this continuous improvement mindset? Those are the three things I would say that to me have been like in places where I found a lot of success. I've often noticed that those three things exist. That's amazing. Love that advice. Everybody take notes on that stuff. Yeah. What's one lesson from your career you think will still hold true 10 years from now? So much change, all this AI stuff. What do you think is something you've learned that feels pretty evergreen? Oof, man, so much. I've been pretty lucky about having people come through my way that have given me a lot of good nuggets. One of them is, I'd say like the biggest one, honestly, is don't burn bridges. Like you never know. You never know when this person that you're working with is gonna be somebody that ends up being a decision maker somewhere that you're gonna need to collaborate with. Never burn bridges. Try your best never to do that. That's like number one, top of my list. The second one, which is very relatable is be dependable. Really be dependable. Like when you say you want to do something, do it. If you can't do it, say you can't do it. And that's okay. It's really important to be dependable because if people cannot depend on you, people will stop pulling you in into important conversations. People will stop pulling you in into important decisions that need to be made. So very, very important. The third one is be very curious. **Abdallah Shaban (49:40.878)** I'm very, very curious. And what I mean by that is one of the things that I do, for example, in my role as somebody that tries to really embody being a product manager and open source is I try to play with a package in the ecosystem every week and give faith back to the author, whether that is documentation, whether it is ergonomics about the developer experience, just Just like finding something that I've seen a lot of people talk about that I haven't used and then actually using it and really getting a feel for it, just so that I get a chance to learn something new. And that's just one example of what I do. Another thing that I do is just I consistently play with a lot of these AI tooling that comes out. I'm actually very much in love with anti-gravity right now. I'm actually building an MC that's going to help me co-host a hackathon that we're doing with the Firebase team. And I'm making it sound like an old timey wrestling announcer. And then I'm actually wanting to use GenUI so that I basically can have it pop up like the team's details and stuff. And I'm able to interact with it using Gemini Live API. But be curious and build with your own hands. There is not a better time to build than right now. It's phenomenal. The fact that A lot of these tools are kind of free and you're able to build with a lot of them. So being very, very curious in building, I think is just going to take you so far because you pick up so many skills along the way. Yeah, I would say those are the three things that are top of mind. I it. Amazing. I feel like we could keep going for hours and hours and hours. So many good insights and stories here. Maybe we'll wrap up with a fun question of like down the line, if one day you were to write a memoir or someone wrote an autobiography of you or something, what would its title and tagline be and why? **Abdallah Shaban (51:39.567)** Huh, what would it be? **Abdallah Shaban (51:52.408)** So a title and a tagline for my memoir. Just a title if that's easier, yeah. Yeah. Title for my memoir. Could be from today or could be 10 years, 20 years down the line. Yeah, a tale of random fantastic adventures. **David DeRemer (52:11.0)** love it. And why would you, why do you think that? Cause I'm very much somebody that loves going on adventures. think my startup was an adventure. think leaving Jordan, coming to the U S has been an adventure. Getting married has been an adventure. think like when I was at Amazon, I was traveling maybe once or twice a month. I was able to really, really see a lot of the U S see a lot of the world. And I'm going to continue to do that in my role here at Google. I'm, I'm adventures are if I don't get an adventure in once a month, once every other month. then I feel my soul starting to fade away. So and I try to keep like a very well-rounded life. I like to do a lot of creative things. I like to do a lot of like brain heavy things. So yeah, so I would say a lot of adventures. That's great. Adventure seems kind of like the key, right? Like what's, you know, what else, what are we doing here otherwise? You know, we're going around. You know, we gotta, we gotta get out there and learn some things. Be curious. That seems to be a definite theme in your career and the things that you do. And even in your work as as an outbound product manager, being curious about customer need. So thank you so much for doing this. This was great. You have a lot of great information here. And I think a lot of people in the Flutter community are going to be. Yeah. **David DeRemer (53:26.338)** Really happy to learn about you and to have you as part of this community. So thanks so much. Absolutely. Thank you so much, David. And I do want to say before we end this, that thank you so much for being just such a great, like such a great pillar and part of the Flutter community. mean, I've been working with you now for what, like four years at this point, even since I was at Amplify. And I've always found you to be somebody that's extremely approachable, somebody that is willing to help at whatever capacity that you can. And And I know this from personal experience that you're willing to help people that are even considered your competitors, because you think that the bigger that we can grow this ecosystem, the bigger we can grow this pie, the better it's going to be for everybody that's a part of this. So I just want to say thank you so much for, for really embodying, like having a growth mindset. And I just really, really appreciate working with you. Thanks so much for saying that. Likewise, so thank you. Really appreciate that. thanks again for being on. Absolutely. Okay, cool. ### Role of Design Systems in Software Development Source: https://verygood.ventures/podcasts/lucas-josefiak-widgetbook-design-systems/ In this episode of Build to Succeed, we sit down with Lucas Josefiak, founder of Widgetbook, to discuss how Flutter teams can improve design collaboration through scalable design systems and better developer tooling. Lucas shares how Widgetbook helps teams align on UI and design decisions, reduce friction between design and engineering, and “shift left” to catch UI bugs earlier in the development cycle. The conversation highlights the ROI of investing in tooling that speeds up iteration, improves quality, and enables more consistent, cohesive product experiences as teams and apps grow. {% audio_embed url="https://open.spotify.com/embed/episode/0HGzjDjISjUltputjATg7D?utm_source=generator" /%} ## Transcript **David DeRemer (00:00.046)** All right, Lucas Josefiak, thank you for joining us today. It's pleasure to have you on the podcast. Thanks for having me, David DeRemer. **David DeRemer (00:25.838)** Awesome. So I always like to start by asking what is something you like to do outside of work outside of your day job outside of technology that brings you back and helps you to be better with technology and on the job. So what I actually really like to do in my free time is to stay away from technology and to really immerse myself into nature. And also I'm doing sports. Ideally, I'd like to combine both of them. So basically, one thing that I really like to do is, for example, hiking up some peaks and summits. That's something I really enjoy. So you live in Germany, Correct. And you grew up in Germany and play soccer and hiking. Does that give you sort of like a competitive spirit that you think has kind of helped you in the startup world? Yes. **Lucas Josefiak (01:20.302)** Yes, definitely. I mean, in Germany, soccer is by far the number one sport and I grew up already as a small kid playing it. And if you are doing any sports competitively, you like to win and you like to get better. So it teaches you discipline. and you have to train a lot. also, especially in soccer as it's a team sport, it also teaches you that being a team player is quite important. It might help you if you yourself, you have a bad day, your team might be able to help out. But also if other team members don't have the best day, maybe you can shine and you can make your team win, even though your team members might not have the best day. So I really think that sports is something really, really helpful for everyone, especially as a kid learning. to be competitive and being disciplined definitely helps. wonderful. There are so many business books and people are constantly looking for advice. When you read LinkedIn, there's all this inspirational stuff about how to be better in your startup or in your business career or whatever. And one of the interesting trends I think is what really seems to help people is just actually not doing work sometimes, you know, getting out in nature, doing something else competitive that you can learn from and bring into it. I think that's cool. One thing you must be really good at headers because I know it's not obvious a lot of times from the virtual videos and stuff, but you're quite a tall guy. which I think is something that is surprising when people meet you in real life for the first time. that's always must've been a nice advantage. **Lucas Josefiak (02:59.82)** Yeah, that's true. mean, I'm so I'm nearly nearly two meters tall or six six. And yeah, that's, was quite an advantage growing up. And I mean, I've, I'm used to being tall pretty much my my entire life, but especially in the age about 15. I grew like 30 centimeters in six weeks. Whoa. And basically, after the age of 15. I stopped growing, fortunately, so I think I'm tall enough. But basically, I'm familiar or I'm used to being tall pretty much my entire life. 30 centimeters in six weeks talk about like the hockey stick growth curve. Yeah, it was quite tough. Yeah, tell us a little bit about who you are, your personal professional journey and how you got to be doing what you're doing today. **Lucas Josefiak (03:51.566)** Yeah, so I grew up always excited about exploring new stuff. I mean, we mentioned, hey, I like to do sports. I tried all sorts of sports. I really liked being in nature, tried lots of stuff there. And that also basically went through my entire childhood. So I was always very curious. But simultaneously, I'm also competitive, trying to always get to the edge and trying to get to the best extent possible. after high school, I wasn't really sure what I wanted to do. I always knew, hey, I would like to have quite independent life because I always wanted to be the person in charge of my own life and basically being able to decide what I want to do. And that's why entrepreneurship was something that struck to me quite early on. And I decided, hey, I want to study business, something that is going to be important anyways. But very quickly during the studies, noticed, okay, only the theoretical part. not everything. And I learned quickly, I don't want to only be the person selling stuff, but I also want to be the person that can in the end build products. So I taught myself how to code as well. And pretty much at the beginning of my studies, I had a great course at our university, which was a corporate entrepreneurship seminar. So a big enterprise came to the university and they brought 20 engineers to the university and they basically mixed our group with 20 people from that enterprise with 20 students. And we should solve a problem for them. And then in just two days, we came up with a solution for the problem, pitched it in front of board of directors. And they really liked our solution. And they also liked me personally. And then they were like, hey, Lucas Josefiak, why don't we incorporate that adventure and have you you join the team? And I was really excited about it. Back then I was 19, 20 years old. And yeah, I was immediately hooked. **Lucas Josefiak (06:14.862)** by it and was like, perfect. I mean, that's exactly what I want to do. I want to build products from there, from the ground up. In the end, unfortunately, that parent organization went into financial struggles. And then they decided, hey, no matter how successful we were, we're just going to spin the company in. So it's not going to be a spin-off, even though we were already profitable with what we were doing. But then I decided, okay, I'm 20 years old. I'm not going to work for an enterprise. So I'm going to basically pursue my own stuff. And then I started building various side projects. But at some point I met my today co-founder and CTO Jens. And Jens basically brought me to Flutter. So in that corporate startup, but also in other projects, I used to do web development. And on the web, I always did develop in And so it was mostly React. at some point I did some client work and we got a bigger client eventually. And it was already together with Jens, by the way. that client eventually needed not only an MVP, but like the real web app. then we were like, okay, we need to build something maintainable here. And then we stumbled upon the concept of Storybook.js. And that was quite exciting for us. We really liked it and the customer liked our work. And eventually that very same customer needed a mobile app as well. And we tried to React Native. We didn't like it that much, but Flutter was still super early. But Jens was like, hey, let's risk it. Jens, I don't actually know how he found out about it, but he stumbled upon some article probably was very early 2020. We tried Flutter and it worked well for what we wanted to do. Worked a lot better than React Native. And then we switched to Flutter and our customer liked it. And then we're like, okay, perfect. And we could learn something new here. So basically me always trying to explore new technologies was satisfied. The customer was satisfied. So perfect match. Yeah. And then that's how we basically emerged in the Flutter ecosystem. **Lucas Josefiak (08:40.654)** And then because we knew Storybook.js is a great tool in the JavaScript and web ecosystem. then eventually that client that already had a Storybook for the web with us was also now sure, the mobile app should not only be in the MVP stage. So basically they committed to the app for multiple years and then it was clear, Storybook solution would make a lot of sense here. And there wasn't a storybook solution for Flutter. So we were like, hey, let's build that ourselves. And when we decided to build it ourselves, we then decided to give a presentation at a local meetup and basically presented what we are doing. Now, we back then it was still like the storybook for Flutter that we are doing. And people really liked it. And we're like, came up to us after the presentation and were like, hey Lucas Josefiak, can you share it with us? And that was the moment where we then decided, okay, perfect. Let's do it. Let's open source it, make it available to other developers as well. And that was basically the foundation of Widget book. And as a side note, in Flutter, David DeRemer, you are very familiar with that. In Flutter, everything is a widget. And that's where we decided to then call it Widget book. That's that's awesome. Can you give us a quick for maybe for people who haven't encountered Widget book or storybook, the general sort of overview of the product and sort of like who would use it, why they would use it, and the benefit they get. **Lucas Josefiak (10:20.782)** Yeah, a Widget book or also a storybook, in the end, both are open source packages. So storybook is for the web versus Widget book is for Flutter. And the concept is that developers can build all their widgets or, in the beginning, their core components in isolation. And the great thing about building components in isolation is that you can build them without thinking about any outside dependencies. So usually if you would build a new widget, you would have your simulator fired up and you would build the widget directly in the screen. And the issue is if you build it into the screen, the screen itself already has some dependencies and that can be quite cumbersome to still build it reusably. And the benefit then of having a widget book is Widget Book is your new, basically like a preview environment for you. You can really focus on building just this widget without any outside dependencies. And then we have some concepts in Widget Book that are called knobs and add-ons that allow you to test the widget basically in every single possible configuration it could have. So you could test every different, or you could preview, first of all, every different state that the widget has. And then you can build it in a way that you can easily on the fly dynamically change all the widget parameters. For example, if you have a primary button, the primary button has, for example, required text property. And then you could configure it in a way that you can dynamically override this text element in the widget book preview environment. to test, example, how does a primary button look with a longer text input or with a shorter text input, but you could then also test it in other global properties. So, for example, on different device sizes, different text scale factors, different themes, different locales, and much more. So, in the end, it really allows you to build your components in isolation. And while you're building your components in isolation, all of those components **Lucas Josefiak (12:45.038)** are getting cataloged. And that's why Widget book is being used mostly by teams that want to build their own custom design system, because it allows you to, first of all, build the design system components, but to then also keep the overview of all these design system components, because in the end, what you receive is kind of your own Flutter design system. And if you then extend it even with entire pages, you can also catalog all your app-specific widgets in Widget book and that helps in the end your developer to build quicker and build maintainably. But it also helps all the other stakeholders like designers, QA testers, product managers, to really keep an overview of everything that is being developed. Nice. So to restate that and replay that a little bit, make sure we're all on the same page. you in Flutter, everything is a widget, right? So when write your code, like technically everything's a widget. But really, for our purpose here, we're talking about like user experience elements on the screen, a button, an input field, anything that you would want to display and have like a reusable component that you'd use throughout your application. Right. And you mentioned the word design system. And I think that this is another interesting word for us to like really define well and make sure that people are aware of. I think designers and sort of people at scale have this context of design system, but maybe a small independent development team or one or two developers maybe aren't thinking about this idea. And so how would you define a design system? What is its value? And maybe like, why should we build them even if it's just like a one or two person team? Yeah, so a design system is, in the end, one consistent common language that you establish for your entire product team. And a design system is not only a list of components that's quite important. It's basically a few different things. So usually, you start with your **Lucas Josefiak (14:48.59)** Usually you start defining your styles in your design system. Once you have your styles, with styles, mean stuff like your font that is being used, but then also, for example, your colors. So basically all your assets or from a design language speaking, all of your tokens. And then once you have your tokens defined, then you can think about your components. And once you have your components defined, and here usually the term that is being mostly used is the atomic design principle. you are starting with atoms, and atoms are basically the smallest unit possible of your components. in chemistry, where an atom is the smallest unit. in your app, your smallest unit of a component would then, for example, be a primary button. And then once you have your atoms, you basically compose multiple atoms together into a molecule. And the molecule could then be something like a cart, for example, so where you have multiple atoms. that then compose together to one molecule. And then you have organisms, which are multiple molecules. And then you get to entire pages. And that's usually how you structure your design system. Very cool. if I so Who is the primary as you mentioned designers and developers and QA people and stuff and so like people that might be familiar with all these tools that are out there there's like Figma and the whole Adobe suite of things which ironically those made it maybe were meant to be one thing at one point but they're not anymore. You have obviously your code and a developer might just be writing this in their in their software directly right. Doing these things and then you have QA and **David DeRemer (16:56.12)** concept of golden test and all that. So what's like the workflow, generally speaking around how a team uses widget book and where does it sit in the ecosystem of all these products? Yeah, perfect. We actually wrote an article about how you can build your own custom design system as a Flutter team. We can maybe link it in the show notes. But first of all, basically to go through that workflow, usually the design system process always starts with the designer. And the designer, I mean, you mentioned Figma already. Figma basically made it. I don't know, David DeRemer, if you still have customers with VGV that are not using Figma. I doubt it. Basically, everyone is using Figma right now. Usually, it starts with the designers who are creating the design system in Figma. Here, I mentioned they will start with tokens, but then also with their components. Then the next step would then be, we need to do with the design handoff. And here, Figma launched a great solution last year, which is called Figma Dev Mode. So finally, as developers, we also get a real seat in Figma now. And with the Dev Mode, the designers can mark certain sections as ready for development. And that's then how we as developers get... and notified it and then know, okay, perfect. That's exactly what we can implement right now. And once that is done, then it's on the developers to build the design system in Flutter. And first of all, what we would do then is we would create our style foundation. So I was talking about the Figma tokens earlier. So it would then be the job of the developers to basically translate those Figma design tokens into Flutter code. **Lucas Josefiak (18:55.04)** And here, I mean, we can go into a lot of detail on how you could do it. You could basically generate some Figma tokens. You could basically generate the theme extensions based on Figma tokens. If you're interested in that, just check out the article that we have. I think it would go a bit too far for this podcast episode. But then basically you would end up already with your own style foundation. And once you have the style foundation, You're in the perfect spot to then build your components or you build your component library then in Flutter. And that's something that you can do with Widget Book. And Widget Book here is an open source package, so it's free to use for Flutter teams. And you would then use it to build all of your components in isolation. And by building your components in isolation, you would then basically receive this catalog. of widgets. what is great about WidgetBook is that we built WidgetBook in a way that you can mirror the Figma naming to Flutter. So all the concepts that we introduced in WidgetBook basically mirror our concept in Figma. So just quickly naming it. So everyone that is already familiar with WidgetBook will quickly realize it. and the ones that are at least familiar with Figma, then know, hey, we have the equivalent, basically. So in Figma, you have a concept of variance where you, for example, have a primary button, but this primary button has a few different variants. For example, default, disabled, pressed. These would be three different states, and these would then be three different variants. And in WidgetBook, you could have different use cases for that. So that's the concept that we have. Then in Figma, you have something called variables, where in Flutter, you have theme data. In Figma, you have variable modes, whereas in Widget Book, you have add-ons. In Figma, you have component properties, whereas in Widget Book, you have knobs. And that really allows you, together with your entire team, to really speak the same language there to make sure that every stakeholder really gets quickly familiar with Widget Book and also really, feels at home there. **Lucas Josefiak (21:17.208)** So that is basically the core of how you would build your design system initially. Afterwards, we will probably talk about that later. But otherwise, we could also talk about that now is you would not only build, but you would then also try to maintain your design system long term. Because usually, as we all know as software developers, it's quite easy to build something initially from the ground up. but to then ensure that we can maintain it long term, that's usually quite a challenge. Right and that's where it gets complicated if you're embedding your UI in the code itself having these components all over the place now somebody comes and says hey we want to change the buttons from being rectangles to being rounded rectangles and you're like okay now I need to go find every place where I created a button and do that but if you have your design system well established you've got your figma the designers are making changes in figma that connects into your components in Widget book you can really easily do that and then when you make those changes boom they're spread throughout everywhere a button is used and that's how your design system works to play. So question that there's been a lot of talk over the, over the last year or so around the Flutter team's decision to remove Cupertino and Material from the core framework. And I think the idea that they're talking about is largely what you're getting into here, which is these types of tools like Widget book and building a design system is very important. If you're the type of company that wants to build your own design language for your application, where maybe you don't want to just mimic the material design spec or use just the default iOS design system that they provide. This is its own thing. So how do you feel about that decision that Flutter has done? how can companies really start to think about this idea of building your own design system and design language to drive your app and brand experience? **Lucas Josefiak (23:05.214)** Yeah, so I think, honestly, it's a great decision for multiple reasons. Reason number one is it really allows the Flutter team to focus with the team's resources that they have on the most important stuff. And If we're talking about the value proposition of Flutter, is really you build it once, and then you can ship it everywhere. And the value proposition of Flutter Therefore, you have the same user experience across every different platform. It's something that is very hard to achieve with other frameworks. And to my understanding, it's a great benefit of Flutter. That's why teams choose Flutter. And that's why we should really play to its strengths. So with Flutter, we should not try to make it basically as native as possible. on every various device, but we should basically say and be very confident about the app that we're building. Hey, we believe that's the best user experience that we want to offer our customers. And that's why we build our own design system that is custom. And no matter if our user is using iOS, if he's or she's using Android, if it's web, if it's desktop, basically whatever platform you can imagine. We always want to achieve the same user experience, the same outstanding user experience. And that's what you can do with Flutter really well. But if you want to do that, you have to build your own custom design system. If you would rely on material, for example, on Android, and you would then have to rely obviously on Cupertino on the iOS side, you would have two different user experiences. And that really, to my understanding, goes against the whole purpose of Flutter. **David DeRemer (25:04.684)** Yeah, I agree. I My joke about React Native is that the value they always claim about using the Native components or even with you know, KMP and all these things of like, no, no, the the design the presentation layer is using Native components. Like, well, the problem with that is the Native part, because actually, this is where you get a lot of variability, you get a lot of extra maintenance overhead. You also get things where, for instance, like, let's say we're using Native components in a cross platform framework. Well, now all of sudden I have to worry about all the different variations of the operating system going back as far as I would support in terms of like, maybe Android has changed their native design language over the last 10 versions of Android. If I'm supporting all of those, I'm going to have to worry about what does my app look like nine versions ago in Android because I'm using the Native components. If I'm using Flutter and my own design system, my own branded design ecosystem, and since Flutter ships its rendering engine with the app, it's going to look the same everywhere, regardless of what version I'm on. And I think that's an incredibly powerful strength that sometimes people point out like, if you don't use Native tools, you're not going to get the latest iOS design language. And I would argue like, yeah, that's a, that's a feature. Yeah, exactly. It's even go as far as it is not a feature. It is kind of the feature. Yes. Yeah. Totally agree, totally agree. So I think that's a really important concept. And I think that's one of the most like, know, liquid glass comes out and the whole React Native community is like, you know, you'll never get that with Flutter, Flutter, you know, and it's like, but you're missing the point. You're kind of comparing apples to oranges. And I think there are definite reasons why you'd use React Native and you have to be aware of the pros and cons of that. And there are definitely reasons why you'd use Flutter and you have to be aware of the pros and cons. And these things are not, you know, exactly comparable. They're quite different. **David DeRemer (26:56.302)** I'm curious, a widget book, right? When you think about these design systems and you're saying like build once run everywhere, ship it everywhere. I think that's an important concept because we also tend to default to like the idea of just mobile. Like I'm using Flutter or react native or any of these tools to build a mobile app. And I'm going to get the same app, you know, and some efficiencies and code, but we all know that Flutter can also run on all these other places from desktop and web to even in the dashboard of a car. or an embedded device in all these different places. Does Widget Book also help a team who's thinking about broader picture? I'm gonna take my Flutter app to all of these different places. Does it also scale with those? Yes, we do support those cases and basically how you can envision it. mean, all of you, could go to [demo.widgetbook.io](http://demo.widgetbook.io/) to see a demo of WidgetBook yourself. And then you would see, and we have a view pod add-on. And that view pod add-on basically allows you to preview your widgets across a variety of different devices. And the primary devices. that we support are Apple iOS devices in the end. We also try to support every Android device, but as you can imagine, David DeRemer, there are quite a lot of options. That's why we introduced the concept of basically allowing the user to add their own devices as well. But then you basically have the chance to test it on every device size that you like. So you could preview your widgets on whatever mobile device you like, could preview it on a desktop device, on a web device. So basically, we even have customers that are using WidgetBook for their smartwatches. So basically, any device type is supported. **David DeRemer (28:53.262)** That's amazing. And I think that really gets to the real power that I think we're still trying to educate the world about what Flutter is really capable of. And I think it's obviously there's a huge benefit by one get one free for iOS and Android. But when you think about the idea that I can build just components even like what you're describing design system. And now all of sudden when I want to go off and do something totally wild I want to build a smart thermostat. You know I can use the same components and see how they work and just build for something totally different on a Raspberry Pi or whatever it might be. So I think that's super cool. In terms of we I think the designers and developers and those types of people who are listening there, you know, probably really getting this pretend now we're talking to a business stakeholder, you know, the CEO of a company or chief digital officer or something like that. And someone's coming to them with a, you know, with widget book and saying, hey, we really need this. And they're going to start asking like, ooh, you know, what's the ROI, you know, what kind of improvements, what is the cost benefit analysis? I'm going to get out of this. What What do you, from a business perspective, what do teams get out of this besides the, the overall sort of improved developer experience? Yeah, so basically what is important to state here as well is so far we've talked about the open source widget book. So what you can do with that to build your design system. But then when it comes to maintaining the design system, it's sometimes quite tricky for teams. And especially what you mentioned earlier, David DeRemer, you basically said, hey, we might change components over time. then We, for example, changed the border radius of our button. And as developers, we might really know, on the homepage, I know it changed. On the profile page, I know it changed. But, I forgot also the checkout page was affected as an example. And that's why what we do as well, we offer Widgetbook Cloud. And Widgetbook Cloud is a visual regression testing platform. **Lucas Josefiak (30:58.162)** for Flutter Teams. So you can basically imagine it's like managed golden tests. golden tests allow you to take snapshots or screenshots of your app states. And you can then run a regression test to compare if a widget in the past basically changes over time. So you basically take the base build. You take a screenshot of the base build and then you get the head build and you take a screenshot of the head build and then you run these UI regression tests to then figure out if there was a change, yes or no. And what we do then with Widgetbook Cloud, you just use Widgetbook. You don't need to write any additional tests, so you don't have to write golden tests anymore. And we automatically generate these golden tests for you. And we then are integrated in your Git providers. We support any major Git providers, so like GitHub, for example. And then we are integrated in your pull request and we automatically run a GitHub check there. if your widget, if there was any visual change, we would basically have a pending check there for you. You could then... Select this check on GitHub, you would be navigated to Widgetbook Cloud and you would then see a list of all the visual changes that were affected by this one change that you made. So in our case with this primary button change where we changed the border radius, we would then see, first of all, just this component. If it would be included in some molecules, we would see the molecules. If we have entire pages that are affected by this change, we also would see all of these pages. And that is basically the other product that we set on top of the open source package. So I think that's important just to give some context for the RIS. David DeRemer, do you think that was clear or is there something that we should clarify further? **David DeRemer (33:09.88)** I think so if I understand so designers in figma, you know, they're doing some updates to the design, you're maintaining your widget book, and then a developer is making changes in the application and they try to push a build. And widget book will help us to catch like hey, a developer changed something in the settings or in the in the code, or the theme or something like that that has changed that now Widget book and my code are not in sync anymore. And it's going to actually demo that or show me that so that I can you know, figure out who is right and what changes do we need to make to get it back into the thing. So we're really kind of identifying sort of widget or component theme based UI bugs and helping to catch those that otherwise might shift to production. Yes, yes, exactly. Yeah, perfect. Do you have any numbers on like how much time you think this saves teams or anything like that? Yes. Yeah. So the great thing with Widgetbook is that we are already live now for, so with Widgetbook Cloud as well for well over a year and we managed to already grow to profitability with the team we are hiring soon again as well. So growing fast, fortunately, and the biggest benefit **Lucas Josefiak (34:24.418)** that teams have is that you are able to shift left. And shifting left is something that is currently quite important for a lot of enterprises. So basically what it means is that you're trying to test as early as possible in the software development lifecycle to catch bugs, basically as soon as they are introduced. And with WidgetBook, we really allow you to catch all of your visual bugs, as you called it, David DeRemer, already on a PR level. why the code is not merged or polished yet. So you catch them very early and therefore, yeah, you are making sure to waste basically as little time and resources as possible. And we published a few case studies already. One is with an enterprise from the Netherlands called Salto. It's one of the leading companies for identity management software. And what they noticed is they were able to prevent all visual bugs in their code, and they saved over 50 % of the development time. So basically every developer that basically works 40 hours a week now has 20 hours a week available to do something else. And because the developer does not need to be concerned anymore about, did I introduce any unintended changes to my code base? Hmm, how do I fix those? those changes now that the code is already polished. How do I make sure to get my team to review my code as well? So basically all of these questions are now automatically answered with WidgetBook. And for Salto, they learned they are saving 50 % of the development time. Significant. That's amazing. Wonderful. So I love that idea of shifting left. You want to catch those bugs. And traditionally, I think over these visual bugs have always been very difficult. Even golden tests are really hard because especially, you know, people who feel like, we're in a really fast cycle, we're constantly changing or startup or whatever. People kind of get away from those. And then the problem is that it's very easy for visual bugs to creep in. And it's really hard to find those because you actually have to kind of use the app. **David DeRemer (36:40.778)** and work through it. So I think that's an incredibly valuable tool to make sure that the designers, the developers, the QA teams are working in sync and anything you can do to save time is massively important. So sounds like you guys have really great integration. Exactly. And that's in the end, the second big benefit that you have. you can really make sure that your developers and your designers are aligned and that they have a great structure to then build a maintainable app long-term. So not only that you build a structure once, but that you maintain it. And there we have another customer that's testified already that also the designers have a lot of time savings. basically their developers and designers combined save over 20%. of their working time in their company. And that company gave a talk at FlutterCon Europe, where they basically presented their entire Widget book workflow. And what they mentioned is that in the past, they always had a code review where basically, so the developer made a change and then in the pull request, they then had a code review. So something that we all know, right? The issue was always, okay, who is now responsible for also making sure that it really aligns with the design. And the developer basically always kind of pushed the responsibility towards the QA testers. The QA testers were also not too happy with their responsibility and the designers, usually they're not included in the process at all. Or if they are included, they are included very late in the process. So they might get a test flight build every now and then, but usually they are not included at all. And for the designers... it's also not really motivating to not be included and not really knowing what happens to the design. So what they included is basically on the same level as a code review and now have design review as well in their pull requests. And there they have a required designer that always has to review the pull requests as well. And that's why with widget book, we included now the term of visual pull requests, kinda. So to really make sure that you have this full **Lucas Josefiak (38:50.188)** stakeholder alignment because with Widget Book, we also allow you to not only basically see, that's how the Flutter implementation looked in the past. That's how it looks now, but you can also compare it to the Figma design in Widget Book Cloud. So when you think about the fact that actually what we're trying to do here is create user experience, like actually create a design and a tool that you actually engage with and use the fact that this isn't already like a very established pattern of a visual pull request is like kind of shocking when you really get down to it and think about it. So kudos to you guys for coming up with some really amazing technology to help solve that. Given that you have this Widget book Cloud offering, you know, one of the advantages of having customers like when you have open source software, anyone can pull it and you kind of don't always know who's actually using it or what people are doing with it. Once you have customers, you have a little bit more insight into, you know, where people are, what are their needs, what types of companies are out there using it. What is that experience kind of taught you or have you observed about the current state of the Flutter community? Maybe like when our companies kind of coming to Widget book, is there a certain trend you're seeing around the size or type of companies engaging with with this? And fundamentally, what do you think that means for the Flutter ecosystem? Yeah, mean, first of all, it's super important that you get this data finally. mean, with VGV, are amazingly also doing a lot of great open source work. And that's why you also know, hey, that unfortunately, you don't always know who is using your open source package. And then you have all these developers and companies at conferences talking to you and are like, hey, Lucas Josefiak, we're using Widget book. It's so amazing. And you're like, okay, and who are you? I've never heard about you that you're using it because we just don't have the... usage data. But with Widgetbook Cloud, fortunately, we have the data. what we are seeing is that Flutter seems to be stronger in Europe than it seems to be in the US. We also have the same, or we see the same at conferences. I mean, David DeRemer, you also joined FlutterCon in the US and also in Europe. And you also see that the European community just seems to be bigger. But **Lucas Josefiak (41:01.75)** On the enterprise side, really feel like in Europe, more smaller companies are using Flutter versus the larger enterprises are in the US. So if I would basically have to differentiate my customer segments into basically, so we have self-service tiers on Widgetbook and Cloud, and then we have enterprise plans. And on the self-service side, most of our customers are from Europe versus on the enterprise side, most customers are from the US. And there we also have customers from Asia as well. So we basically don't have paying customers from Africa yet. I hope to change it eventually. But we basically have paying customers across all the different continents. And to your question, at what stage are companies usually integrating Widgetbook, I would say Mostly it's mid-project. that basically means, and here I'd also be curious about the experience that you have, but to our experience, mostly the teams that are using WidgetBook are starting with a POC of WidgetBook. So usually you just build, either you build an MVP or you build an internal app only where basically in case it fails, no one really cares that much. So it's just easier to... to get it started. And then you notice, it's amazing. Let's actually make it into a real product. And then the team already started and then they are not starting. So, I mean, we have some teams that are then also throwing away everything they had. So they threw away their spike and then they're starting kind of from scratch, but most teams are then already integrating Widget book into an existing app. Mostly, I have to say that that app is not, mean, product is not a final, right? But usually, It is not in a stage yet where the app is already super mature. So usually, WidgetBook is integrated in a state where you now basically have the validation that you can actually have a multi-year commitment to your app. And that's why now it really makes sense to have a design system for the app, as it makes sense to have a design system for your app. It makes sense to use WidgetBook for your app. And then you... **Lucas Josefiak (43:28.876)** Yeah, start using it for your design system. then, yeah, large enterprises then always also use it for their various apps. most, I would say the Widget book is the strongest in bigger companies where ideally you have one design system for multiple products. So you have like a multi-branded design system. And then you start with your design system, a repo and widget book. And then you also bring your app specific. repo to Widget Book, where you then also catalog your app specific widgets and entire screens to Widget Book as well. Very cool. Well, it's wonderful to see the growth and the success you guys are having and getting into more enterprise customers and things along those lines. You've been a very involved member of the Flutter community and sponsoring and showing up at all the events and being a great driver of the community. So I think for that, everyone in the community should thank you for your presence and your participation in that. think that really helps, you know, showing up at these events, adding that weight. And I think it starts to pay dividends in the long run as you start to see the growth and the opportunities. Actually one question I wanted to ask you around that is like you have been very involved in the community and, maybe for companies sitting on the sidelines thinking about like, should I get a booth at FlutterCon or, you know, should I, you know, actively participate in these communities, go to a meetup? What has been the impact of that involvement on your business? So I think for us, it's a bit special because we're building a developer tool. So basically every developer that is walking around at the conference could be a potential customer for us. So I think if you're listening and you're building a developer tool for Flutter, yeah, I mean, basically you have to join the conferences. So it's kind of the lead generation, especially on the enterprise side because... **Lucas Josefiak (45:20.238)** I if it's a self-service tier and you're charging a few hundred bucks a month, okay. mean, trust is not really an issue there. You can just risk it. And if it doesn't really work, yeah, okay. What did you waste like a few hundred bucks? But if you're talking about enterprise sales and you're talking about six figure contracts there, it's also really about building a relationship with a customer, making sure that you can trust each other. And in the end, it's a people business, right? So you really miss out if you're not meeting people in person and conferences are the best way of meeting people in person. So I would say if you're building a developer tool, you have to go to conferences. No excuse, basically. But also if you're not building a developer tool and you are either a developer or an executive of a Flutter team, I would also really encourage you to join conferences for multiple reasons. One really big reason is Flutter is so special because of its community. mean, all of us, know that Flutter is maintained by Google. And it's true, but you also have now even more contributions from non-Googlers than from Googlers. And Flutter is, I think, now the top three ranked repository on GitHub. Am I correct, David DeRemer? I think it's up there. ahead but it's up there it's definitely one of the biggest ones. **Lucas Josefiak (47:05.25)** Yeah, exactly. So there are so many contributions that you have from the community. at the conferences, you always meet other developers and many of them are quite influential. And you have the chance of talking to other open source package maintainers. You have the chance to basically share best practices with other teams and make a connection with them because very, very likely whatever issue you're facing right now, someone else faced it as well. And if you then have these connections to the other teams, you can just ask them and learn from each other. And that's something that's also like so easy to do at conference. And then obviously you can also go and join talks and basically learn from a presentation from people. But I really feel like that human connection at conferences is what makes a conference very special. And then I mean, very, very, very easy. RIS, if you're hiring right now, you have a booth and you can just talk to other developers that you can then just quickly hire is also a no-brainer. Yeah, I totally agree. And I think I love that sentiment. If you're building developer tooling and you can afford it, obviously you really should be here. And I think the more we have, think it also creates, it's like a flywheel, you know, it's like a snowball that rolls down. The more people that come, the more people who have a booth or give a talk, the more people come to the community and it just becomes this like self-driving engine at some point. And I think it is immensely valuable. And I think it's, the value is demonstrated by the fact that There are people who have been sponsoring and going to these FlutterCon events for many, many years now and are always there, always showing up. And if it wasn't good, people wouldn't keep doing it. So I think that's really important. The other thing I think is for anybody sitting on the sidelines, I think there's a feeling like sometimes you have to get some sort of lead or business out of this. And I think the way I like to think about it first and foremost is those relationships. There's a famous Harvard study. **David DeRemer (49:08.212)** about like factors that lead into like human happiness has been going on for a long time. And the number one thing is relationships, like good relationships. And I think that's really telling and more broadly, and not just to each of us as individuals, but even the the happiness or healthiness of a community, right, the more relationships people have, the more connections people have, the more willing to help people become and the more opportunities there are to increase that surface area for luck and make good things happen. I think coming in and just being like, you know what my objective is to meet people that I've only seen in a GitHub handle or or not as a package owner or on a recording or something along those lines. I think it's really, really important. So you've shared so much with us today and I want to kind of wrap this up a little bit. And I think one thing I'd like to know is you've been on this interesting journey. You started building things, you identified Storybook, you found Flutter, you thought it was really cool. You're like, oh, Flutter needs Storybook. We created Widget book, you went off, started your own company and have grown, now have enterprise customers. As you kind of like reflect back on that journey, what do you think is like a key lesson you've learned that you think you'll take with us, not just forward in your own business, but maybe over the next 10 years, you think it'll still hold true. So, I mean, there were lots of learnings. I would say the key thing really is whatever you are building has to be something that you're really passionate about. I mean, you were talking about happiness and how important relationships are. And that's especially true then also for the team that you're surrounded with all the time. I I have a beautiful girlfriend. at home and I love spending time with her. But as much as I love spending time with her in the end, I'm spending most of my time with the team in the office. Most of my time I'm spending with my co-founder Jens. And I mean, we're not married or something, but it's often feels like it. And it's really, really, really important that you are really only working together with people who love the problem that you're solving. **Lucas Josefiak (51:15.372)** I really can't stress it enough. So biggest kind of biggest learning for me. I would, I will definitely take it in all possible future ventures that I might end up building. Basically only work with people who really love what you're doing and who are not just looking for another job, because then you're really onto creating something super beautiful. I mean, all of us, we're spending most of our time. in the job and we should really make sure that we spend all our time well. Yeah, I think that's really vital. You know, I think, obviously, we need money to survive and to grow and make our give our families better opportunities. We need ways to manage stress, you know, we need a bunch of things that matter. But I think you are hitting it right on the head. And I think it's something that we you can see time and time again that when people really care when there's passion, when there's commitment to each other and good, healthy relationships, those other things tend to come. If you prioritize, I'm going to jump to this job just for the money and but it's like a toxic environment. It's not enjoyable working no one's passionate about it. In fact, I think that's there's an inverse correlation. I've noticed in my career that companies willing to pay the most. The reason they often do that is because it's the it's not a great environment and they need to offer some counterbalance to that to make it work. You know, whether it's this whole nonsense going on in Silicon Valley of what is it like the you whatever the numbers they have now for just like busy working all the time, you know, and having no personal life or anything like that. So, you know, each person needs to make that balance. But what I've found is that passion, you're right, like the commitment to it. At the end of the day, that tends to yield the better results and even get to those same outcomes, but maybe be a different path. **Lucas Josefiak (53:02.196)** what makes it fun, right? in the end, we only have one life. Statistically, we're just spending most of it at work. So let's work on something fun. So if somebody based on your journey, maybe there's been something along the way that you really drew from or learned from a book, a mentor or movie or something like that. What's something someone you would recommend someone go out and pick up and read and learn from in order to help them down their journey? So I would say most influential book on my career and probably therefore also my personal life was Aria Greece, The Lean Startup. I think it's very, very classic read. So basically everyone that aspires to build a startup someday should really read it. And especially for software engineers that like to basically make things perfect and basically building. all the time. And then I like, okay, I just have to ship it. I just have to press the button. And yeah, the revenue is going to come. I'm telling you, nope, that's not happening. So it's build, measure, learn. And yeah, that's what Eric Ries teaches you really well. Yeah, I totally you know, one of the things I've it's like building it is actually the easy part. So many people think about like, oh, building is the hard part. If I can just find a way to get this done. And know, especially if you're non-technical, you're like, oh, I got a technical co-founder, somebody will build this for me. All my dreams will come true. And in reality, like, no building it is like actually the easy part. The hard part is making something people want and getting people to be aware of it and use it and continue to want to use it. That's the hard part. So that's a good one for sure. **David DeRemer (54:40.268)** Well, thank you, Lucas Josefiak, so much for joining us, sharing your journey, telling us all about Widget book and helping to educate us on design systems. If people want to find out more about you and look into Widget book, where can they learn more? Basically every social media channel I'm on there. So I would say Flutter community is most active on X and LinkedIn. So feel free to hit me up there. Otherwise, [widgetbook.io](http://widgetbook.io/) is our website. They'll also find every way to contact me. and they'll also see you at tons of Flutter events. Exactly. Join us. And I mean, probably same for you, David DeRemer. In case you are not so sure if the ticket is worth it, we are sponsoring basically every major Flutter event. I think David DeRemer is doing the same. And if you need a discount code, hit us up. 100 % well said. Thank you so much Lucas Josefiak. We'll see you soon. **Lucas Josefiak (55:40.91)** See you. ### Streamlining Full-Stack Dart for Faster Teams Source: https://verygood.ventures/podcasts/viktor-lidholt-serverpod----streamlining-full-stack-dart-for-faster-engineering-teams/ In this episode, we sit down with Viktor Lidholt, Founder of Serverpod and former Flutter engineer at Google, to explore how full-stack Dart is reshaping modern app development. Drawing on his experience as a startup founder, CTO, and core contributor to Flutter, Viktor shares how Serverpod simplifies backend development for Flutter teams—without sacrificing scalability or control. We discuss Viktor’s journey from game development to building developer platforms, the evolution of Flutter’s ecosystem, and why breaking Material and Cupertino out of the core framework unlocks more innovation for the community. {% audio_embed url="https://player.cohostpodcasting.com/daef20a9-f93e-449c-8df3-f91e17e8a5c5/d54de575-9680-4a0e-b2d5-56d08497c028" /%} ## Transcript **David DeRemer:** Hi, welcome to Build to Succeed from Very Good Ventures. Today we meet with Viktor Lidholt, Founder of Serverpod, an open source scalable app server written in Dart for the Flutter community. In this episode, Viktor shares how his game industry roots and experience on the Flutter team at Google led him to start building a backend framework optimised for Dart and specifically Flutter. He explores product leadership, ecosystem insights, and how his team is shaping the next wave of developer tooling. I hope you enjoy this episode. Welcome, Viktor. **Viktor Lidholt:** Thank you. **David DeRemer:** Great to have you. Let's start our conversation today with something fun. What's something that you do outside of work that energizes you or influences you in what you do every day at work? **Viktor Lidholt:** Yeah. So, I do a lot of CrossFit. So, I usually take a break at 11:00, is my class. Head down to the gym. I have the best of setups. There's a gym in the basement of the building where we have our Serverpod office. So, I go, that takes me a minute, do my class, eat lunch, and then I'm energized for the rest of the day. **David DeRemer:** That's amazing. Yeah. So, the question is, how many pull-ups can you do and do you kip or no kip? **Viktor Lidholt:** If I do kipping, I've done 130. **David DeRemer:** Whoa, that's a lot of pull-ups. **Viktor Lidholt:** Yeah. Not in a row. Not in a row. **David DeRemer:** Oh, wow. **Viktor Lidholt:** During an hour. **David DeRemer:** No, that's impressive. CrossFit's no joke, so good on you to bring that in. Do you feel like that helps to focus you or how does it help clarify your mind? **Viktor Lidholt:** I think it definitely centers me. Having that little break in the day, I think is really helpful. You get a bit of a perspective on whatever you're working on. And running a startup, I'm sure you know or running a company can be pretty stressful at times. Just having that break when you're lifting weights, and jumping, and doing burpees, there's no way to think about work. So, you get a complete break from that. So, I really think that's a good thing. It's like not just the exercise, but it's a bit of a mindfulness thing for me. **David DeRemer:** Right. I mean, I did CrossFit a long time ago. Way out of it at this point. But in terms of exercising, like an activity you can do, I feel like it's actually a good simulation to some extent of building a startup team, because when you're with a group, everyone's together. You're all supporting it, but you're still testing yourself because there's a lot of things that you're doing alone. And so, it's interesting. I've always thought it was an interesting one where it's really focused on you specifically, but in the context of the broader group. And everyone's trying to encourage each other to get their own personal best and stuff, which is kind of cool. I also know you've been travelling a lot recently. Obviously, I saw you in Berlin this year for Fluttercon. And I know you travel the world somewhat for various Flutter community events. Have you been to anywhere interesting recently? **Viktor Lidholt:** Yeah. I got home this morning. If I look a little bit tired, it's because I slept on an airplane. But I spent the past five days in Nigeria, Lagos, visiting the FlutterBytes Conference there, which was awesome. I think it's a great community. It was I think 600 people at the conference, so Africa's biggest Flutter conference. And great vibe for the conference. And I think probably among the friendliest people I've hanged out with at the conference, so that was really awesome. **David DeRemer:** That's wonderful. 600, that's a big crowd for what we're doing here in the Flutter ecosystem. So, you got Fluttercon EU, the US version. There's FlutterBytes. I know you've got Flutter & Friends. I think Fluttercon's doing one in India this year. I know there's some Flutter conferences in Japan these days. Have you been hitting as many of these as you can or what's your strategy for attending these events? **Viktor Lidholt: **Yeah, all of them. There was also one a couple of weeks ago in Spain, Málaga. It was a little bit of a smaller event. I think it was like 220 people or so, but still, it was a great event. Also, nice place to visit, especially if you're a Swede this time of year. It's really gloomy here. So, getting away, getting a bit of sun. It's always a good energy boost going to the conferences, I think, and getting a feel for how excited everyone are about the whole Flutter community, and Flutter, and building stuff. **David DeRemer:** Yeah. It's just been great over the last couple of years post-COVID to see the resurgence of all these events and stuff, because obviously for a while there, all the meetups, all the conferences, everything was shut down or virtual. And I think you're seeing the people in real life, and seeing the enthusiasm. And making those personal connections is absolutely critical. **Viktor Lidholt:** Yeah, I think so too. I mean, it's a great way for us to get more people to see what we're doing with Serverpod, but also building your network. That's something I am a very strong believer in. Basically, connecting people. I always try to help out, even if there's not a good fit with say Serverpod or what we do. They can often be like, if you can find two people you can connect, that's always a win. **David DeRemer:** I totally agree. I sometimes think about what would I like to do in the future if I retire or something like that. And I think there's a lot of enthusiasm and excitement over just if you connect two people. And then you find out a year later that something amazing happened because you made that connection. I love those stories. But I want to get into the community stuff, because I know it's been a really important part of your efforts and your strategy to grow Serverpod, but let's maybe take a step back. And can you give us your story from beginning to where you are now, culminating maybe in what is Serverpod and why does it matter to the tech community? **Viktor Lidholt:** Well, it's a long story, but let's do the shorter version of it. But I started programming when I was probably 12. Learned programming on Macintosh Plus. It was almost the original Macintosh, the black and white, two-color computer. And when I was a kid, grew up, I wanted to do computer games. And then I worked in the game industry for many years. I was a lead developer for one of Sweden's biggest game companies, and there I built a tool for building mobile games. And it got picked up by Zynga and another capital of the world starters game companies. So, Zynga bought it from me. And we made it open source and I moved to San Francisco to work for them. So, that was an exciting time. Then I got an offer from a startup in San Francisco, because all the clients used my tools to build the games. So, they gave me a team of 10 developers. And I worked there for two years and it was a fun time. They used my tools to teach game development at MIT, UC Berkeley, Carnegie Mellon. I got to go to all those places, give lectures, and travel to conferences all over. Unfortunately, they ran out of money, a start up sometimes, too, and then they sold me to Google, where I ended up on the Flutter team. So, I worked at the Flutter team for... it was about a year. That was very early on, it was before the release of the first version or public 1.0. It was all up on GitHub, all of the code, but I don't think anyone was using it at the time. So, I moved back to Stockholm, had another startup. I built a news app. And while building that, obviously I used Flutter, but I really missed having a good backend for it. So, when that failed, I felt like I wanted to give startups a second chance, do everything right this time. And something I had really been missing was having a good backend in Dart. So, that's really where the idea for Serverpod was born. **David DeRemer:** So, you got exposed to Flutter and Dart as a result of that acqui-hire into Google. You got placed on the Flutter team. Did you have any agency in that decision to be placed on the Flutter team or did Google just put you there and it ends up being a serendipitous thing? **Viktor Lidholt:** So, the startup I worked for, they tried to sell the whole company. We were maybe 100 people, 80 developers or something like that. So, they shopped us around at Google, Twitter, Dropbox. We did interviews at all these places. And then they didn't manage to sell the whole company, but Google bought the top eight developers and then they sold a bunch of other developers to a gaming company. It's crazy that it works like that in Silicon Valley, but it was the reality of it. But I was in the US for a work visa and H1B. And if you don't have a job, basically you have a week to pack your bag and leave the country. So, I got more or less an ultimatum. You sign for Google tomorrow or you have a new job tomorrow. So, it was an easy choice, also not the bad place to end up, right? **David DeRemer:** Sure. Yeah. But it's cool that you found Flutter and Google, like Flutter and Dark this way. And then here you are building backends with Dart. **Viktor Lidholt:** Yeah. I feel like it was a pretty good match for me, because my background in doing a lot of low-level computer graphics, and game engines, and all that stuff. So, I had pretty good understanding, I would say, about mobile. And I thought Flutter was really a project that was exciting to me to work on, but I was only a year at Google. **David DeRemer:** Well, it's cool. I mean, I think a lot of people probably don't know this about you, that you were so early on the Flutter team and then that's where the... Because I mean, obviously we see you in the community for Serverpod and we know you've been around, but it's been a long time now. So, it's cool to get that context, like your enthusiasm. And that connection to gaming is really cool, because I've heard people say Flutter's kind of like a 2D game engine for user interfaces. So, that's cool to see that story art connecting all the dots to get to where you are today. **Viktor Lidholt:** Yeah. No. I had a great experience at Google. So, it was a very cool company and it's cool to see from the inside. I didn't realize before I joined how many things they do and how many cool things they do. You see the search engine, now I guess you see the AI, but there're loads of projects and products that may be released in five or maybe even 10 years. It's not so many companies that can build products on that time scale, like really big things. **David DeRemer:** Yeah. It is impressive. Obviously, Google is so massive and they do so many things. And it's very easy to find fault with a lot of their decisions and how they do things, because everything they do, good or bad, is at a big scale. But I think you're right, that they're making investments and bets. Even the AI stuff I think is largely... the transformer pattern is because of Google researchers figured that out. And all the tensor work they were doing over the years and all those sorts of things. Although, one time I went to a Firebase summit and an engineer was like, "Yeah, all this machine learning stuff exists so that YouTube could figure out how to serve videos of cats to people that like watching videos of cats more effectively." And it's like, oh, that's where all this AI stuff came from, is YouTube recommended videos, pretty much, but it's kind of fascinating.  So, you mentioned that you were missing this backend for Dart. And when you were building your first startup and building an app and all that, what was the insight or sequence of steps that got you to feel like, you know what, this is a piece that's really missing from this ecosystem and I'm going to solve it? **Viktor Lidholt:** Right. I started writing our backend in Python, because we used a lot of... It was before generative AI was a thing, but there was still a lot of text analysis tools available in Python that was definitely the easiest to use, and set up and worked the best. So, I used them and then I figured like, yeah, I'll just do everything in Python. And I hated it so much. One day I just ripped out the core methods or features I needed in Python, made it into a tiny little server, and then I rewrote everything in Dart, but it wasn't like a framework. It was just like a raw server. So, that's quite a lot of more work to build your APIs that way, so that made me think someone should really do this. And so, we have a great backend solution, a little bit like what Flutter is to Dart on the front end, something like server is for Dart on the backend. And it felt like it really was a missing piece in the ecosystem. I mean, there was Dart Shelf, but it didn't feel sufficient, at least for what I needed. **David DeRemer:** Yeah. And a lot of people in the Flutter community would default to Firebase and things like that, but they never really built a Dart, first party Dart SDK for Firebase, which I think a lot of people always were clamoring for that and saying, "Hey, if you did this, we could just be writing Dart on both sides." So, I think that missed opportunity creates opportunity for others to come in and innovate. **Viktor Lidholt:** Yeah. I mean, I started looking at Firebase when I built our app there, but the things we were doing had to do a lot of server side stuff, scraping websites. And then also, the Firestore document database structure wasn't really a great match for what we were building, so it wasn't really a solution. I'm sure Firebase works great for some type of apps, but it's not like a sort of fits everything. **David DeRemer:** So, with Serverpod, obviously one of the advantages that you can write full stack Dart, you can write Dart for Flutter, your front end code with Flutter, and you can write Dart for your Serverpod code in the backend. And that allows for some code reuse and sharing across that stack, which would be awesome. What are the architectural or product features of Serverpod that are differentiated or specific to Serverpod, that maybe people don't know about or would want to learn about? **Viktor Lidholt:** Yeah. So, we put a lot of time, and effort, and thinking into making Serverpod a really smooth experience to work with. So, something that we built in Serverpod that is kind of unique is that you just add your methods to the server. We'll analyze your server code and replicate the same methods in your client, and then we generate a whole API in between. So, you don't really have to worry about the API. Under the hood, it's like a well-formed dressed API, so there's no real magic. It just feels like magic. So, it's a much more seamless experience. You have to add methods on the server, and you call them from your front end, and it just works. So, that is a pretty sweet experience. And also, you need to put your methods in an endpoint methods. We solve a lot of issues that otherwise can be hard with things like Firebase. When you have to version your API, say for mobile apps, you can have old clients that need to be able to connect to your server. In Serverpod now, you can just inherit or from an endpoint to create a new copy of that endpoint, and add new stuff to it, and you still have the old one there. So, that's a pretty cool feature that we've added I think that people may not know. It's pretty new, so we're going to push it more into like the next version of Serverpod. But it's really a super feature, because it will just save you a ton of work. **David DeRemer:** Right. Yeah, that's awesome. In terms of integrating with databases or other third party systems, do you just have full flexibility in that regard? Or what's the scope of what Serverpod can do for someone? **Viktor Lidholt:** Yeah. I mean, you have full flexibility in terms of you can use any Dart packages to connect to external services or you can use other REST APIs. Nowadays, if there's something missing in Dart, it's pretty easily generated for Dart with generative AI. But we do have really, really great support for Postgres. So, we built a complete ORM. We did all the hard parts, database migrations. That works great. We have support for relations, joints. Many ORMs are... you start making the easy parts where you can basically take a row and convert it to an object. And then you get to the harder parts, building queries that are type-safe and stuff like that. And maybe you fail, but we spent a year figuring out how to do it, do it right. So, that's actually a really cool feature set of Serverpod as well. **David DeRemer:** That's awesome. **Viktor Lidholt:** And you get like all the auto complete in your editor and all that stuff, since it's all type-safe, which is really nice. **David DeRemer:** Got you. Nice. And can you tell me a little bit about the deployment and how Serverpod...? If I'm a new customer to you and I'm evaluating this, is this a thing that you guys run, and I have to contract with your infrastructure, and run everything with you or is it portable to different cloud providers? Or how does it work in that ecosystem? **Viktor Lidholt:** Yeah. So, Serverpod is all open source, so you can run it any way you want. You can run it on Google Cloud. It's pretty easy to set up on... Is it called Cloud Run, for instance? **David DeRemer:** Mm-hmm. **Viktor Lidholt:** You can run it on AWS, so you can run it on your computer in your basement if you want that, or you can run it on Serverpod Cloud and then it's even easier. It's really easy configuration. So, you have your Serverpod, you just type S cloud deploy and we set up a full infrastructure with load balancers, database, domain names, all that stuff. So, it's a little bit like a magic- **David DeRemer:** I love it. **Viktor Lidholt:** ... turnkey solution for deployments of your Serverpod. And so, we'll be able to support any Dart servers as well. It's really no difference. It's just we started making it really good for Serverpod and then we'll go from there. But that's something we've been working on for the past two years. And it's in a private beta now. But if you join our Discord, we can hook it up if you want to try it out, basically. **David DeRemer:** Right on. I like how you positioned it as like Flutter is the framework for Dart on the front end and Serverpod is the framework for Dart on the backend.  Would you compare it? I mean, I'm trying to make sure that anyone who might be listening to this can wrap it where they... how to think about what Serverpod is. Would you compare it to like a Node.js or like a Ruby on Rails type thing? Or what do you think would be the closest comparison to some other ecosystem or is it totally different, you think? **Viktor Lidholt:** Yeah. No. It's probably pretty similar to Node.js, in that sense that you have Node.js and then you have React. **David DeRemer:** Got you. Perfect. **Viktor Lidholt:** But no one has really done it for Dart. I know you have Dart Frog obviously, but the scope is smaller, which is great for some projects. I think it's sort of a different fit for different projects, what you want to go with there. If you're doing something small, if you're doing a bigger app with like... For instance, a really killer feature that we have in Serverpod is streaming. So, in your server, you can just make a method that returns to stream, and then you can listen to that stream from your Flutter app, and that works. You can have 100 streams in your Flutter app that connects to your server, pipe it through a single web socket, and routes all the messages to the right methods on the server, keeps track of the life cycle of the connection on the client, all those things that are otherwise quite a lot of work to set up and get right. And there are a lot of small little things that you... pitfalls you can fall into and not get it right if you write it. So, we really did all the hard parts there so our users can just do the fun things. **David DeRemer:** Yeah, that's great. I think it's wonderful that we have options all the way from Shelf, things like Dart Frog and Serverpod. There are different levels of complexity and work that's done to make it more effective for a developer to pick where they want to go in that ecosystem.  I know you're working on something new, too, that you've code named Relic. Can you tell us a little bit about maybe some of the upcoming things you guys are working on within Serverpod, what we could look forward to? **Viktor Lidholt:** Yeah. We have a lot of things coming up. So, Relic is probably one that is closest to be done. It's going to be released in a 1.0 version probably in the next few weeks. We're basically finalizing the documentation and making that really nice. That's something that's been lacking a little bit for Shelf—hasn't been super great documentation on how to find good examples. So, we're putting together both a suite of examples, but also really good documentation.  As we see it, we started with Shelf and then we have just fixed everything that we thought were a little bit awkward or not that great in Shelf. Shelf was written quite a long time ago when Dart looked different, so Shelf has a great support for type-safety. There are lots of dynamic types that you pass into methods, which is not good practice anymore. For efficiency and for all sorts of reasons, you want to keep things as typed as possible. When you pass HTTP headers, they're all typed now. So, instead of passing a string with a date that you need to know how to format, you'll pass the daytime, or the same with cookies, or any of these other things, and they just work. And you can add your own extension methods if you have custom headers even. And then we looked at the inside. There're like a ton of updates, many smaller ones. And then we made the interface look very similar to Shelf, so you can use that, but we also added a bunch of shorthands that makes it easier to work with. **David DeRemer:** That's amazing. **Viktor Lidholt:** So, that's an exciting release coming up. And we're also working on Serverpod 3 that is close to being done. And we have migrated Serverpod 3 to work on top of Relic, so that's the reason we really rewrote Shelf. We felt like it didn't really fit the bill for what we wanted to do with Serverpod, so we made sure to make it really, really good. And then we are building Serverpod on top of that. And that gives Serverpod things like middleware support also and a really good web server. **David DeRemer:** That's wonderful. I think it's also a great example of the power of open source software communities, because you were using Shelf, which helped you to get started. You learned a lot along the way building Serverpod. Started thinking, "Oh, how can we make this better?" And then realize like, "Well, actually, we can just make a lot of improvements," but then also give that to the community as part of what you're doing. So, thank you for doing that, tackling those problems, and helping Dart and the overall community of people building with these tools, giving them more options and more opportunities.  I wanted to ask you a little bit how you guys have been growing, too, so your customer base over the years as Serverpod has grown, the Flutter and Dart community has grown. How has your customer base changed over the years? Or I guess, what is it showing you about the Flutter community in terms of the customers you're starting to see use Serverpod? **Viktor Lidholt:** Yeah. So, unfortunately, with Serverpod, we can't really measure super well on the DT. We don't really track our users in that respect. I mean, we can see how many downloads or when someone is using the CLI tool. So, we can see that there's thousands of apps being built, but we don't know what these apps are or anything like that. But obviously, we know the people we speak with. And that's something that's been really cool at these conferences, because for every conference we've been to, there's a larger share of people we meet there that are using Serverpod for the projects and some pretty big ones, too. And a lot of app agencies that they're building with Flutter has starting using Serverpod, because it gives them... Basically, they can build the products faster, is probably the main point. But they can also quite easily reuse code between projects, which is huge for some app agents, if they build similar typed apps. So, I think we've seen over the past year, like a year ago, it was more hobby developers we met that used Serverpod. Now it's more app agencies or pretty serious funded startups. So, that's definitely a change that is very exciting to see for us. **David DeRemer:** Obviously, you can't track all the implementations of it since it is open source, but I think you have an interesting pulse on the growth of the Dart ecosystem from a different angle, I think, from other companies in the ecosystem that might be providing a service to a Flutter app that's already been built or chosen. The server decision is a very different decision, and I think that's pretty cool. And I know you've also said one of your goals is to get people into Dart and Flutter as a result of Serverpod, not the other way around, right? **Viktor Lidholt:** Yeah, right. Now people choose us, because they use Flutter. But in the future, people will choose Flutter because they love Serverpod. **David DeRemer:** I love that. If you were to talk to somebody who's considering, “how could I build my server side logic for my app,” what is the most powerful or maybe underrated technical advantage of Serverpod that you think a CTO or somebody sitting down might not know about or should really deeply consider as to why to go after Serverpod, as opposed to all the other options that are out there? **Viktor Lidholt:** Well, I would say there are many reasons, but a few that comes to mind that I've gotten as feedback from some of our users is that sometimes you do things in your Flutter app that you realize should really be on the server side. It can be different form of validations of data and stuff like that. And if you have a separate team that writes in a different language and they need to replicate those, that usually may not happen. But if you use Flutter or Serverpod, you can just copy-paste that code and place it in the right spot or share it between the app and the server. So, that's a pretty cool thing, having everything in the same language. Also, when you're hiring, it's so much easier for Flutter developer—if they need to do some small change on the backend—if it's using the same language, the same project, everything else fits together like one unit, it's much, much easier for them to go to fix that little thing that they made themselves, rather than talking to the backend engineer or even maybe project manager in between. So, that's also something for efficiency. And how you can, as a company, recruit people that know Flutter and Dark, is much easier than finding full slack developers to know say go and Flutter or JavaScript the Flutter, or the perfect combination of the two. So, for startups or app agencies, that's a huge advantage, also when it comes to recruiting and time to build stuff. **David DeRemer:** And it's also been something that I think the JavaScript community has held over the heads of the Dart and Flutter community, of like, well, it's full stack. We've got Node.js and JavaScript, TypeScript on the backend. You've got React, you've got React Native. And for a lot of years, React and React Native was just kind of like clever branding. They actually weren't that... just because you built something in React, it didn't mean you could just put it on mobile with React Native and vice versa. I know there's been some work to try to help with that, but Dart in the early days didn't really have that full stack story. So, now thanks to you and the efforts of Shelf, and Dart Frog, and all these other things, we have more full stack options. And I think that Dart is, especially if you're a TypeScript, JavaScript developer, Dart is a relatively straightforward language to pick up in terms of its similarness. So, I think it's just wonderful for us to have these options, and to be able to make these choices, and to have teams like yours really putting a lot of time, and energy to not just make a product that does the thing, but also to invest in the infrastructure like you're doing with Relic, to make it all better. So, it's great to see.  So, you're building these tools, this developer tooling and we're talking about all the benefits of Serverpod. But I also want to crack into your startup founder point of view, because you've done that several times. You've also worked at big companies like Google. You had a startup that didn't go well and out of that came Serverpod, but it's all the trajectory and the learnings. What practices or insights have you picked up over the years through that journey that has enabled your small team now to move fast and build something really ambitious? Because this is not an easy problem you're solving. It's technically difficult. What has helped you to move quickly and figure out what to build? **Viktor Lidholt:** I think there are a couple of things that I learned from my previous experiences. One is, be very careful and spend a lot of time on recruitment, making sure that people we hire are really skilled. It doesn't really matter too much if they know Dart. I think that's something you can pick up if you're a good developer, but this can be different for different types of projects. But for something like Serverpod, it's so complex. It's like really the nitty-gritty details of generating code, analyzing the Dart side of things, building trees of parsed data. There's a lot of pretty complex stuff in there, so really need good developers. So, spending that extra time finding good talent, I think is super important.  But another learning is that if you have a startup, maybe this is true for many companies, you never know the outcome. It's very insecure in a way. We raised money, so we're good for a pretty good while, but you never know. And we're doing everything we can for it to work out, obviously, and putting all that effort in, but there's a chance it won't work out. So, I figure the important thing is we should have fun doing this. Then if it were to fail, we kinda won anyway, because we had a couple of great years building this. If you just think about getting that next release out or whatever it is, I think there's also a bigger risk of failure. If you have fun and build a team together you like working with, the chances for success is much, much greater, too.  **David DeRemer:** Yeah. That's fantastic advice. As founders or especially founders, you're in this interesting spot where you have this big, bold goal, it's very high risk. And a lot of times you don't even have necessarily full control over whether or not you're going to be successful, because you can make an amazing product that fits the market well. But maybe right when you launch, there's a big economic downturn or something. There's so many external factors that it's a tough position. And one thing that I think is interesting as a founder, especially in the very early days, let's say the first three to five years. Maybe if you raise a series C or D or whatever, it's a different story, but like quitting is not an option. You can't just be like, "Oh, I don't like this job. I'm going to go just change jobs." As a founder, you can't really do that. And I think that creates a pressure or tension to really make sure that what you're doing is motivating to you, is passionate to you. And like you're saying, and out of... I personally have felt like when people are bringing passion to something, the outputs are so much better. The carrot-stick model of incentives in companies and business, I think they don't nearly create the result of when people are just genuinely interested and passionate about what they're trying to accomplish. So, I think that insight of making sure you're having fun and enjoying it, I love that idea, too. That at the end, even if for whatever number of myriad reasons that you can't control, it doesn't work out, as long as you can look back on that time and be like, "We had fun and we learned something and we did something cool together," you've already won. **Viktor Lidholt:** Yeah, I think so. And for me, I think I need to work in a startup. Being in a big company, it's fine for a little bit, but I have too much creativity, ideas that I need to get out somehow. **David DeRemer:** Yeah. Well, I mean, it's great the world has both. I think if big companies were run by startup founders all the time and that's what everybody was, it would be complete chaos. **Viktor Lidholt:** 100%. **David DeRemer:** And so, you need the Googles of the world, like we were describing before, just in terms of the sheer scale, and the time horizons, and the investments they can make at such a massive scale. But then you need small, creative, really passionate people who are working to really change very important problems in different ways. So, need more people like you out there for sure.  So, you are very active and have been in the Flutter community space for a very long time. I think it's an obvious benefit to you to immerse yourself in those communities to attract people to it. What are some other advantages of showing up at all these events and being such an active contributor to the community, creating open source, writing content, and contributing, and just being like a known person and company within that ecosystem? What are some of the advantages you've had maybe beyond the obvious of the opportunity to maybe meet a customer? **Viktor Lidholt:** The feeling of being a rockstar. No, I'm kidding. But yes, I had my 15 minutes of fame at one conference. The crowd was chanting my name when I entered the stage. **David DeRemer:** Awesome. Love that. **Viktor Lidholt:** Yeah. Otherwise, I mean, to be fair, I mostly do it to build our brand and raise awareness. But also, I think what I get back the most is probably all the connections I get, especially when I go to conferences, and meet people in person, and they know me. And I get a chance to meet all these people, that I think that's the biggest thing, maybe on a personal level. **David DeRemer:** Yeah. I think that's important to highlight, though, because I think one of the things that I've been trying to help the community of... especially the enterprise user base within the Flutter community saying, "Hey, we need you here. We need you at the Flutter events. We need these bigger companies that have large teams and maybe 100 engineers or more, we need them there." And there's a little bit of a question of why. What benefit does a company get out of showing up at this event? The obvious ones are like, "Well, maybe I can get more customers by making the people more aware that we exist." Or the second thing might be, "Maybe I can hire or recruit people." But what you just highlighted, I think is really important, that I think if you're looking very tactically on how many leads did I get and then how many leads did I convert—and that's how you're judging the value of attendance at an event—you're missing a bigger picture, which is that maybe in the course of that event, you encountered 100 people that had a personal interaction with you, that you created a connection, that relationship. It might be that the benefit isn't within six months of the event you've signed a new customer. It might be that six years later that contact has developed into a friend or a network. And professionally, that can unlock something or could make a connection like you were describing before of like, "Oh, you know who you should talk to? Is Viktor." And then all of a sudden, boom, years later. And that's really hard to quantify, but I think what you just hit on there is super important. That gets lost a lot of times. **Viktor Lidholt:** Yeah. I think we touched a little bit on that in the beginning here also. Just helping people out that you meet along the way, it may not have an immediate effect. But I think in the longer term, it definitely will have, because maybe just help someone or connect two people that can help each other out, but all that will come back if you do it enough times to you in many good forms. So, I think that's also a great way to look at it. If you go to a networking event and just focus on the persons that you know this could be a potential sale or this can be like, "I want to talk with this person because of this," I think you miss out. So, it's good to not ignore the persons that may not look interesting. And if you can give them a helping hand along the way, that can maybe help them now, but help you or help someone they will help in the future, help you. Who knows? **David DeRemer:** Yeah. I think that's good advice, not just for tech communities, but for all communities. In terms of communities and where you see Flutter going, do you see any needs within the Flutter community or things that if we had this or could approach certain things better within the Flutter community, it would improve...? And Dart, let's be more broad about Dart. What things would really help these Dart communities grow and thrive more in the years ahead, you think? **Viktor Lidholt:** I think it's really cool that the Flutter team is breaking out material and cupertino from the sort of core of Flutter. I think that opens up doors for the future, for the communities to step up, and build high quality packages for different types of UIs. So, that is definitely something I think the community can step up and do a better job. There're already, I mean, some amazing stuff out there. We're using Shut CM for Serverpod Cloud, our console for instance, and that works pretty good.  But something I kind of started a little bit is doing native liquid glass implementation with... What's it called? Platform use. Actually, works pretty well. So, you can get liquid glass proper in your Flutter app. Getting that to a production level is something that would be super cool if we could do as a community effort. You can do it pretty easy if you just use platform use. It's not that hard, but it would be nice to have a full library so you can just drop it in and have it working. So, that's something. And maybe you shouldn't be done with platform use. It's possible that you can do it even more efficient, so you may want to do some research into that. But having that, I think would be a super cool thing for the Flutter community and moving a little bit faster on those. So, make sure it's up to par with whatever is the latest and greatest on each platform. **David DeRemer:** That's great. We'll wrap us up here with a couple of quick fire questions. One thing I wanted to just get your point of view on. Given your interesting career arc through games, building tools for that community, Google, and a couple of startups, and building server side Dart tools here with Serverpod, what's one lesson from your career that you've learned that you think 10 years from now will still hold true? **Viktor Lidholt:** I already touched upon it. But it's like, make sure you have fun. Whatever you do is... development work should be fun. **David DeRemer:** Totally. **Viktor Lidholt:** That is my belief anyway. **David DeRemer:** I like it. **Viktor Lidholt:** And I always enjoyed it, but maybe it's easier said than done sometimes. **David DeRemer:** Yeah. To some extent, otherwise, what's the point? You're using your very precious time. And some people chase money, some people chase prestige or a sense of accomplishment. But at the end of the day, you got to enjoy the ride.  Viktor, thank you so much for joining us today, and sharing your story, and for all the contributions you've made to the Dart community. I hope that anyone listening takes an interest in Serverpod, checks out what you're doing, and can hopefully give it a try, and use it within their business. Because it's definitely, I think, a really important part of the ecosystem and has really helped Dart, and Flutter, and the overall ecosystem be more robust and able to provide this full stack story for everybody. If people wanted to follow your work, explore Serverpod, pursue careers with you, where could they find out more? **Viktor Lidholt:** So, serverpod.dev is the easiest place to start. With Serverpod, obviously, we have also an academy, which is like a four to six hour class with video tutorials you can follow along to learn Serverpod. And you can follow me on LinkedIn or X, Viktor Lidholt, if you want to find me. **David DeRemer:** Awesome. Thank you so much. Really appreciate you spending some time with us, and sharing your story, and thanks again for everything you've done. **Viktor Lidholt:** Thank you. **David DeRemer:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review. And if you want to get our latest episodes, don't forget to subscribe. Thanks again and see you next time. ### Rebuilding Mobile Architecture With Flutter & 3D Source: https://verygood.ventures/podcasts/kody-peterson-foresight-sports-rebuilding-mobile-architecture-with-flutter-and-3d-innovation/ This episode, we sit down with Kody Peterson—Director of Software Engineering, Digital Studio at Foresight Sports—to explore engineering leadership, cross-platform development, and pushing the boundaries of 3D, data-driven personalization in sports technology. Drawing on his experience at The Walt Disney Company, multiple startups, and in golf technology, Kody shares how his team blends hardware, software, and Flutter-based mobile development to deliver high-performance, intuitive tools for athletes and coaches. {% audio_embed url="https://player.cohostpodcasting.com/dbce59ad-0d61-42fd-a371-59eeef0986df/e30b889a-f00d-45f6-80c9-4967eb8b5066" /%} ## Transcript **David DeRemer:** Hi, I'm David, and this is Build to Succeed from Very Good Ventures. Today we meet with Kody Peterson, Director of Software Engineering at Foresight Sports. In this episode, we explore Kody's engineering leadership experience building high performance apps with exceptional user experiences. At Foresight, Kody's team is pushing the boundaries of Flutter, including new approaches for creating 3D experiences, and he offers some really compelling advice about choosing the right technology for your team. I hope you enjoy it. Now here's Kody.  Welcome, Kody. Thanks for coming on the podcast today.  **Kody Peterson:** Super excited for this one. **David DeRemer:** Got a chance to meet you recently, and really appreciate what you guys are doing at Foresight. So wanted to get an opportunity to talk with you and share your story. But one way I like to start is, tell us something about you that connects your personal world to your technical one. I know we have our jobs and our things that we do, but what's something you do for fun that really connects to your work and technology? **Kody Peterson:** Yeah, that's a good one. For me, I've found a love for trains. They're super interesting to me. They use technology from forever ago. A lot of people don't know, when a train's coming up to a crossing, it's just like an electrical line that causes those rails to come down. And so lately I've been experimenting with my 3D printer and trying to print train tracks, and then use a little bit of our Duino, and some servos and stuff, to really get that to work like it does in real life, which poses its own challenges because we don't have things like electrical on plastic. So you have to think of other creative ways. And I always think that's fun, is trying to take technology that exists in the real world, maybe bring it down to a much smaller scale, and then go ahead and implement that in some way. So yeah, that lately has been the thing I've been hacking on in my free time. **David DeRemer:** Nice. There's an interesting scale there. Is that physical world interaction—has that influenced how you see software architecture? **Kody Peterson:** Yeah, I think so, because especially here at Foresight, we interact with physical products to a mobile app or to our golf simulation software. And so it's a continued extension of that. Even an Apple Watch, or something like that, it's taking something that happens in the physical world and then implementing it in a way that you can do something in the virtual space to do that. So the trains, for example, they're all controlled by a web interface. The trains know where each other are. So you've got this connection between what's happening physically, and then translating in that some way so that your code and all the ones and zeros know what's actually happening, which is really the hard part, because they can't see it. So you have to find creative ways to translate that. **David DeRemer:** That's interesting. I might have to check out some of this train tech then. I remember the old Lionel trains that would roll around a Christmas tree or something. **Kody Peterson:** That's right. It's a little bit more advanced than that. **David DeRemer:** Yeah. Pretty neat. Well, so take us back to the beginning. Give us your story. How did you get into engineering, and what led to your current role at Foresight Sports? **Kody Peterson:** Yeah. So for me, I got started in the education sector actually. I dropped out of college because it wasn't for me, and I was bored, and applied for a web job at the college. And for some reason, someone took a gamble on me. And so I ended up working at the college for a little while, working with really large systems. So right straight into big Oracle data warehouses, and that sort of stuff. I'm a newbie to tech, have a little bit of HTML experience essentially. And now they're like, "Go find a way to query these really large databases." And I'm like, "Sure, I can totally do that." And in some way I was able to do it. It's just like figuring out creative solutions. So did that for a little while. And actually, funny enough, I started almost immediately in my career journey trying to figure out ways to do mobile without Native iOS and Android, mainly because I didn't know it. I didn't know how to do it. And I knew PHP and HTML JavaScript. And so I came across this thing called PhoneGap, now called Apache Cordova, or maybe it's called something else now even. And it was what I knew, HTML, JavaScript, CSS, and I was like, "Sweet, I can make mobile applications now. Look how awesome I am." And so actually ended up pitching that to the college, and we started working on it a little bit, but then I got this really great opportunity I couldn't turn down to go work for the Walt Disney Company. And so we never really got PhoneGap off the ground there at the college. But went to go work for the Walt Disney company for a while. Once again, a good gamble on their side, I guess, which I really appreciate now, coming in with a little bit of experience and being able to work on some really large, complex websites. It was the time when Magic Bands were coming around, and we were trying to get that whole system integrated into the web. My introduction to physical to virtual space again, it always keeps coming around. So I did that for a pretty long time.  And then I got an opportunity to go work for a startup, which sounded really exciting to me. Didn't really know what I was getting into, but it turned out really great. We were in the FinTech space, essentially creating chatbots for the financial sector, well before ChatGPT. So definitely a bit ahead of our time. And so did that for a little while, then moved into management within that ecosystem. Starting to become a leader and learning all the leadership things, and how to motivate people and make big business decisions, like moving to Flutter or something like that. And then after that, moved on to another startup in the golf industry. So really just a lot of different industries. And recently we were acquired by Foresight, and we've come into Foresight now and been able to provide some leadership there, and really change the software ecosystem here at Foresight into, now what we've got, with this brand new app that we released earlier this year. **David DeRemer:** That's awesome. What a good mix there of startups and then Disney and all those sorts of things. And sounds like in your career, you've had a lot of opportunities to take some risks and try new things, things that maybe someone trusted you to do, or like you're talking about, the database queries. And do you think that that early belief, like, "Hey, I'm going to give you this hard thing, and go figure it out." Did that skew you towards startup decisions later, I'm curious? **Kody Peterson:** I think so. I think it was, one, I'm really self-driven. And I think to join a startup or to be a part of a startup, you've got to have that mentality. And so that worked in my favor. But yeah, I think me having those opportunities to, someone just taking a risk on me, and then me having to fulfill that because I felt indebted. It's like you know yourself, how good you are at what you're trying to do. And sometimes in interviews, we definitely try to look a lot better because we're striving to grow and move our career in different directions. And so for me at least, it's like, "Wow, I recognize you're taking a risk on me. Thank you for that. I'm now going to do everything in my power to exceed those expectations." And yeah, so then a startup comes around, and you're like, "That's all it's going to be. It's going to be just running all the time, continuously trying to surpass expectations of the customers and everyone else you're working with." Because at the end of the day, you've got to get funding, right? You've got to continuously deliver to customers. And that's the beauty of startups, is just being able to just keep iterating and iterating and iterating. And so yeah, I'd say a lot of it came from just that risk, for sure. **David DeRemer:** It shows a lot of self-awareness too, when you're in those situations where people are taking a risk on you. I think to turn that into creative energy, to actually be motivated to push harder. I think a lot of people have personal motivations, "I want to do this for me," but I think that's pretty cool to think about, like, "Hey, people are taking risks on me to help me out, to invest in me, to give me hard problems and help you move forward." I think that's pretty awesome.  And when you think about that progression in your career, all the way through now, you've been through an acquisition into Foresight here, were there any key moments or lessons learned that you think have best prepared you to navigate that transition and now be in your current role? **Kody Peterson:** Yeah, that transition is an experience all on its own, I'd say. And I'm glad to be able to have that opportunity to go from startup world to being acquired. And then now you're going to have to prove yourself again, almost. It's like that the acquisition was for a reason, that doesn't mean you pump the brakes. You almost start accelerating even more. You're given more resources to do that, you're given more stability to do that. And now you have this opportunity to take the product, or take what you're building, to the next level. But with that comes a lot of weight on your shoulders, to be able to deliver that and to do that. So it's almost like taking the startup and then starting up again, but with a lot more stability and a lot more resources. Which is really the best part, if you will, to me, because you get to grow your team, you get to meet new people, hear ideas from different ways instead of really, really small team just doing what they think is best and building the best product they can. **David DeRemer:** Yeah. That's really important, because when you think about acquisition... I think when you read about acquisition, it's like, "Oh, this company got acquired by so and-so." You see the number maybe, and everyone was just like, "Cool, we have this acquisition. Our job is done." It's like, "Nope. Well, the reason that company acquired you is because your job is just starting." **Kody Peterson:** That's exactly it. Yeah. It's just, start that loop again. Yep. **David DeRemer:** Yeah. Well, and so tell us about Foresight Sports. What do you guys do and what's your role there? **Kody Peterson:** Yeah. So here at Foresight, we develop launch monitors. We have two kinds, overhead units and then ground units, essentially for those non-golfers out there, including myself. Just worth calling out, definitely not a golfer, but I'm learning a lot about golf throughout this whole journey.  But we've got these devices and you put the golf ball in front of it, you hit the golf ball and it provides a whole bunch of data about the ball, the characteristics of that ball, what it did in rotation, at speed, how far it went, with extreme accuracy. And then additionally, we can provide details on the clubhead and actually where the impact was and what angle your club was at. All this data golfers tend to utilize to get better, and we can utilize that data to help the golfer get better. And that's where the software aspect of the hardware comes into play. So we get that same data that the device has, but we can present that data in so many different ways, and provide visuals to the golfer that they've never been able to have before either. And throughout those experiences and throughout some AI features and some coaching features, and all those things that we could add to our software, we provide a way for the golfer to get better, and to better understand the data that they're getting from the device as well. **David DeRemer:** So if I'm a golfer and I want to get better, I have this device in the ground or above me, right? And so I do my swing, and it's tracking my swing, the ball, everything. What's it looking at? **Kody Peterson:** Yeah, it tracks the ball and the clubhead specifically. And so between that, we have all the data required to know exactly what happened, all the way through the shot to impact, and then out through the flight as well. And then yeah, it's just about visualizing that and providing data. In so many different ways too, because every golfer learns differently. We all learn differently, whether it's golf, whether it's a new skill, or anything like that. And so our challenge, on the software side, and really the challenge for our product folks and our design folks, is how do we present this data in a way that multiple personalities and multiple ways of learning can understand it and can grok it and learn from it? And so on the software side, that's where we're just building out a bunch of visuals so that if you're a data person, you can look at our table view and you can just read the data and you'll understand that. But if you're a more visual learner, like myself, I need those visual aids. I need representation of what was happening to the club and to the golf ball, to help me learn. **David DeRemer:** Yeah. That's so cool. Sports in particular, I think, has that unique balance of the data, like you're saying, the tabular data, and wanting to get into that, and then the visualizations. We work with a NASCAR team called Trackhouse, and there's like so much data, and some people just only want to look at the numbers, and other people are like, "I can't make sense of this. Can you show me a graph or something? What should I do with this?" So I think sports in particular has that balance, and having good effective tools to help you iterate through that, I would imagine would be pretty important. So let's maybe get into that. What are some of the technologies you guys are working with? **Kody Peterson:** Yeah. So we're really trying to expand the ways in which we can provide this data. And so in the Flutter space specifically, we've got a lot going on there. Our design team is very blue sky. And as developers and engineers, we don't ever want to say no to a challenge. And that's had us build all custom components. We don't use material styles, we don't use Cupertino styles, or anything like that. It's all very custom. We use them as bases because they're good foundations to build on. But if you were to look at our app and you were to look at the design, you wouldn't see anything there that you've seen within UIs themselves. And that's a design language choice. That was actually one of the things, when we came in through acquisition, we identified as one of the goals that we wanted to do here at Foresight, was start to build a design language and start to build this consistent user experience, and give Foresight this look and feel that you look at and you go, "That's Foresight." But that's an immense challenge on the software team, to have to build that out. You go essentially straight from Figma and you're like, "Okay, well, let's build out the borders and let's build out the exact curvatures and spacing and all these things." We have a whole button library because we couldn't just use the material buttons, because they're all vastly different in our own sizes, and that sort of stuff. And even outside of just the UI, we've got complexities. And the logic, the data, and the data is so important. We have to ensure the precision of the data coming in from the device is exactly what you see in the application. If the software doesn't show the same numbers that the device shows, the golfers are going to be asking, "Well, which one's right?" Our customers will wonder \[about\] the accuracy of the data. And so we need to ensure that the algorithms and the formulas, and the way that we're calculating the data, is identical. And so we've got a lot of logical things. We use Block for our state management system. We've actually built wrappers around Block because we just needed more. We needed more ways to understand when events were actually done and complete so that we could do other things, because there's a lot of order of events that have to happen. And then we can even dive into our 3D modeling, which I know we want to talk about. I've previewed you a little bit on that one, but yeah, we brought 3D to Flutter, in a roundabout way, but we're really excited about that. And that was a huge challenge. And I think that just talks to our software team, is that we get something from design and we don't think, "Well, can Flutter do this?" We think, "How can we get Flutter to do this?" Or, "How can we get Unity to do this?" In terms of our game simulation software. It's a really different mindset. It's that startup mentality that we've brought to a large organization, and we found that that mentality allows our engineers to get really creative and grow, even in their own right. So yeah, lots of neat technologies that we're pulling in, but a ton of custom Flutter work. So we know a lot about Flutter at this point. **David DeRemer:** Awesome. Yeah. Well, and so much to unpack in what you said there, and so many things about Flutter specifically, that I think are interesting, that idea of building your own design system. Right now, the Flutter team is working on actually taking the material and Cupertino out of the core framework, and things that you could bring back in, for precisely this reason. The original intent of it was, you build your own design system and your own thing. Yeah, we're going to give you material in Cupertino as something to start with so you can build something easily. But just that acknowledgement that I think a lot of big brands and companies that really care about how they're showing up brand wise, they don't actually want to look like a Native app. They don't want to look like the default Apple stuff, or Google material things, they want to have their own aesthetic and user interface paradigms and stuff. So I think that's been really cool. So yeah, design system's a big one. And I think it's so cool how you guys took that approach too of like not expecting it to be there, but be like, "How can we push the framework? How can we do more?" And I think that's really the philosophy that is the whole point of open source software really, is like, "Hey, build on this. Take it and run with it, add more things. Don't just wait for Google to figure it out for you." If you do that, even though they have so many resources, you never know if they're going to get to it.  How did you get to Flutter in the first place? Were some of these things part of the evaluation criteria, and what led you to it, or were these happy side effects? Tell us the story of going from your trajectory into Flutter. **Kody Peterson:** Yeah. So when I came into the Foresight ecosystem, one of the things that I started to look at is, "Okay, well, what do we have here already? What are we building on top of, and what are we going to just make even better?" And they already had a mobile app here. It was built in React Native. So the first step was like, okay, let's think about what we want to do. Let's think about what we want to accomplish. We've got a whole slew of features that we're looking to bring into the mobile space. We've got this new design language that we were talking about, and is React Native the right choice? And what we ended up identifying was, for performance reasons, for the animations and the high fidelity level of UI design and control that we wanted to do, Flutter just outperformed in that way. We ran multiple POCs within the React Native space and within the Flutter space, and some various other frameworks as well. And we found, for our requirements, is that Flutter was going to work better for us. We knew that we wanted to try to identify some way to do 3D. We knew that we were going to want to have a really quick iteration on things, and have full control over the platform itself. Another consideration was, we have to talk to Native SDKs, that we build in-house, to talk to the devices. What bridge exists there, or what would be really well served? And while both React Native and Flutter have a bridge to talk to the Native side, what we were missing on the React Native side was this type safetiness, this really well, easily orchestrated way to get data models from one side to the other in a way that still gave us our type safety. And so utilizing Pigeon, for example, on the Flutter side, we're able to have this really well-defined structured messaging layer that allows us to talk to the Native side. And so that was another checkbox on the Flutter side for us. And so once we identified we wanted to go the Flutter route... And additionally, in the golf startup, PinSeeker, which got acquired by Foresight, we had already been using Flutter there. We knew the advantages of Flutter, we knew the speed of development. And not that that doesn't exist with React Native, it's just we knew the nuances and the differences. And so we came in with that knowledge as well. We came in with some foundation that could help us out as well, interacting with some deeper GraphQL stuff, and custom build runner code generation stuff, that we could quickly get up and running. So after we identified Flutter, the next step was, "All right, well, how do we convince the business to essentially say, "Hey, we've got to start from scratch."? Because there's no magic, convert this React Native over to Flutter, as much as we would love that. Or convert this Native to Flutter even, because that would make a lot of our jobs a little bit easier, but it doesn't exist. And so now you have to figure out, "Okay, well, what are the business benefits and how do you tell the business that this is a great reason to do this long term?" **David DeRemer:** And so did you build a brand new Flutter app or did you integrate into the React Native app? Or how was the approach there? **Kody Peterson:** So for this one, we built from scratch. We just started a new repo, spun up Flutter, brought in some foundational components from PinSeeker, and went on.  In the PinSeeker world though, that one we actually started as Native iOS and Native Android. And that application had a lot of screens, it already had usage, but we ended up needing to be a bit more leaner with the team as the startup went through its different phases. And so as we start to lose some of those individuals, that new Native iOS and new Native Android, and we get down to like one or two people, you end up thinking, "Well, how do you develop with one person or two people, iOS and Native IOs?" You're duplicating the efforts. And so that's where we really started to think like, "Well, what if we could just do this one time for everything?" And so that's where Flutter came into play. But being a startup, you can't just say, "Well, let's throw it away, essentially, and just build it from Flutter." And so we had to start doing add-to-app strategies. Which is probably, I'd say, one of the most uncommon things about Flutter is its ability to be injected into an already existing Native iOS or Android app. You don't have to use Flutter, just straight Flutter, right? You can have some views that are Native and then switch over to Flutter views, and then switch back, go back and forth. You can share data. I would say though, it's not easy, and not for the faint of heart. We had to learn a lot about the Flutter cache engine and understand how to start Flutter up in the background for some of these views, so that when you switch to it, it just showed up. We had a lot of effort to keep designs across Flutter and Native the same. So it was definitely one of the biggest challenges we had at PinSeeker, but we knew that long term we would get to a place where we could begin to migrate everything over to Flutter, and then at the end we would be all Flutter. And eventually we got there, or most of the way there, I think we still have a few lingering views, or something like that. But for the most part, it's all Flutter now. And we found the time to take those views, redesign them, allow product to take a second look at those views, and then implement those in Flutter. But for a long period of time, it was a lot of coordination between switching to Native and switching to Flutter, and back and forth. **David DeRemer:** Well, it sounds like you're a go-to person for anybody who's curious about React Native versus Flutter, or do you go from scratch or do you add to app? And then there's the other side of Flutter, that I think is cool, you start with Flutter and add Native to it or add other things. You can go that side too, right?  But when you think about coming into Foresight, and you had this existing React... Because this is at, really, the critical point of the debate around Flutter, React Native. There's a whole debate around either one of those, relative to Native, but the React Native to Flutter thing, in your experience with your team and what you've seen from the teams who had seen both, what's your honest take around how those two... They're both great technologies. It's cool we have them both, but what's your honest take around the pros and cons? **Kody Peterson:** Yeah, I think it's really interesting. I think that React Native has some really great qualities around it. I think from a talent perspective, it's probably a little bit easier to find people that can just get into React Native. Think about all of your engineers that know React, or know TypeScript, they can almost next day begin in React Native. Sure, there's platform nuances and that sort of stuff that they have to work out, but that's no different than dealing with CSS nuances, or browser, Microsoft Edge versus Google Chrome, on the React side. So I think that's one of the big things that you could look at React Native and go, "Wow, finding talent is really easy there." The Flutter talent pool is a bit smaller, right? It's still, I'd say, a new community. It's still maturing and evolving, but it's evolving into the direction of high performance and high fidelity. And I think that's the big differentiator, especially for us, as we looked at the two, is, sure, React Native is mature because it builds on top of React, and it gets all of that maturity and thought process through React, and Flutter is building something new, right? It doesn't have that to fall back on. It's got Dart, but they came together a little bit, if you will. And so I think that's where it is. But because of that, because it is new, because it's being built specifically for this use case, it has the advantage of working through high performance, working through high fidelity, and really focusing just on these platforms, mobile, desktop, web even. And so I think that's really where it comes down to me on it, is, what are you going to be doing with it? What is your use case in trying to understand what you're trying to do with it in the future even? Which is a hard thing to think about. When you're developing a product, a mobile app for the first time, you're just thinking about what I need to do now, that MVP, right? It's really hard to think about in the future, but if you put some thought into that now, you'll likely make the best decision for your future self as well. **David DeRemer:** No, that's insightful. And so many people want to know what's the answer, right? Just tell me which one to pick. And there's never just one answer. Because you're right, if you have a whole bunch of really great TypeScript, JavaScript developers that know React, and they want to get into that, that's an advantage you got to pay attention to. It's also maybe not as hard as people think, to like, okay, if you've got a really good engineer, who really understands fundamentals of solid software engineering practices and has been building mobile, and they're expert at TypeScript, they're probably going to pick up Dart and Flutter pretty quickly. **Kody Peterson:** There's so many similarities between Dart and Typescript or JavaScript. And that's what we've found too, is we've got some really talented senior engineers, some that, maybe on the Unity side, et cetera, and that didn't have Flutter experience before. I mean, I'd say even myself, four or five years ago, or so, didn't have Flutter experience, and now I'd say I'm pretty good at it. But it's that just understanding of programming concepts, and understanding of just other coding languages, that you put the pieces together and work it out. So if you've got a pretty strong team that understand different languages, they're probably already going to be able to pick up Flutter and run with it. **David DeRemer:** Yeah. And like you said, what are your objectives? Are your objectives to build high performance, data driven experiences, where you want to have full control of your design system and you don't want to worry about... One of the things I think is interesting about the Native side of React Native is that while that's a benefit in some situations where you want it to look Native, if you're really trying to have very tight control over how your app looks on every device that runs it, well, that can be a huge pain, of like, what if somebody's using an old version of iOS or Android on an old phone? And how is that actually going to show up in terms of what the Native component is going to be rendered as? Whereas with Flutter, you ship the rendering engine with your app, and it's going to look the same everywhere. You also mentioned about having to convince leadership, and work through, how do you sell this internally? Take us a little bit through that habit, maybe even going back to the PinSeeker days, and then coming into Foresight, I guess, and pitching Flutter into this existing team, what are those conversations like? **Kody Peterson:** Yeah. In the PinSeeker days, it was a little bit easier. Smaller team, it's startup, so you already get that advantage of like, this will help us move faster. It's proven already. You can read hundreds of articles about it. That one's pretty easy. Where it became challenging was when we identified the nuances of add-to-app, and the challenges that come to that, you end up finding it's taking a little bit longer than it normally would if it was just Flutter to start to implement those things. So now as you build that stuff out, the business conversation becomes, how do we, as quickly as possible, begin the migration process? We've now seen, yes, you can build these views very quickly, but now we're running into time challenges with getting the Native integrations working, getting the transitions working, and that sort of stuff. So you've shifted the problem, not so much in convincing to do Flutter, but trying to now convince, "Hey, let's pause on product features so that we can do this migration, because now you've seen the benefit of it."  Whereas on the Foresight side, it's the ask of, "Hey, we'd like to move away from React Native, this app that already exists in the app store, that's already in production and serving customers, and we'd like to not put any more features there. And we'd like to build this new thing and this new technology, that we have to build every view that's already there, from scratch essentially. We've got some logic that can probably be copied and pasted over with AI, that can help us a little bit, which is great." Now that's the discussion. And that's where it comes out to be, well, we've got a proven model on the PinSeeker side, which is great. We've got to prove a model across all these other companies that are doing that, just like VGV, and how do we now tell the business those case studies and those user stories and explain how that is exactly what we're trying to do, and will, in the future, pay off. And that's really what it's about, is, even if you're coming from Native iOS and Native Android, you're making that decision of, we’ve got two teams, or you've got one team of hybrid engineers that know both, which is pretty uncommon, but it definitely exists, and you're now thinking of wanting to move faster, and you want to ship features faster, and you want to do all these things quicker, how do you do that? And one of the answers might be, "Well, we move to Flutter, because we just build once and it ships everywhere. But to do that, we're going to have to move slower." And that's really the hardest bit of conversation that you have to have, and you have to have a lot of conviction in it. You have to have a lot of confidence in your plan. There were a lot of conversations, where it's like, "Well, maybe we just, one more feature in React Native, one more feature in React Native before we start the migration." So the migration keeps getting pushed back a little bit more to start Flutter, because it's so easy to just say, "That platform already exists. Let's just keep adding it." But if you can come to it with a plan, if you can come to it with all these features, it starts to make sense. But it's something that you're going to invest in, in order to get those long-term benefits a little later.  And I'd say one of the other things that's really helped us in that business case, and that decision at the leadership level to do it, is, we weren't just taking what we had and converting it to Flutter. We were taking what we had, looked at all the views, looked at the user experience, and took it as an opportunity to redesign and rethink through the user experience. And if you end up in an opportunity like that, you have a lot more power, I'd say, to be able to convince the business that, "Well, we're going to have to rebuild all these views anyway in this language or this ecosystem. So because of that, this now becomes way more of a possibility, because it's either rewrite it over here or rewrite it over here." And so if Flutter's that thing that you think is going to give you that long-term advantage, are you already going to be doing a redesign or already re-looking at the user experience? If so, it's more of a reason to be able to do the Flutter thing now, than keep building it with what you have. **David DeRemer:** Right. Yeah, that's very insightful. I've definitely heard a lot of people talk about old code bases that are just too cumbersome to maintain, right? Where it's like, "Oh, we've been building this Native code base, 2 Native for eight years, and they've just diverged so much that to ship a new feature in with parity, it's so hard because the architectures are so different. We're now maintaining different APIs in the backend to talk to them, and all this stuff." And that's out of a need of like, "We just can't ship fast enough." But I think that insight of, well, maybe you're at a moment where you're doing a big brand refresh, you want to completely rethink this, and you're going to have to do a lot of energy to redo your user interface anyway, take that as an opportunity to put yourself in a better position. Given that, I'm curious how many people out there have taken a React Native app and then pitched like, "Let's go to Flutter." Certainly, we hear a lot about Native to one of those two. I think that's really interesting, having like we have a React Native thing, and it sounds like the design and the user experience was a part of the discussion. But I would imagine, and leadership who, they're looking at P&L and everything, they're like, "Wait, wait, whoa, whoa, hold on. We already have this thing. We already paid for it. We built it. We got to redo it. Why would we do that?" What are the one or two, besides the design, which it sounds like might maybe is one of the two things in the UX, what else really resonated in that discussion with leadership, you think, in retrospect? **Kody Peterson:** Yeah, I think it came down to, once again, performance of what we were going to be building. We came in with a really good idea of future features. And I think if you're in a position like we are, and you're trying to pitch to business, move from React Native to Flutter, for whatever reasons that you have, me, as someone being in leadership, I'm going to ask those same questions, like, "Okay, so you want me to not only approve you finding newer talent, or different talent to support this, or training your existing engineers for this, but also I'm going to have to put a pause on new feature work, for how long, in order to you to do this?" Or are you saying, "Hey, we'll just keep building it in React Native, while on the side we build this Flutter thing too?" Which is a very slippery slope, because if you don't cut it off at some point, you'll end up with two apps that are never ready to go. And so yeah, I think it really comes down to that future understanding when you're thinking about going from an already existing multi-platform framework to another one. Because you don't have the advantage to go pitch to leadership, "Oh, this is right one's to deploy everywhere." That one goes out the window because you've already done that. You can't go to leadership and say, "It'll be faster to develop because you're already there." Like we talked about, there are a lot of similarities between React Native and Flutter, right? They're both doing the same thing, just differently. And so if anything, it's a much harder pitch. You've got to find the right time to do it, and you've got to find the good reasons to do it, right? Just because, maybe Flutter's new hotness probably isn't a good reason, especially at the leadership level to do that. And so I think that's where it comes down to, is just trying to find those right reasons, and find the right time to be able to pitch something like that. **David DeRemer:** Find the right reasons, find the right time. That's very, very wise advice. And I think that's what's so cool about that story, is, you're right, when you're talking about Native to either React Native or Flutter, you have that whole thing of like, "Oh, well, just can do more with less." Generally, that one's a pretty easy sell to management a lot of times, even though there's a cost upfront to do that. But yeah, your ace card was taken off the table right out of the gate with that one. So kudos to you for getting there. And I think it really demonstrates also the experience you guys had had already, and the ability to demonstrate that performance and that improved user experience you're going to get out of it. Speaking of improved user experience, I know you guys are also pushing the boundaries a bit with Flutter. You mentioned some of the 3D work you're doing. Can you tell us a little bit about that? **Kody Peterson:** Yeah, I'd love to. Yeah, that project has been really exciting, from an engineering perspective. So we, once again, went into this mentality with design of, "Hey, Blue Sky, show us what you want to do and we'll figure out how to implement it." And so we saw the first designs for what's called Club View in the mobile app. And Club View is this ability to see the club from different perspectives, at the point of impact. And so this allows the golfer to see exactly where the ball hit on the club head, and then allow you to see the angle at which your club was, in multiple views. And so we thought they're going to come to us with a static image that we can just rotate and we can plop a little point on, and all is good, and we could totally do that in Flutter. And they came to us with a prototyped out 3D view, these 3D club models that were built from scratch by the design team. They were moving, like animating in. You would tap buttons, the camera would like rotate around them to the views. They had these really great animations. And I looked at it, and I go, "That's amazing. I love that user experience. I have no idea how I'm going to get Flutter to do this." Because this was very much when Flutter announced that they were starting to look into 3D, and they have this experimental library that lets you put a 3D model on the screen and look around it. But I knew we needed way more than that. I knew there wasn't camera controls. There wasn't a 3D scene, there's none of that. And so we started to sit at the table and go, "Okay, well, what can we do? What are some technologies that exist out there already? What can we take advantage of, or work out to make this happen?" And I just happened to watch a talk by the SpaceX Starlink team, who, from what I understand, actually builds their app in React Native. And they were coming to the same conclusion of like, "Hey, we want to do 3D in React Native. How do we do that?" And they had the same problem, there's no 3D standard support in React Native. And they came up with a solution to utilise JavaScript and HTML technologies to put 3D embedded into React Native. And I started thinking, I said, "Well, first off, that's a great idea. So I got to give them kudos because we've built our architecture, pretty much foundation, on top of that." And I said, "I wonder if we could do that in Flutter." And so we started looking at the different JavaScript libraries that existed out there for 3D, and we came across Babylon JS, a very mature, well-known 3D JavaScript framework. And so we started to build a little POC out, and then we took an in-app web frame and we loaded the HTML JavaScript in there, and we had 3D in Flutter. And we were like, "Okay, this is great. This feels good. We've got some performance issues we've got to work out. We've got to bring down the size of the JavaScript, and whatnot, to make it just load a little bit faster, but we've got an idea here. We brought a 3D model into Flutter and we can move the camera around." And then so the design started to evolve a little bit more, as they tend to do. And the next iteration of their design had data points drawn on top of the 3D models. And below were some really gradients, and angle gradients and stuff like that. And we said, "Well, we can't do that in the 3D space because we need a UI system." And that just didn't work. We're like, "Okay, well, what if we built these layers in Flutter?" So we've got Flutter doing the overlay, we've got Flutter doing the underlay, and then we've got the 3D model doing its thing with the cameras. And we found that we could make the web view transparent, and we could now have Flutter sit on top and below this web view. But now we needed to coordinate these systems. We've got two vastly different systems that are running in their own space-time continuums, if you will, and we've got to sync those up and bring those in. So like Pigeon, we built a custom bridge between the Flutter space and the JavaScript space. And while you can send messages already between JavaScript and Flutter, you can't send structured messages, right? It's the similar problem that Pigeon is working to solve. When you send messages to the other platform, it's just like a string essentially. And so we had to create this custom layer that allows us to communicate between Flutter and that JavaScript web browser. And then the Babylon systems had to be built out to recognise those and send events the other way, so that when a user tapped a button on the Flutter side to switch to side view, for example, it would tell Babylon. We would animate out the overlays and then turn the camera, and then animate the backend for those overlays. So we have this coordination and orchestration layer now that seamlessly connects these two worlds together. And when you're using the app, you wouldn't know. And that's the key. That's what I think we're all trying to strive for, as engineers, is we want our customers and our consumers of our apps to believe what we're showing them. I say this all the time, UI is just a facade. We're just having you believe what we want you to believe in what you're seeing. And so we built this out, and it feels streaky, it feels native. The whole thing is just completely connected. You wouldn't know their two systems. And that, to me, is a huge success. And so yeah, we brought 3D to Flutter, asterisk. **David DeRemer:** Yeah. That's awesome. What a really cool and interesting thing. Have you guys done any open sourcing of that? Or any blog posts or developer content or anything? **Kody Peterson:** Yeah. So we're working on that. Actually, I have an article going out on my LinkedIn a little later and that gets a bit more detailed. We had to develop a web server, and this sort of stuff too, that runs on device to serve these files. We had to get ODR on device resources in play too, because we went to go submit to the Google Play Store, and they said, "Your app's too big." And we said, "What? There's a limit." We didn't know this, right? It's just like, we've been dealing with code and just assets. And so yeah, I've got an article coming out with that, that'll provide a lot more details. And we are looking to get that communication layer to an open source repository, like Pigeon is, because I think that providing examples on how to do this multi-layered system, and keep them in sync through messages, I think it's really, really powerful. And there's a lot of use cases outside of even 3D where you could bring these two pieces together. And so I think that that messaging layer is the missing link between those things. And it's really a pretty great way to solve for complex things like this while we wait for Flutter, and the open source gods out there, that are doing great work, that I just stand on the backs of, to bring this sort of stuff more natively in. And I'm really excited about the future of Native Flutter 3D. And thanks to Impeller, and stuff like that, that had to happen first. And I think we've got some big challenges with GPU, and such like that, just like web did. And that's why web worked. That's why Babylon worked, is because all of this effort to get 3D to work in browser has already been done. But now all that effort has to be done now within the Flutter ecosystem to get the communication to GPUs across multiple phone types, and that sort of stuff. That's the big challenge next, which, lot of headway on that recently, which is great to see. So we're looking to open source that, and I think that could be pretty useful to a lot of people. **David DeRemer:** That's awesome. Well, I'm glad you were here to tell the story too, because I think the more people hear about the use cases and the opportunities, the more I think people get motivated to like, "All right, let's invest in solving this problem too." Because you guys are using that in a really cool, compelling way. What would be the alternative? You'd have to maybe figure out how to get Unity or some other 3D tool, or could you not even do it? **Kody Peterson:** And we prototyped that out actually. So we thought about Unity as well, where we've got a whole team that does Unity here because of our golf simulation software. And so that was actually one of the first things that we thought of, was like, "Let's just embed Unity." And then you realize to do that, you have to be on a very specific version of Unity. The layer is just not there. Unity actually recommends against it because of the communication layer. It's not something you can't do, but it's a lot more of a challenge, for sure. And then, when Unity is not running, you're still using 200 megs of RAM on your app just to have Unity loaded. So now you've increased your utilization of precious resources, I'd say, on mobile devices, especially as we think about all of the markets of mobile devices. Not everyone has the iPhone Pro Max, where we could take advantage of that. So we're trying to satisfy the entire market of devices, to be able to support this sort of stuff. And so slowly Unity became less and less of an ideal choice. And so we had to get really creative, and that's where we got to Babylon. **David DeRemer:** That's very cool. Well, thank you so much for sharing that. It's really exciting, innovative uses of it, and I'm happy to hear you guys are pushing the boundaries there. Take us through a little bit of technology leadership, right? I think that you've told so many interesting stories. You have a very risk-tolerant background, very self-actualized in pursuing things. You're leading teams, you've gone through these acquisitions, and you're in a leadership position in engineering. So when you think about everything you've learned, and all these things you've worked through, what are some of the things you've learned about being an effective engineering leader in a company like Foresight, or some of the other roles you've had? **Kody Peterson:** Yeah, I think for me, it's all about trying to help the engineers that work with me as efficient as possible. And that means providing them tools, providing them training, providing them procedures and policies, and all that sort of stuff that we hate to have, but it helps with structure. It helps with the engineers, to be able to do what they're really good at, and not have to focus on those other things. And so, one of the cultural things that we do here at Foresight is, everybody's got a budget for AI tooling. And so we've got cursor accounts for everyone. We've got Copilot accounts. We've got, whatever AI thing you want to try, we have to help you be more efficient. We're not saying go have AI write all the code, because personally, I don't think we're there, and I'm hoping we never get there because it takes away what we love to be, as engineers, in my opinion. But AI, as an extension, to be able to help make you more efficient and be more efficient, I think is really where the power comes in, and to be with those sort of things. So we provide that, and I provide that to all of our engineers, and that helps them be more efficient, which helps them get through tickets faster, which is great for me. But also it's great for them, because they get to see stuff get to production faster and they get to see customers utilize their stuff faster. And that's really what it's about for individual contributors, is them being able to see people utilize their work, and them being able to look at a marketing post and go, "I built that." That's what the feel good is about. And so it's just, for me, it's really focusing on helping individual contributors be more efficient and be more effective, and growth opportunities, I think, are something as a leader you need to always be trying to look at. I have one-on-ones with all of my direct reports every week, or every two weeks, and we focus on, "How can I help you grow? Are you doing this or are you doing that?" Or, "Hey, is there a training program that you want to take?" Or something like that. And then lastly, I'd say for me, as a leader, I'm still into code. I'm still looking at the code. I may not be coding as much as I used to, or at all, most times, but I'm still keeping myself familiar with the architectural decisions and what code is being pushed. And that allows me to talk to the engineers in a way that they understand as well. And it makes them know that I'm right there with them in the trenches too. I've written code to help us get through features as well. And I think that's an important thing for leaders to keep doing, and to be able to do, is gain that trust with your individual contributors and those engineers that are, I would imagine, looking up to you, right? You are their leader and you are trying to help them be as efficient as possible, and succeed, and you're helping the business succeed. And I think the only way to do that is everybody in the trenches together. So yeah, I still look at code. I still get pull requests. I still understand architectural, and I help guide too. And I think that's, for me at least, as a leader, that's really what's important. **David DeRemer:** I love that. That's really wise advice. Maybe it's one of these questions or one of those points, when you think ahead 10 years from now... The rate of change right now is so insane, and it's so hard to think about what we're going to be doing a year or two from now, let alone 10, but when you think back and you reflect on some of the common themes, and the lessons you've learned, is there a lesson that you think will absolutely still hold true like 10 years from now, despite all the change, despite all the AI? **Kody Peterson:** Yeah. I don't know if it's a lesson, more so a phrase. So one of my really great friends, and someone that I get to work with, he drove this mentality into me. It's this startup mentality, as he calls it. It's the, if not now, then when, if not you, then who? And I think about that phrase all the time. And it's a really easy phrase, but it can be utilized in almost any facet, not just engineering, but whatever. And I think that's something that continues to stick with me, 10 years from now will still stick with me. It's almost a motto that you carry along as I make decisions as a leader, as I make decisions with my teams, and so on and so forth, it's driving that mentality to the teams as well. And I think that's something I continue to carry. And I do so even in my non-engineering stuff, it's like stuff around the house. It's like, "Well, is it going to be me or is it going to be me?" So yeah, somebody's got to clean up the dishes and whatnot. So yeah, I think that that's definitely what will continue to stick with me. **David DeRemer:** That's awesome. That's really good advice. And like you said, in general, not just in engineering. It's so important. You got to be able to be motivated, and say, "Hey, I've got to do this. I got to take responsibility. I have that internal locus of control. I can change the world. I don't need someone else to change it for me." I think that's great. Well, that's a great place to end. Thank you so much for spending some time with us today, telling the stories. So many interesting things going on with Foresight and with what you guys are doing. If people are interested in Foresight Sports, your product or maybe a career, where can they find out more? **Kody Peterson:** Yeah, definitely catch me on LinkedIn. I post there pretty regularly. We've got articles that share insights into what we're doing, and love to connect through that. And then, if you're looking for a golf launch monitor, foresightsports.com is a great place to go ahead and pick up one of those. **David DeRemer:** Wonderful. Thank you so much for the time. **Kody Peterson:** Yep, thank you. **David DeRemer:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review, and if you want to get our latest episodes, don't forget to subscribe. Thanks again, and see you next time. ### Enterprise-Scale Flutter Architecture at SoFi Source: https://verygood.ventures/podcasts/phil-rabin-sofi--enterprise-scale-flutter-modernizing-architecture-for-a-2-million-line-flutter-codebase/ Phil Rabin, Director of Engineering, Mobile at SoFi, joins us to break down how enterprises scale large Flutter teams, manage rapidly expanding codebases, and design mobile platforms that support continuous growth. Drawing on a decade at Uber and his leadership at SoFi, Phil shares practical lessons on documentation, tooling, team alignment, and maintaining engineering velocity as systems become more complex. {% audio_embed url="https://player.cohostpodcasting.com/dd72cb24-32a8-413f-ab43-c1c4298c6812/05322696-02b7-4c89-82e3-4949633a7187" /%} ## Transcript **David DeRemer:** Hi, I am David and this is Build to Succeed from Very Good Ventures. Today we meet with Phil Rabin, director of mobile engineering at SoFi. We discuss what it's like to lead a very large Flutter team, navigate complexity and platform engineering and how to leverage AI and automation for better developer productivity. It's a great conversation and I hope you enjoy it. So, here's Phil. Phil, welcome to the podcast. **Phil Rabin:** Hi. Thanks for having me. **David DeRemer:** Thank you for spending some time with us. I thought maybe to get us started, I wanted to ask you a question maybe about something that you do outside of work that helps you be a better leader at work. **Phil Rabin:** Absolutely, yeah, so I am a podcast fiend. I listen to a lot of podcasts. I pretty much always have a podcast on if I'm doing dishes or working out or riding my bike or something I love, having a constant stream of information and keeping up to date what's happening in industry. I've got a bunch of favourite podcasts I like to listen to. I love kind of the more techie business podcasts like Lex Fridman, Scott Galloway, Diary Of A CEO, Tim Ferriss. If there's something to learn, I'm all over it. That's awesome. **David DeRemer:** Yeah. Well, you're in a good spot for that then. So, we, you'll get to listen to us talking pretty soon. Why don't you kick us off by telling us a little bit about your journey? I know you're at SoFi now. You've worked at Uber. Catch people up to where you started to how you got to where you're today. **Phil Rabin:** Sure. I mean, I'm originally from Canada. I was living in Vancouver. Well, I actually started coding when I was about 15 years old back in the nineties. I was kind of an early adopter of web technology. Surprisingly, I was probably one of the first, probably the only people in the entire high school that was really into computers at the time and not just computer games but actually building websites. So, after college started at the CBC, which is the Canadian Broadcasting Corporation. Always like to say it's the BBC for Canada for people who don't know what it is. And I got an early taste of building things that people actually like. We built a pretty popular digital music streaming service called CBC Radio 3. It was kind the backbone of indie music in Canada. It was really exciting. We were kind of SoundCloud before SoundCloud. We were peer-to-peer social music streaming platform. I remember even the SoundCloud came out after us. We actually worked innovating on a lot of the live-streaming interactive stuff early on and then I worked at another tech company after that and then did my way to San Francisco and worked at a startup, went under. Classic story. And then very shortly after that joined Uber and I was one of the first engineers, the first 75 and one of the first mobile engineers at Uber as well. So, I joined as a web developer and then learned a mobile pretty much on the job. So, it was a pretty exciting time to be there. As it was one of the hottest startups at the time. We were growing like crazy mobile first and again, I really got a taste for building products that people like to use and I thought that's kind of cool. It's like being in a band that has a hit song and then it grew with the company. Rapid growth, staying in place took a lot of effort and to actually grow the company is just, was nonstop. You're eating, breathing, living coding, working all the time. That was kind of the culture at the time, but I really got a taste of having the scale. Everything was about scale there, grow, grow, grow, grow, grow. Code bases growing, engineering teams growing. After about three years I moved into management and started the what's called the Rider platform team, which is a mobile platform team for the Rides app and grew that group. I had three teams in three different cities and then as time went on I actually switched over and worked on mobility, which those jump bikes and scooters. Then after that and went started the Eats platform team and moved all in eventually I was there for about 10 years in total and so we saw it from about a 300 person company where I joined to like 30,000. I had to grow through all the phases of it from startup to large corporate multinational. So, that's pretty cool to go through all the different phases. Usually people turn over after a few years you get different types of people that come through the door, but I had learned those different phases and it was awesome and then ended up at SoFi after that. **David DeRemer:** What an incredible moment in time to be part of front end user experience for a company like Uber. I mean if you think about where you started relative, all of a sudden you're doing scooters and stuff and Eats, which probably if you went back to the first couple of years you were there, it probably would've been wild to think about where you would end up. And I think Uber for me always was a classic example of somebody who was really doing mobile. One of the things I always loved about Uber was so many companies want to keep you in the app, how much time spent in the app and I always loved the Uber design aesthetic of no, open the app, let me do the thing and actually get me in the car. I don't want to be glued to this thing. I wanted to be super efficient and I always really respected Uber's design, UX and technology from that perspective. **Phil Rabin:** We cared a lot about that, about compressing the time, it's time to cap, time to ride. Everything was about speed and efficiency. How fast can we get the app to launch, how quickly can we get you the ride? Can we keep shortening that and making the network more efficient, making everything more efficient and because it just drove more rides. But yeah, it's definitely an app first experience. **David DeRemer:** Awesome. And through, as you were saying in that story, you went through this journey of being a member of the team into a more engineering leadership and management role over time. How did that happen for you and what were some of the kind of key pivotal moments or key lessons learned that helped you grow into becoming the engineering leader you are today? **Phil Rabin:** Yeah, I actually was a manager before when I was living in Vancouver, so I did get some training and I got a taste it. I think generally I had a natural inclination to leading the team. I recognised that early on and it offered for me to grow a new team and I was just in my late twenties. I thought it was a little bit early at the time. I wasn't ready to get off the keyboard and just be in meetings all day. So, I actually went back to IC, moved to San Francisco, kind of retooled out of the dot net world and into more of the open source stuff and I think that was good. I think I needed more maturity before moving into management and encouraging anyone who's looking for management. It's okay to be in IC for a while, sit and not build that respect of your peers, really learn your stuff, ship products, learn how the workflow works. And then at Uber what was interesting is that there was so much growth and turnover in my first few years that I ended up having about six different managers in the first three years it was a little chaotic that managers were coming and going so much and it was unstable. So, in that period I just kind of took over the team. I just started running scrum, running our process, running planning, and a manager would pop in and maybe last for a few months and then we wanted the next thing. And so I was kind of like that stable person in the team that was just tech leading and mentoring people because we just needed it. And so what ended up happening is that management was like, "Hey, it seems like you're already doing the job. Why don't you just start this team?" Because I've been proposing that we probably need a platform team. We need someone who's just going to be dealing with the overall app architecture and a lot of the tooling and bringing in new libraries, working with our central, we had a mobile platform team that was central but no real platform team was dedicated to our app because Rider app is just an operating system. It's just so huge hundreds of engineers working on it, but you needed a team that's coordinating all that and so they offered for me to start that team. And so it was just kind of slipped right into it. I pulled over a bunch of my peers and I'd already been growing the respect of the team and working a lot of on the foundations of the app. I was working on architecture and foundational pieces anyway, so it was just a continuation what I was doing. **David DeRemer:** Nice. That's awesome. Now let's talk about SoFi, right? When did you move to SoFi and I know you've undergone a similar massive scale effort there, a stepping up and sounds like more scale in the future. What got you over to SoFi? What was the work like when you started and how has it evolved? **Phil Rabin:** Yeah, so after Uber I took some time off. I think it was well deserved 10 years of grinding it out. I think I was ready for a break, so I took about five months off and discovered something about myself that I actually got bored pretty quick and I wasn't really ready to retire and I was just seeing the AI boom starting to happen. I was like, "I got to get back on the field. It is too exciting. There's too much stuff happening. I'm not meant to be just a stay home guy on my phone or just even just going and playing outside." That kind of runs out of excitement pretty quick. So, I was looking around for different opportunities. I was definitely looking for that director scope. I've been essentially doing that level of work at Uber. The Rides organisation for example was a thousand people and we've probably had about a hundred mobile engineers and I was leading platform work for that, so I was looking for something maybe similar size or bigger or something that would become bigger and it just kind of threw myself out there and it was interviewing and networking and then the SoFi opportunity came around and it just checked all the boxes for me. It was like I had a laundry list of things that I was like to help me eliminate opportunities, too small of a company. I didn't really want to go back to startup. I didn't want to go work at a company about Google size or a meta or something. So, I was looking for that sweet spot of a growth company that was maybe struggling on the scaling phase. And so that felt like that's where my skills could be best applied was the actual scaling part. Let's go from that intermediate size where getting out of the startup phase to big company, big code bases, large teams, I'd done that a couple of times at Uber on the Ride side and the Eat side, so I think I had to cut my teeth and understood some things about scale teams and so when SoFi came around, the growth story was really compelling. It's consumer brand, it's mobile first. The mobile first bank. We had kind of a nice product and then just as we were talking, I was talking to my director Kevin, or senior director, and he was talking about some of the needs to scale the team and scale the code base and that we really want to be a top 10 financial institution and build a financial super app. And I was like, this seems really exciting. This is a cool opportunity. So, I took it and I've been very happy. **David DeRemer:** Yeah, I'm curious about the scaling side of it. I think a lot of engineers are out there out of different spots, small startups, big companies where they maybe already scaled. When you talk about scaling, what are the unique things that you're really focused on? You mentioned team, you mentioned kind of code base, some things like architecture. When you think about scaling up a system or maybe when you came into SoFi, what were the top three or four or five areas of focus you really put energy into? **Phil Rabin:** Yeah, so when I think about scale, what worked for you as a small shop or a small code base starts to break down, that's typically what happens, right? You can build a product and a Ruby on Rails back end, for example, one database, one server and a web app on top. Great. That gets you started. What happens though when you have a million users or 10 million users, all those assumptions that you made about how many connections you can maintain or how big your database is getting, right? You start having to start your database, start producing microservices, those concepts, although mobile apps are a little bit different, but so those ideas you need to apply different types of thinking. So, what happens when you have a hundred or 200 people working at one code base at the same time? All those assumptions about your module structure and your build start to break down. Even things like indexing the code base start to fail. Your CI, right? Maybe you ran a couple nodes on your CI, now you've got potentially hundreds and hundreds of merge requests coming in a day, maybe 500,000. Your test runs starts slowing down and now you're starting getting a lot of engineers, I call them busy intersections start forming where you have potentially, you have your one big file that had maybe a couple thousand lines of code. Now it's reaching 5, 6, 7, 8, 9,000 lines of code and the complexity is starting to bog the teams down. You don't have a design system so now your app looks different everywhere and you start having to have struggle managing that. So, those are types of scaling challenges that you start to see and we have to address those as we go through and start planning and making assumptions like, hey, we're at 110 engineers right now. We're probably going to have 200 by next year. What does that look like? You have to keep capacity planning and start looking at all of your pipelines and planning to try to speed up development time, speed up onboarding, speed up your CI runs and you're looking for efficiencies across your entire pipeline because really you're generating a giant code factory and that code has to go out every single week. So, the automation across every part of your stack was even just pushing code out becomes a big effort. Getting everything tested is a big effort. So, all those things need to scale up. **David DeRemer:** So, that's super interesting because when you talk about problems of scale, and I know SoFi now is one of the largest Flutter companies out there in terms of number of Flutter engineers and complexity of the code base, number of lines of code. How have you brought that sort of scaling mentality to a Flutter code base, which is a relatively new technology, maybe not as many engineers, maybe not as many best practises and standards and tools and things? Tell us a little bit about that experience. **Phil Rabin:** So, when first of all, the decision to use Flutter was made before my entry into the company. I think it's an awesome decision. I mean truly now that I've been working with it, I'm like ecstatic, I would promote it to more companies. I think especially seeing at Uber, just the Herculean effort to get two apps out constantly and just the pipelines and just dealing with the feature set and trying to keep the feature set in sync with each other, having to hire, constantly be trying to keep our levels of engineers balanced because sometimes someone quits or leaves the company or whatever. We over hire in one area and now we've got three iOS and one Android or vice versa on a team, and now suddenly the iOS roadmap starts to accelerate ahead and the Android roadmap falls behind. What we saw also was an Android development usually just took longer. You just had the more devices to test against, there's more fragmentation, getting UI to be really look perfect. It was really, really difficult when you're supporting old versions of Android, newer things. Where on Flutter, I mean it's pixel perfect. I mean across all the devices we rarely have a device specific rendering issue. It's almost never. It's just not what happens because the render engine ships with the binary. So, we can have a lot of fine control over how layout works. And then coming into SoFi though, I mean we really were just a few years ago kind of like a startup. Most of the hirings happened in the past couple of years, and so the team maybe wasn't totally prepared for how quickly we grew. And so there really were the foundations in place for what I call an institution. We had a bunch of engineers writing code pretty much converting from Android and iOS engineering to Flutter engineering learning on the go. So, really didn't have a lot of training, so not really consistent practises and the information about how to do things the right way, it really wasn't published anywhere. So, as you start to scale, you really can't have a bottleneck. You don't want to have your team bottleneck on single engineers. When I first joined, I'm like, where do you get your information? I just surveyed people. I started talking around the organisation and they all pointed to one guy, they're like, that guy's got all the information in his head. So, it was kind of like a six-month effort to try to dump what was in his head into documentation. We had hundreds of pages of archive or out of date information that had to archive because it was confusing people. So, you got to think about really, really keeping your written documentation and what you want out of your engineers and your values and your principles, your coding guidelines and practises and process. All of that needs to be written. That's just like the first thing you have to do if you're going to scale any organization. And I think even small teams should be doing it because something that's almost a constant is that new people are going to be coming in at your word all the time. People are leaving, even if your head count's flat, you have to assume that maybe 10 to 20% of your workforce is going to be turning over every single year. And those are new people you've got to train over and over again and you might get different outcomes depending on what team they land on. If you don't have anything written, you don't have good training in place. So, that's absolutely critical. And then in general, I'm thinking about creating a really resilient culture. So, it's kind of steering the culture away from just only moving fast, but towards quality, towards changing the values of the organisation, the mobile organisation as a whole, really focusing on testing, really focusing on good architecture, solid code reviewing, those things that you take for granted. Maybe some people care about it but other people don't. Some teams really aren't enforcing the practises. We all have to live in one code base, which is different than maybe backend services. You have these little microservices and you get to federate, but in mobile, everyone's living in the same soup, so you've got to make sure that everybody's following best practises because one code, one line of code can basically take down grep. So, we're really trying to enforce feature flagging. We've been having test coverage requirements and now bringing in new design systems, trying to get consistent UI. So, all those things you start bringing to the organisation, you don't get it for free. You got to build that stuff out and it's a lot of work to ramp up. And then once you invest in it though, then it's basically repeatable. You get consistent outcomes on every team. You should be able to go to every single part of the code base and it should look relatively the same. That's the long-term goal here that I shouldn't have one team doing really, really high quality work and the other team maybe slurring on the go and not necessarily knowing what they should be doing. **David DeRemer:** I love that. Because I think so many people focus on the tools or the frameworks and they get these sort of religious wars around what type of framework to use or language or state management package or whatever it is. And I think so much of it is, yeah, are we aligned on values? Are we going to do things consistently and have consistent standards that we're going to all hold ourselves accountable to? **Phil Rabin:** Yeah, and I think that's why having it written in a central place means if people can argue over the written outcome, you can say we're going to change the policy or we're going to change the process, we're going to change that rule, fine, that's great, that's good, but at least we have a rule and we have some process and that anyone can point to. And what I found over time is one of my rules for my team was don't respond to a Slack message without a Wiki link. And that rule is really good, because it means am I responding with documentation or am I just kind of responding to this question? And if you respond with documentation, totally changes your posture. You'd be like, well, what is the right way to do things and where are we going to put it? How are we going to format it? And then the next time the question comes up, you don't have to answer that question again because here's the documentation, here's documentation. And so that's something I saw over time too is that as we started really investing in the process and policies and documentation that engineers were sharing those links and short-circuiting discussions in Slack, so it's actually more efficient. Because you don't have 300 line message, 300 messages in a row of people all arguing and talking about something. We get out of that Slack message and get into the Wiki so then we can all just coalesce around that and then share the link and then next time, share the link and then that question never get answered again. **David DeRemer:** And a big thing here has been scalability, this big part of your career, the things that you've learned and grown, and again, kind of putting the emphasis in places where we as people doing these things, we kind of focus on these false things to look at, which is like, oh, how fast did it compile? Or the API response was 50 milliseconds instead of 500 or whatever. But you've talked a lot about teams and I think so much of scale is the team itself high performing? And that's a lot of times not even a technology influenced thing at all. It's like, do we have the right standards? How do we operate? So, it sounds like really investing in teams and structures has been a key part of your management philosophy. **Phil Rabin:** Absolutely. I'll say code, anyone can write code and in fact, code is becoming free to write. It's like what do we do? How do we do it? Is it understandable? Are we all on the same page? Are we working well together? Is the energy good? I think about just negative energy can really just suck, take the wind out of someone's sails, they become less productive, they don't want to come to work. It's a drag. So, it's really making sure that the morale continues to be high, that people stay focused, that they work well together, that there's even personal conflicts are weeded out early and that's usually improved with good clarity, with good process and systems and clear lines of ownership. You make sure that if you're the tech lead, that you're the final decision maker. So, I'm going to create a structure and make sure that hey, you're the director, responsible individual on this. I'm going to go to you for questions and if there's maybe an engineer or two working under you, I'm not going to undermine you and go to the person who's, call it, reporting to kind of the project. I'm going to go always to the tech lead to make sure that the decision making flows in one direction. And so I think those types of things is give people more confidence that they're doing the right thing, that they know who to talk to, that if there's a disagreement we can have a really clear chain of escalation. I think that's the stuff that can sink an engineering organisation. Again, the code, anyone can write code, you can go to a bootcamp and learn how to write code, especially the AI tools, just where we are cranking out code, that's usually not the bottleneck now. **David DeRemer:** I know you're scaling and modernising a very large code base with a lot of people. You mentioned that you'll be hiring growing that team quite a bit, and I think your number of lines of code in one of our early conversations you're talking about that's growing on an increasingly accelerated path. Can you dig into that a little bit? Maybe give us how big is this at SoFi? How big is this application? **Phil Rabin:** Yeah, so in the first couple years of rewriting the Flutter app, we added probably about a million lines of code and then just last year alone we added another million and then we added about 500,000 lines just in the last quarter. So, we're looking at a pretty steep curve, AI tools and just the growth of the engineering team combined is starting to really make a steep increase. So, the current rate, if nothing changes, just our current rate will be around probably another doubling next year, but 400,000 lines of code, so just the weight of the code is starting to get heavy. You're dealing with even just switching branches and dealing with Codejet and dealing with indexing and builds are returning to we're trying to feel that drag and it's not going to get better unless you really invest in it. We've been throwing hardware at the problem just temporarily and I mean every single person in the entire company got brand new like M4 Maxs just because we were able to show some data that we can compile faster, we can just generally work faster. So, that was a no-brainer, but hardware can only take you so far before you start having to really start restructuring your code and doing more incremental builds, parallelization and being a little smarter with your resources. **David DeRemer:** With all those lines of code, is that humans? Is that AI? What's driving that acceleration? **Phil Rabin:** Yeah, I mean code is code, right? Some of it is code gen. A lot of it's unit tests. You think about unit test are probably like four or five or six to one of unit test code to application code, and then just your Cursor and Claude are cranking out a lot of code and it's not even that much code. If you think about adding 120,000 lines of code per month, that's only a thousand lines per engineer, 250 a week. I mean Cursor can generate you 500 lines of code in a second, and especially test code, we just put out a new product called SoFi Pay and just a small team was able to write a thousand lines, sorry, a thousand tests for that product and did so, that was over the course of a couple months. So, yeah, it's adding a lot of code faster, so that's something to deal with. **David DeRemer:** Do you think that when you think about the scaling of code, both people and AI enablement that comes with that and the operational, has Flutter helped or hurt or just neutral relative to your past experience or other platforms? **Phil Rabin:** For sure helped. I mean just having one language that we, one language, one framework, one code base to deal with means that we don't have to split our effort, be like, okay, here's all of our AI rules for iOS, here's all of our AI rules for Android. We get to really just as a new organisation, invest in one set of tools, one code base, one set of training, one best practise, everything. So, Flutter has made it just dead easy comparatively. I kind of cringe at now the thought of having to maintain two independent platform teams and two sets of code bases, everything, and then try to keep those in balance. Just be way more of a headache these days. **David DeRemer:** And yet so many people are still doing it. **Phil Rabin:** That's right. Yeah, yeah, they're invested, they're deep into it, but I think rewriting with AI tools now is also much easier. **David DeRemer:** And do you have any concerns about scalability in terms of third party tools or anything about the engine or the framework that could keep up with that as you go forward? **Phil Rabin:** I think what's hard with AI tools is just getting it to do the right thing, the hallucinations, just the correctness. We put a lot of effort into building up a lot of our markdown files with dos and don'ts, best practises to coax the agents to do the right thing. As your code-based scales Cursor, really you think about it, what the secret sauce of Cursor is \[inaudible 00:27:04\] and a coordination layer across your code base to try to reduce the context window so it can actually talk to agents because you can't actually put your whole code base in the context window for an agent. So, you have to try to reduce the context window as much as possible and operate on small chunks of code. We've looked at some vendors that they claim they can work on much, much larger code bases, so there's some opportunity there for us, I think with the tool set to try to index the entire code base, there's some vendors out there, but generally that's the hard part with AI, it's just getting it to do the right thing. Really great at the function scope, but I seen say refactor all of my code base, you just can't do it. Not today at least. **David DeRemer:** Yeah. When you think about the next level of scale for you guys, what are the major hot items you're thinking about? Is it architectural, testing, automation? What are the main things you're focused on? **Phil Rabin:** For us next year ... The way that the app was set up was it followed Flutter best practises for the time and was probably a good layout. If you're dealing with a small app, you probably would've been fine. I mean I'm sure lots of small companies would never outgrow that, but most of the code is in one package. It's well organised within the directory structure itself into features, libraries, that kind of stuff. But it just means that that one package keeps growing and growing and growing and growing. So, we've actually started an effort, we call it internally, call it Project Athens, but it's a move to modularize the code base. So, extracting features, extracting libraries, having service packages and networking packages, trying to organise this app and then bringing in Riverpod. So, we did an analysis of all sorts of different architectures. We had a working group that was building prototypes, looking at all the frameworks out there, and then we landed on Riverpod after we thought was some really, really good demos and it did everything we wanted. So, we wanted real dependency management. I think we wanted to use the providers, we wanted to have scopes and actual structures and we wanted to be able to do dependency inversion for the packages and then actually have better unit tests. So, I think one of the big things would be maybe a miss on the early architecture was bringing in service locator, which is get it, it's fine when you're small, but having singletons of singletons of singletons throughout the entire code base actually starts to break down as you have really big code base. So, we end up with things like flaky unit tests. Unit shouldn't be flaky, they're only flaky if you're mixing state globally. And we try to shard our test runs across a lot of different instances because they're starting to slow down. And what happens is that when you're running your test and you made some assumptions about global state and you run it on your machine, sure it works. Then suddenly when you start sharding and that global state actually gets mutated or you have synchronisation issues, now you've got tests that are doing weird behaviours. So, we're trying to get away from that and move towards a real dependency injection, construction injection and then start breaking down these packages. So, that's going to be a big effort lifting and shifting and then moving our dependencies out of the service like here, haven't you vended from the call it components or DI graph? **David DeRemer:** That's super cool. Hopefully as you guys land on that and get some best practise in place, I'm sure a lot of people will be interested to see where you land. That modularization of such a large complicated code base. We're getting to some point in Flutter's evolution, we're starting to see companies achieve that or get to that need, and I think that's sort of this uncharted territory of best practises and opportunities. **Phil Rabin:** Yeah, we did it at Uber in the early days. The first version of the app suffer the exact same fate. I mean I've seen this and say we've been around the block a few times where we had one package and the builds and we had a similar growth story, but we're plugging up more engineers working on one app than we did even at Uber on the Rides app and we saw the packages in the build times just to try to explode and grow and then we ended up trying to do it while we were running the app. That was quite difficult. And then we ended up just doing a whole rewrite and that gave us the kind of a clean slate to bring modules and package structures and all that stuff. We ended up with about a couple hundred modules per app, so no stranger to large architectures. I mean you can get to a certain size and that becomes a problem unto itself. Your tool chain starts to break down even at certain scale. I don't think we're going to hit that scaling problem for a while if we start modularizing the app correctly, I would assume we'd probably end up with probably 20 or 30 packages by next year, which just totally maintainable. **David DeRemer:** Right. One of the things, there's this idea of Conway's Law, which is the shape of your product starts to mimic the shape of your team. I'm curious how your team is organised and how you deploy people within the organisation, especially since you are doing Flutter, where the most natural first cut is like iOS or Android, you don't really have that problem and potentially as you have multiple apps running Flutter, people become pretty fungible. What is the sort of organisational structure you guys are pursuing or might look to move to in the future? **Phil Rabin:** We follow a matrix organisation, much like a lot of bigger companies, business units or business verticals and all those business verticals are going to be building their own features and then committing code to the one trunk essentially of the code base. We use feature flagging a lot to enable that so that we don't necessarily have teams breaking other teams code as they're rolling out these features. Then we have a big platform team I think right now with contractors and probably you had about 23 people, and so I think as you start to scale and you have vertical teams and you've got platform teams, you really have to invest in them. So, I think you really can't get to this scale without having a really strong platform team. And the goal there is to make product engineering fast and easy. You want the product teams to be able to crank out new features, get products to market really fast, iterate on UIs, find the product \[inaudible 00:33:24\], onboard new users, I mean, you should be experimental. You should be able to slap together a UI quickly like Legos, get it out there, try it out, doesn't work, shut it down, we'll try again. That's what we want to see out of product teams, but you've got to provide the tooling to unlock that. So, you really absolute need to have really talented, usually far more senior level. I've got a lot of staff engineers. I work with principal engineer, so I try to skew the seniority of the platform teams to far more senior people have worked on big apps, worked in big teams and understand how to scale stuff. **David DeRemer:** That's awesome. As you clearly have seen quite a number of amazing experiences in terms of building teams both at Uber and going through that whole journey. And SoFi you're on this incredible upward trajectory. Is there a leadership principle or two you've learned along the lines or along the way that you would share with either up and coming person or someone in your shoes? What is the takeaway that you have discovered that has really helped you lead? **Phil Rabin:** Yeah, I mean generally I really like the principle of radical candour where you're, you care deeply, you're giving them a lot of feedback and I like to be deeply involved in people's careers and make sure that they're growing. I really want people to be right on that edge of comfort. You're kind of out of your comfort zone. You're kind of in that state of flow where you're not pushed so far off the deep end. You don't know how to swim. Maybe the waves come crashing down on you. We'd rather have you do this on that edge where you're learning, you're growing, you're working towards the next level or constantly checking in, making sure that you are moving in the right direction. I really don't want to have people be stagnant just hanging out in their level for 10 years, whatever. I'd rather have people always be working towards the next level, strong encouragement that we're working towards that next promotion, figuring out working our way backwards. Well, what do it take to get there? And I think when you provide that for engineers to get really motivated to really, they think that, "Hey, this person believes in me, there's a path to move forward. I'm given those opportunities. I can step up and grow into that opportunity." I think that's motivating and exciting for people and they get motivated to come work. Because they know that there's a path for them to grow, and I think every single person on the team should be given that opportunity. **David DeRemer:** That's amazing. That's wonderful. That's great, great leadership advice. One final question about Flutter specifically. Obviously given our role in BGV and being deeply in the Flutter ecosystem, what advice or comments would you have for somebody who's kind of out there sitting on the sidelines, looking at Flutter and being like, "I don't know, native seems the pure way to do it?" Given that you've seen both, I mean you have I think a very credible and that you've led very large teams both at Uber, which is one of the arguably most complicated mobile apps or teams that are out there and now SoFi. What kind of guidance or advice would you give to someone who's evaluating that decision? **Phil Rabin:** I mean, it's never been easy to learn new stuff. Even most engineers that we hire are Android and iOS because it's actually very hard to find staff level or senior level Flutter engineers. Because it's so new most folks have either been tinkering with it or worked on small apps, maybe as a hobbyist or something. So, we have to train people, we have to teach them. And so most people pick it up pretty easily. You'd be surprised, even web developers because Dart is pretty much JavaScript anyway, and the documentation out there is great, but also Cursor in the agent is conversational. You can be like, teach me stuff. So, I feel like a lot of engineers with Cursor have been learning. There's a back and forth with the agents, kind like a coach. We're hoping to invest in that more. We'd like to see more coaching come out of the agents, but for anyone who's interested, I mean, pick it up. Just get your hands on it. Get your hands dirty. Try to get an app out there. If your friends have a business or a band or something, you try to build something. That's how it always happens. And then maybe build up a portfolio, but also assume that we tend to hire folks with mobile experience, not necessarily Flutter experience. So, don't be shy to apply if you don't have Flutter experience, because we'll give you the time that you can learn, and we've got an awesome community of support, lots of support to help people out. **David DeRemer:** Super important insight. We hear from a lot of enterprise companies sort of this concern about can I hire senior level expert Flutter engineers? Well, it's really only been around for seven years in production in major ways. So, maybe there are some people out there that have seven years of super high-end complicated experience, but there's so few opportunities for that. But I think exactly what you're saying, it's harder I think to get the senior engineer skill set more generally, and then you can learn Flutter, like let's get that, versus requiring people to achieve that level of expertise. Only using Flutter this time is probably. **Phil Rabin:** And I think also you have to consider what are skills that are easy to acquire versus skills that are hard to acquire. Learning a new technology is actually pretty easy to acquire. You can pick a new framework, you could pick up a new language, you could learn a new language in a week or two. It's usually not that hard once you've been coding for a while. The skills that are hard to acquire are things like organisational skills, good communication, teamwork, project management, deeper understanding of architecture of people who can interview other engineers, those types of skills. Can you grow, can you mentor others? All those soft skills are actually what are probably the most valuable, and that the coding part, yeah, people can learn how to code, but for a staff level engineer, principal engineer, we're talking 10, 15 years of experience of working in software. And for most engineers, software is going to come and go. Different frameworks are coming and going constantly. So, usually you have to retool every 3, 4, 5 years anyway. So, I think it's just part of the course for anyone who's been in the biz a long time. **David DeRemer:** That's great. One more question for you just to wrap us up. **Phil Rabin:** Sure. **David DeRemer:** You've been through so many experiences already. You went through Uber and even your prior roles that you had, now SoFi, probably a lot more ahead, right? A lot more experiences ahead. If you could look into the future and think about one day you write a book or something about your professional experience, what do you think you would title and what would it be about? **Phil Rabin:** Yeah, I think I wrote this down just in preparation. This is one of my mantras, and I think this would be if I was writing a book on leadership or business, it would be just called Treating People Right, and then How to Motivate Teams without Burning Out. I really think that if people are aligned and that you create good opportunities, they love working hard, people love doing hard things. It's usually harder things where you're not aligned that burn you out. You're not really excited about the work, you're grinding. This is just day in, day out of doing things you don't want to do. But if it's exciting, you've got good opportunities. You like your team, you want to show up to work, you want to put in those extra hours because you're passionate about it and you're having fun and you're shipping things and you're growing as an individual. So, I think that if you find the right way to motivate people, they won't burn out and they'll actually come to work and want to work harder for you. **David DeRemer:** I love that. Well, thanks so much, Phil. Really appreciate you taking some time out of your busy day. I know you got a lot of people that you're accountable to and not to mention customers and leadership, so I really appreciate this. I mean, the SoFi app has been, I think, a real inspiration to a lot of people out there just seeing what you guys have accomplished, hearing those stats about how many engineers, number of lines of code, and the rapidly accelerating pace of that as we're still building, I think we're still in the early phases of Flutter, and personally, I think Flutter is the perfect language and tool for AI enabled development too. And so it's just been great to see your story, and I just really appreciate you coming on here today. **Phil Rabin:** Cool. Pleasure was all mine. Thank you so much for having me. **David DeRemer:** And if you want to learn about SoFi or if you guys are hiring, where can they find out more? **Phil Rabin:** Absolutely, sofi.com and yeah, we've got a jobs page. We're definitely hiring, and we'd love to have folks who have Flutter experience or just mobile experience in general. **David DeRemer:** Amazing. Well, thanks so much. Really appreciate your time. **Phil Rabin:** Thank you. **David DeRemer:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review, and if you want to get our latest episodes, don't forget to subscribe. Thanks again and see you next time. ### Scaling Flutter DevOps and CI/CD With Community Source: https://verygood.ventures/podcasts/martin-remmelgas-codemagic----the-power-of-community-in-scaling-devops-and-ci-cd-for-flutter/ In this episode, we sit down with Martin Remmelgas, CEO at Codemagic, to explore how his background in chemistry, a passion for technology, and a relentless focus on community helped shape one of the most beloved tools in the Flutter ecosystem. Martin shares Codemagic’s evolution from an internal experiment at Nevercode into a CI/CD platform trusted by startups and enterprises alike. He breaks down what truly drives adoption of developer tools, why credibility matters more than marketing, and how community relationships ultimately shape product strategy. {% audio_embed url="https://player.cohostpodcasting.com/38d47dcd-6dcf-43bd-93c9-ddfee7291ac1/b1387065-d1f5-4ad6-bf7a-304b3f5af654" /%} ## Transcript **David DeRemer:** Hi, I'm David and this is Build to Succeed from Very Good Ventures. Today, we're joined by Martin Remmelgas, CEO of Codemagic, a company deeply embedded in the Flutter ecosystem. Martin's international background and entrepreneurial journey have shaped a unique approach to building developer tools at scale, especially for startups, but now increasingly for enterprises. I hope you enjoy our conversation. Hi, Martin. Thanks so much for coming on Build to Succeed today. **Martin Remmelgas:** Thank you, David. It's a pleasure to be here. **David DeRemer:** Well, you and I have known each other for quite a long time now, going back to the very early days of Flutter. You were super early in the Flutter ecosystem. I thought maybe a way to get started, I think you have a pretty interesting backstory, like your personal journey, all the different places you've lived. I know you're in Estonia now and grew up in Estonia. Maybe to get us started, can you just give us a little bit about that story personally of how you got to where you are today? **Martin Remmelgas:** Yeah, hi everyone. I grew up in Estonia and I wanted to become a chemist. So, I went to study in Edinburgh in Scotland. During my studies, I earned my money by doing door-to-door sales in America, so I got a chance to work in Montana and like Butte and Billings, also in Indiana, in Bloomington, for example, around that area and in Texas as well, south of San Antonio in the desert. And then, in my final years of studies, or one year before the final year, I was in Singapore doing my industrial placement for a year. And turns out, I didn't want to become a chemist anymore. I wanted to change the careers. I saw some guys building startups and I also saw what an expert chemist looks like or where is this train going that I am on at the moment, and I decided maybe it's time to change trains or make a stop and to do something different. **David DeRemer:** What was it about technology that really caught your interest? **Martin Remmelgas:** I had a dream of building a research lab in chemistry, but that takes 10 or 20 years to do. And technology, you don't have stack traces in chemistry. You can't get error messages if something goes wrong, right? But technology, it allows you to build something quite quickly compared to anything else. So that attracted me. If you have an idea, you can actually do something about it and implement it, whereas if you consider chemistry like construction work or something else like that, you require some capital, but everybody can start the technology company. **David DeRemer:** Well, and you also were telling me that about this door-to-door salesman thing that you had, and I'm curious whether that gave you any desire for speed and moving quickly because I know that you got the door slammed on you quite a few times in your door-to-door sales. How did that influence your career decisions and you, as a professional? **Martin Remmelgas:** Sales and door-to-door sales, it teaches you a lot. The most that it teaches you is that you'll be surprised what you will get if you always expect the best. In the beginning, if you're starting out trying to sell something, you don't know what you're doing and it doesn't work out and then suddenly, it does and you'll be surprised. You can work in low income areas and succeed and you can work in high income areas and fail, and you're thinking to yourself, maybe these guys don't have money or I don't know. There are small things that you can do that make all the difference in the world. And having this performance metrics like working hard, paying attention to the details and just not giving up and always trying to do better, it does translate to results, and it gives you a confidence in whatever you do in life that if you work hard and you're always wanting to get better, then it's like this belief that there is a light at the end of the tunnel that you will get there. So, that's what it helped me with. **David DeRemer:** Nice. It sounds like you learned some lessons early on in your career about speed, being able to move quickly and that you can't wait for that 10, 20-year cycle persistence and just working through both failure and success and a lot of that, I think, comes, you see it in your products actually. I think in terms of helping developers move more quickly, learn from what they're doing and et cetera, et cetera. So, why don't you tell us a little bit about the story of how you got to Codemagic, which is the product you're leading today, I know in Nevercode more broadly. Tell us that story arc. How did you get that started and then what was the journey to get to Codemagic as we know it today? **Martin Remmelgas:** Back in university, when I was in Singapore, so when I was doing my industrial placement then, I decided let's try this startup thing. So, in the summer when I came back to Estonia, I had some ideas what I wanted to build. I tried to recruit the team and we tried a startup idea, which failed. But one of the mentors at the time was CTO at Nevercode, which was the company behind Codemagic. And after my studies I was working in Ernst & Young and then, he invited me to join Nevercode or Codemagic at the time. And that was at the inception of this, so before Flutter Live or Flutter 1.0 release, I joined right before that and Codemagic itself, it used to be a development agency quite similar to Very Good Ventures, for example, but this agency, it did an internal hackathon in 2012. And back then, if you recall, Hudson or Jenkins was the norm. Yeah, it was painful to manage some CI pipelines, like a trial manifesto has been created and people were trying Scrum and all that stuff and managing one of the operations and processes becomes a full-time job to somebody. So, they started this idea of can we productize our knowledge and our operations to build the product out of all of this DevOps work that we're doing inside the company and sell it to our customers perhaps. So that was the idea behind Greenhouse at the time, which evolved to Nevercode, and then to Codemagic. That's where it all started and I joined right before Codemagic was launched. **David DeRemer:** Wow. So, it came from an internal hackathon and then you pivoted over to just this as a business. And you guys were super early with the Flutter focus and being big members of the Flutter community. I mean, you've been there since day one and that every major Flutter event that's ever happened, there's always been a Codemagic presence. Even going back to, I don't know if you remember the Chrysler Building party, I remember you guys had a section of that and everything and even before events were really events for Flutter. How did that relationship with the Flutter ecosystem come about and what did it do for your team and the growth? **Martin Remmelgas:** It came about before I joined. In the summer of 2018, I believe it was droidcon Berlin that they had this, or I think it was even this Mobile World Congress when they announced the beta, but droidcon Berlin, some of the team members went there and they saw some demos of Flutter. It's like, "Hey, people get excited about this Flutter thing. What if we do something about it?" And so, Nevercode product, back then, it created some hack on how you can build Flutter apps. And our CMO at the time tweeted about this to Tim Sneath, who was the head of Flutter back then. And to our surprise, Tim responded and then we got to work on what is now known as Codemagic. So, we completely built a new product only for Flutter and that's how this relationship came about with Google and Codemagic. **David DeRemer:** Was it about the Flutter ecosystem at that time that made that an exciting opportunity for you? **Martin Remmelgas:** I think the team got excited about it because the technology seemed interesting and cool. So, they were wanting to experiment and try out what is this new thing that is Flutter or Dart and people seemed really excited about this new thing. So then, when you try it out, they realize it's quite different from any other multiplatform framework on the market and the community behind it was very, very welcoming. So, when we created this MVP or this first idea supporting the Flutter community, everybody jumped on board and was interested in joining our private Slack channel, was interested in giving feedback, being these early adopters and supporting us and feeling like, "Yeah, let's do something great for Flutter guys." And obviously, once Google showed interest in supporting us showing that we are doing something for the community and I wanted to see that we can be a strategic partner for companies adopting Flutter so that they have a build partner that can provide CI/CD support for this new framework. This was like, so now you have some big player recognizing you're a small player and then, you have the community getting excited about what you're doing. And so, we decided to go all in and try it if we can make it work. **David DeRemer:** That's amazing. I think you're very similar to VGV in that way, that early on in the community and there's a handful of other companies and also a lot of individuals, I think, that really grew professionally as a result of being early and deeply involved in the Flutter community. When you look back on that strategic choice and maybe it wasn't purposeful, even with VGV in the early days, it wasn't really a purposeful decision of like, "Oh my god, Flutter is going to explode and this is going to make great business sense." It was just more like our team just liked the tool and wanted to use it, kind of what you're getting at, but when you think back and reflect how important was that early engagement and contribution to the community to Codemagic success? **Martin Remmelgas:** It's the only reason we succeeded. Without it, we would be nothing. Nevercode was a good product, that was before Codemagic and it was a good product, people liked it, but we didn't know how to sell it and the product itself was not scalable. So, after a certain threshold the way some architecture decisions were made, the product, and this was a known issue that we couldn't take it too far. One customer may have started to affect other customers, so this was critical for our success and if we didn't have the community to speak on our behalf or advocate for us and we didn't have Google trusting us or also putting bets on their partners, we wouldn't be here today. **David DeRemer:** Let's talk about that community aspect. So obviously, the technology choice getting recognized by Google and I think being early to an ecosystem, it creates opportunity for you to be recognized by a company like Google, whereas if you got into Android or something else, you would be swimming in a sea of tonnes of other companies that are already there. So, the technology choice was helpful, but I know you're also a very strong advocate for community involvement, not just embracing a technology but actually showing up and engaging the community and going to the events, and talking to the community more regularly. What was the provocation or source of the desire to lean into the community in the early days of Codemagic? **Martin Remmelgas:** So, when I joined, I joined to become this business person or be a business development person and had to come up with a plan like now that Flutter is out there, how do we now market ourselves? And we didn't have a big marketing budget either. And so, I tried different things, but when I was, I think, it took me two, three months or something to start going into this community stuff, but I quickly started to message people in the community and ask them, "Why are you using Flutter? Where do you get your information on tools? Where do you go to meetups or where does the community get together?" And then, I realized that there are a lot of these groups of people who are excited about Flutter that meet in different cities, and this was new for me. I hadn't worked in tech for so long and I was like, "Okay, so there are these really big fanatics that come together and start celebrating some technology. Okay, cool. What if I go to some of these meetups and see what they're like?" And then, you go there and you meet the people and they're telling you about the work that they're doing and what they're experimenting with and they're really learning to help you. And then, I started to go more and more to these events because, first of all, I learned a lot, and secondly, you start building a network and with a little budget that we have back then, word of mouth is what I put on my bets on is trying to show up, show that I care, support what they are doing, and learn what they needed so that I can be this a devil back then or listening to what the community wanted and then doing something. And what ended up happening is that you start getting a lot of opportunities. So, when you start keep showing up, people start inviting you suddenly or people start saying or asking you for advice, it's like, "Hey, we're thinking of doing another meetup," or "Hey, we're thinking of building a project," or "Hey, we're thinking of doing a hackathon. Do you guys want to participate?" And now, suddenly, we are somebody. So now, suddenly, people are paying attention to us. And I've always had this belief that if you do good for the community, the community will do good back to you and you just stick to this value, this is a promise that we're going to make. And so far, it's been paying off very well. If we keep investing in the community and because ultimately, all of our customers are Flutter developers and the only customers are Flutter developers, then our success is very closely tied to Flutter's success. So, we'll do our part which is to go to these events, try to support them and listen to their feedback and it's been very, very good. **David DeRemer:** So, when you go to these events, you sponsor meetups, which has a cost obviously, get the booth and everything. You have to travel and pay for people to be there and attend these events, and you, being this visible part of the Flutter ecosystem. How did you, as a business, plan or justify or measure the ROI of that activity, because I think if there's companies maybe sitting on the sidelines of that activity, they're like, "Well, what am I going to get out of it and how do I explain this to my leadership or the budget people?" How did you guys contextualize this and what have you learned in terms of ROI of these activities? **Martin Remmelgas:** In the beginning, there was no justification as such, so there wasn't anything. It was just what can we do, what seemed like a good idea and then started to do it. I think the ROI aspect are justifying investments, what the type of investments we're making today. Back then, first of all, I just showed up to those meetups and I tried to connect people. Let's say somebody wanted to speak at another meetup, so I just brought them two together. So that helped me a lot. But talking about the value that the community has for the business, so how do we justify that? How much should you invest in community activities. When you're investing in ads, so then, you can have a relatively good understanding of your returns. So, that's a marketing spend. Then, you measure the number of sales that you're making. If you increase your marketing spend, do you get more sales? So, that's a fairly straightforward approach. With community, it's a little bit different. So, those dividends start to pay later. So, one thing that's very difficult to measure is brand. So, how do you invest in brand or how much should you invest in brand? So, this is part of a community investment. How do you measure a brand return? It's very difficult to measure these things. It's something that you trust is a good investment and you put money there in the hopes you'll get returns. How I've justified it today is if you're starting out, there is a good chance there is a default choice on the market. Let's say, if you want to build a Flutter app, maybe there used to be a default choice where you're from, what you should choose and choosing anything else is a very bold move. Most people will argue against you. Why would you choose something else? Consider VGV or consider Codemagic. Why are you not choosing GitHub actions or why are you not choosing Jenkins? Everybody in the company has been doing it like that. Why on earth would we change something or why would we try something new? And here is our brand plays a role, if you can say that Codemagic has been with Flutter since the beginning, and if you can make that association in person's brains that Codemagic and Flutter go hand in hand together, it's a match made in heaven, maybe you stand the chance to actually be in consideration of a customer in this case. So having this good brand is important. Another thing that I'm getting feedback on is, or what I find valuable is product and customer feedback. You can build the best product in the world, but if you don't have a community, it's very hard to get people on the call on the phone or let's say you're about to release a new feature, you're sending hundreds of emails and no one... Let's say you put, I don't know, $10 Amazon or $100 Amazon voucher in your email saying like, "Hey, if you spend 15 minutes with me to explain the product or your feedback, I'll give you $100, you enter the raffle of a $100 Amazon voucher," and that already introduces bias. So now, you are getting people replying who care about this thing or maybe there is a wrong motivation here. What really helped us to build a good product was this community because now, we can go and ask feedback from these guys as well that I have made friends with before. It's like a network. And I've used this when, for example, we introduced pricing. So, in the beginning, Codemagic was free. And then, when I was talking about it at some events in the States, people were very skeptical and they asked, "So what's your business model? You're a free product, so what's your business model?" And I was like, "What do you mean? It's a free product. Isn't it something to be happy about?" It's like, "No, I don't trust it." And you wouldn't figure this stuff out unless you actually go out, talk to people and they're feel confident enough, comfortable enough, enough to share it with you. And then, you was like, okay guys, we should probably make some business case for it. Otherwise, people just not going to get it. They're thinking of stealing their data or whatever. Maybe we should explain how we make money. And coming up with our pricing model, it was very helpful. So now, I could actually put these community people that I've met as personas and think like, okay, what are they going to say if I present this to them, and I can actually talk to them and present these ideas to them and get feedback. And that helped us to create their first pricing, for example. Similarly, when we create the major new feature release or product release that allowed to build not only Flutter apps but other apps as well, like native applications, then how do you position that or how do you create your messaging around it that still keeps your existing community but now allows them to bring their other applications on Codemagic as well. So, in that sense as well, this community has been super helpful. Similarly, for hiring, for content writing and many other things, the community has been very good. And how do you measure that? I think the easiest way that I've thought about it is let's say your company has certain goals that you're about to hit, so how much budget are you going to put aside to hit those goals? And community is just part of that budget. If you take it away, then there's a good chance you'll run into some problems. Strategically, you'll find that, "Oh, we couldn't get people on the phone. It's difficult to get user interviews," or I don't know, "We couldn't find content writers. They're difficult to find," or stuff like that. "Oh, we made a bad product decision." So, having this community there is just like a booster to building good products. **David DeRemer:** Amazing. I mean, so many good things in there. It's brand, lead generation, product feedback, all the things that come with community. I think we're in an interesting phase. Those of us that have been around since the very early days of the Flutter ecosystem where it was very small and the community, you could almost know all the major voices, but now you've got all these big gigantic companies also working in Flutter. I'd love for them to hear what you just said around the importance of showing up at these events because it's a way to really engage face-to-face with maybe your potential future hires, customers, relationships you can forge. And I think, that we have a tendency as a business people to be like, "Oh my God, what dollar can I spend and get $2 back and how quickly can it happen?" And these things are longer term investments too, like you're saying, relationships and being able to call those people up when you need the help, and that's super valuable, but hard to put a dollar figure on or something. **Martin Remmelgas:** Yeah. Attending conferences is different. You can sponsor conference events. It's relatively easy to measure the ROI of that. How many leads did you sign up from that event and how many deals did they convert from there? But there is added value to it as well, which is this community part. How do you actually build relationships in this world? **David DeRemer:** Yeah, well, and while you're right, you can track those metrics and how many leads and contacts did you get and deals did you get out of it? But at the same time, I feel like that also can diminish the value if you're only looking at that, how many new clients did I sign up because I sponsored this event? But realistically, I mean, I like the phrases where it depends, some people say 6 months, some people say 12 months, but your current pipeline is based on what you were doing six months or 12 months ago. And I think if you think about the, especially the 12 months, that's a long time in the future before you might start to see the results because you just don't know. Maybe somebody meets you at an event or a meetup and there's nothing happening then, but maybe a year later, they're like, "Oh, now I'm in a situation where I need a solution and maybe I'll call Martin at Codemagic. **Martin Remmelgas:** Exactly. People buy from people they know. It's very difficult to sell something that requires trust to somebody who doesn't know you or doesn't trust you, right? **David DeRemer:** Yeah. **Martin Remmelgas:** It's how do you always stay on top of mind of people? Even if you have the best fridge in the world, you're not going to convince somebody that already has a refrigerator to change it, but maybe you have a friend who's looking for something or maybe you're going to buy a new flat or something like that, then it's like, "Oh, I'll call Martin. He has the good fridge. I met this guy. I like him." **David DeRemer:** Yeah. Let's shift gears a little bit into the changing Flutter ecosystem because we were saying in the early days, it was a lot of enthusiasts, a lot of individual developers. You had Google in there, but I know you're in an interesting spot because you have companies that sign up to use your product and I'm curious how you've seen the Flutter ecosystem change over the years just based on the shape of the type of customer you're getting. Has it changed recently? **Martin Remmelgas:** It has. It has changed significantly, especially in this year, in particular. We've been good at this small businesses and this community stuff where you have people and hobbyists and enthusiasts who are interested in trying your product out. And I've always believed that if you build a good foundation, let people experiment and learn and use your product, and maybe in the future when they go into positions where they can choose the tools they use, that they'll come back to you. Well, today we are definitely seeing that and the types of companies that are coming to use Codemagic. We've got some really big companies now using it. Somebody that we've never thought was possible before, but now they are actually coming themselves and reaching out to us like, "Hey, we're interested in using Codemagic and you help us out?" **David DeRemer:** And these are massive, large enterprise-type brands? **Martin Remmelgas:** Yeah, very big companies. **David DeRemer:** That's cool. **Martin Remmelgas:** This was not the case before, which is the surprising part. It only and in particular, this year is when we are starting to see that. Maybe they were there before as well and only we just got lucky this year, but if you're asking what have we noticed different, then definitely, a lot of enterprise customers and in Flutter specifically, have signed up with Codemagic recently. **David DeRemer:** Do you think that's both a result of the work that you guys have been doing but also a result of where Flutter is in its maturity? Is it both of those things or one or the other, you think? **Martin Remmelgas:** Our success is very much tied to Flutter's success as well? It's hard to pinpoint it. I don't think we've done anything that different. We continue to invest in community activities, we continue to make our products better, but have we done anything massively different in the past couple of years compared to today? I wouldn't say so. I think people we're riding the wave in a way, so you can say that we got a little bit lucky or maybe the efforts that we have been putting in all these years, they're paying off. **David DeRemer:** Well, I mean, I think anecdotally we can see that Flutter's enterprise adoption more broadly has ticked up a lot over the last couple of years. And I think one of the interesting nuances is you see it in your customer data, who's signing up, who's talking to you, who wants to use it? We see it in our client-base and I think more broadly you see it in the community, but it's not quite as visible. One of my hypotheses, there's so many big companies using this technology comfortably and safely that the community is not even aware of. I mean, we find out monthly or even weekly about some new gigantic, highly recognizable company brand that's been using it for years even, and nobody knows. You find some job posts where someone's hiring a senior Flutter engineer or something and you're like, "What?" No one even knew. So, it's definitely been interesting and I think it's cool for that. You're actually seeing it in your data as well. It's not just anecdotal, you're actually seeing it happen. **Martin Remmelgas:** Actually, something, what we have done recently, maybe this is a big reason for all of this, enterprise is paying attention, I think now already for two years, we have been this SOC 2 audit company, so we have this ongoing audit, like the SOC 2 type 2 audit. Previously, we didn't advertise it this much, but this time, we put it on this enterprise page that we have this SOC 2 because some customers, we promised them that we're going to get it and that their contract depended on it. So now, that we have it, we have been getting more interest. So actually, I take it back, maybe we did do something, so maybe this SOC 2 thing has helped us a lot. **David DeRemer:** You're probably right in that regard, in that certain highly-regulated industries, financial services, these types of things, they need that security, that compliance in order to feel comfortable moving forward. And I think that's probably a symptom both of Flutter's maturity and of your maturity as a business where you get to a certain point where all of a sudden, you can actually invest in doing that, because we just went through it as well. We just got it also. And it's a pain. It's not an easy thing to do, and there's all these rules and things you have to set up and audit some things that you have to do, and it seems a little like, "Ugh, why do we have to do this?" But, these bigger companies, these large enterprises, they not only need it, they require it. And so, I think it's also a thing where Flutter itself gets more mature, it gets more safe, and it's a safe choice. Whereas two, three, four or five years ago, Flutter was a very risky choice for an enterprise business, but now I don't think it's seen that way. It's seen as very reliable and something that any company can make, but the fact that you are getting to that point where, two years ago, you went through that step, it unlocks a whole new set of opportunities, not just for Codemagic, but probably for the overall community of saying, "Hey, this is a stable, reliable ecosystem that you can participate in as a business." **Martin Remmelgas:** Mm-hmm. Yeah. And the reports themselves, I think what problem does it solve, right? What problem does this, having this report solved and not having it, it doesn't solve. We are still as secure as we were before. That's not really what this report is for, but buying our product, when previously, if an enterprise customer wanted to use us, they would have to fill in a bunch of forms anyway, but now, if we didn't have this audited report to show, they would've spent months filling in security questionnaire reviews and all those things before they could adopt us. But this is a feature for their risk team, so that they can more easily use us, so that they have some standardized report that they can use. **David DeRemer:** Well, there's a couple of things you're talking about here that are important. I think when we think about digital technologies and building product companies like you guys have built here, building tools for a community of people to grow, there's definitely product and technology choices, but there's also brand and marketing choices around getting involved in community or even process and these SOC 2 type choices, that's not really a product decision. It's not like you've done something in your actual product to make it a new feature or some new thing that it can do, but in choosing to invest your business's resources in doing that, can unlock a new set of opportunities. That being said, are there things in Codemagic that you guys are working on? Where does the tool go next or what new features have you added recently? **Martin Remmelgas:** Yeah, Codemagic, the coolest feature that we added recently is to do with this collaboration between or how you can test your apps easily. If today you had to share a build with somebody, like let's say you're working on a button or let's say you're working on a page and now you want it to show the latest to the designer, then there's a good chance the designer had to instal the new version on their phones and test it, which isn't necessarily bad, but I think we can make it better. And now, Codemagic allows you to have application previews. So, we call this product Stellar, where if Codemagic produces you a build, you can now open it in the browser that's running on a real iOS simulator or an Android emulator, which is really cool actually, because if you have access to a simulator or an emulator, that'll also give you access to the developer tools in that. Imagine changing location on your device or changing language or text styles or sizes. Let's say you want to see what the app looks like in Arabic, for example, then these are all things that are relatively complicated to do on a physical device. And secondly, sharing a build immediately as a link is I think a better user experience than having to download it and test it on test flight and then leave feedback over there. So, this is a cool feature that we have and what we've been working on. We have ideas how to progress it even further. Don't want give out too much yet, but today, we're introducing this app previews thing what Codemagic users have access to. **David DeRemer:** No, that's super exciting. Congrats on the new feature, and that does sound very compelling. Definitely, it's interesting because where you guys got started was being able to accelerate these builds and do the distributions, but you're right, getting someone to instal it on the phone, and I know people have experimented with using Flutter Web to do those things, but actually being able to run it in a real simulator, real emulators is very cool, and congrats on the new feature. So, just to wrap this up, I mean, we've talked about so many things here, the importance of community brand building. You guys got in super early. I'm curious, as you've progressed through this, what are some of the lessons you've learned and maybe advice to someone who might be an early member of a startup, a tech company, what are some of the lessons you've learned that you think you would give advice to if you could go back in time? **Martin Remmelgas:** I think trusting yourself is important and trying to catch yourself on whether you are too hopeful, whether you are ignoring facts or reality and trying to be overly optimistic. Then, catching yourself there sometimes, it's like trying to rationalize decisions is probably not the great idea. So, trying to be more objective at times is good. But if you're starting out and you're considering then trying to get on board, like technology, like Flutter at the moment, it's super good. We've seen as well that Shorebird, for example, launched and everybody is excited about this stuff. If you can launch something for a specific segment, market segment, and this will automatically build love for you, and having this community love for your product is something that I think it's very difficult for competitors to do, especially if they're large companies. So, going after a small segment in the beginning, it'll pay dividends to you. So yeah, that, I think, was a good choice, and if others are considering it, then now is a good time to get \[inaudible 00:33:51\]. **David DeRemer:** Yeah, actually, I'm curious about that because we talk about, "Oh, we got started seven," eight years ago now in the Flutter community, and we're talking about enterprise maturity and adoption, but that I think Flutter is still only really just getting started in the grand scheme of things, of what's possible. Do you think that there's still plenty of opportunity for startups? What pain points or niche opportunities do you see that the community maybe could go solve or what advice would you give to people to get started in the Flutter ecosystem? **Martin Remmelgas:** Actually, if you think about it, considering the number of developers on Google's side who are working on Dart and Flutter and considering the user base that they have for Dart and Flutter, you wouldn't expect that such a small time, the team can serve such a massive market to do a good job at it at the same time. I think at the Google IO, it was said about a third of the new apps that are released to the stores or Flutter applications. So, the question is how do you pick an idea or what problems am I seeing in the community? But the good news is that everybody can check what problems exist in the community if they check the Flutter issue tracker. So, if you go and actually using these AI tools now, maybe an interesting way to do it, so if you go, maybe somebody who's listening can try it, if you ask AI what problems exist in the Flutter ecosystem by looking at the issues or maybe looking at the sentiment that's on X or maybe looking at what's being posted in Stack Overflow if people still use it or in Reddit. Now, it's best time I think to try to get these ideas because the data is all out there and now actually, we have some tools to read it, when previously it has been difficult perhaps. So, in trying to pick to talk about the issues, I think, is a good place to start. **David DeRemer:** I think most people, not most people, but one of the things that a naysayer against a community like Flutter or somebody might come in and say, "Oh, well look at all these unsolved issues that are open on the Flutter repo," or "Does this get enough support from Google?" But I think you've just reframed it as, don't look at that as a weakness of Flutter, look at that as opportunity. Go find something important and high scale, because you're right, there is this disconnect. So many engineers, so many teams, so much I think commerce being driven by the Flutter ecosystem right now. Really, you're right, being supported by, on a scale, relative scale, not the biggest team in the world in terms of the folks who actually work at Google doing this. Not that it's a small team, but that the disconnect between how big that team is and how big the market is, is quite large. Tonnes of opportunity in there to go find something to do. **Martin Remmelgas:** Exactly. Think about it, you have a third of the old apps that are released in the world are Flutter applications, and there is a small team, not a small, but a good team, good talented team supporting it, but the demand for all the tools, what you need to now release these apps and maintain these apps and grow these apps, this is still there. And I think companies like Widgetbook, Dart Code Metrics, Shorebird, there's a bunch of other ones, all of them have proven that you can build a business with this. And Eric, who was one of the guys who started Flutter or founded it, is putting all his eggs in this one basket that is betting on Flutter to be big. Why shouldn't you? So, I think there is definitely room for opportunity here, a lot of developer tools that can be built. **David DeRemer:** Yeah, that's super, super insightful, and I hope anybody who's in the developer community of Flutter that listens to this takes that to heart and says, "Oh, there's huge opportunity that if I can go find something that I'm passionate about, that I can solve a problem for a lot of people, that can be the foundation for a business that I build," or even if it's not a business, just something that I really deeply care about and passionate where I make a difference to the community. One of the things when I was at Fluttercon EU that I observed in the same hall as Droidcon and Droidcon would have two or three or four, even five competitors that are doing a similar thing. And when you go to the Flutter side, usually there's only one or two, but when you think about the relative size, that's another example of that. There's the Flutter team building at Google building for all of these engineers, but then there's all the companies, and I forget who I was talking to, but we were talking about like, "Oh, wouldn't it be cool if actually there were competitors all here where they're selling the same thing?" And you might not want that, but if you think about it for Codemagic, if you had across the way from you, the aisle in the Flutter space, somebody who's competing with you, I'm curious to your thoughts, but I think that's good for the ecosystem because that drives innovation. That drives momentum. It drives competition to make the ecosystem better and continue to do more and more and more things. And so, I think it's exciting and there's a lot of opportunity for people to jump in and just create new things that can make a big impact. **Martin Remmelgas:** Yeah, absolutely. And we have a lot of competitors. We always have to answer this question, how do we still win if we are small and we're competing against big companies, or even if we have direct competitors, like what sets us apart? Because if we don't set ourselves apart, we're going to die. It's going to fade out. So, it forces you to innovate and forces you to do better, and this will always lead to better products for the community. So, the more competition there is, it's a guarantee you're going to get more affordable products, better features, more of everything. So yeah, come join the party. **David DeRemer:** Yeah, wonderful. Well, Martin, thank you so much for joining and sharing your insights today. It's amazing to hear the story arc, both for you personally and for Codemagic and embracing the Flutter community so early. And it's just wonderful to hear that even especially this year, really growing quite a lot and seeing a lot of exciting new opportunities. If people want to learn more about Codemagic or try out your new app previews feature, where can they find out more? **Martin Remmelgas:** Codemagic.io And in particular, you'll note this app previews in one of the sidebars if you log in, so you can check it out there. **David DeRemer:** Awesome. Well, I look forward to seeing you at many more community events. I'm sure we'll see you there for a long time, and thanks again. **Martin Remmelgas:** Yeah, thanks for listening everyone, and thanks, David, for inviting me. Cheers. **David DeRemer:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review, and if you want to get our latest episodes, don't forget to subscribe. Thanks again and see you next time. ### Building the Future of Apps With Flutter and LLMs Source: https://verygood.ventures/podcasts/frank-van-puffelen-puf-low-code-no-fear-building-the-future-of-apps-with-flutter-and-llms/ In this episode, David chats with Frank “Puf” van Puffelen—ex-Googler, longtime Firebase advocate, and one of the most recognizable names in the developer community. From the early days of Stack Overflow to shaping Firebase and Flutter’s rise, Puf shares his journey from software engineer to developer relations leader. They dive into the art of teaching through code, how to conquer stage fright as a developer, and why AI and low-code tools are reshaping how people build software today. A conversation packed with humor, wisdom, and lessons from a career built on helping others learn. {% audio_embed url="https://player.cohostpodcasting.com/5b664cc7-315d-4c79-9c91-46d951917d8e/e3e7bcab-3048-4fbb-b991-ef1bd5a63340" /%} ## Transcript **David DeRemer:** All right, Puf. Thanks so much for joining us today. It's a great honour. **Frank van Puffelen:** Hey David, thanks for having me. Always a pleasure to chat my friend. **David DeRemer:** Well, you've been somebody I've admired in the developer community for a long time. Not just Flutter, Firebase community, but just in general. I think that like many people you've influenced me in my engineering career and I think many, many people out there just in terms of if you ever asked a question about Firebase, especially probably you, Puff, weighed in on that and I just really appreciate that. I for sure remember seeing you and definitely helped me along the way. **Frank van Puffelen:** There's a good chance that I answered that question, right? Yeah. **David DeRemer:** Yeah. Well, I think to kick us off, I think to my knowledge, you're one of the most prolific contributors to Stack Overflow of all time. Correct me if these numbers are right. About 18,000 answers and 600,000 reputation points, top 50 worldwide? **Frank van Puffelen:** And these are the ones that always get mentioned. My favourite number is that I've had 30 million views on those answers, which even when I was at Google is a large number. That really helps when your performance evaluation comes along. **David DeRemer:** Oh, a hundred percent. Well, and I think it made a name for you, but also helped so many people along the way, and I think in today's world, which I'm sure we'll get into this, where you can ask AI, at some point you had to get help. And I think getting access to help and we at VGV felt it very acutely in the early days with Flutter where there were no answers or people weighing in on Stack Overflow. So I think your commitment to helping the community is really to be applauded and kind of really helped a lot of people along the way. One question I wanted to ask you, it started, you became a very recognisable persona and voice in the community. What was the voice that pulled you in? How did you get involved in technology in the first place? **Frank van Puffelen:** Ooh, there's a lot to uncover there. I always find it so interesting to be sort of like, I often joke that I'm a minor celebrity in a niche of tech, but whenever I get recognised somewhere, that's always a mode of, "Huh, me?" And I was at Google last week, last week, had to think of when it was having coffee with a friend and a, for me, random Googler walks by and says, "Wait, are you Puf?" And he shakes my hand and I never met him, but he was new on the Firebase team and knew my name from that, which I find fascinating. Yeah, no, I got started in tech a long time ago back in the 8-bit days I would say. Right. So Commodore 64, Atari 600, ZX Spectrum, that type of thing. And initially I thought these were fun devices for playing games on like we all did. And then I started writing my first code on that and realised that being able to tell a machine what you do and it then listening and doing the thing you said, not the thing you meant it to do, by the way, a clear difference was very interesting for me. So I decided to go to college for computer science and turned that into my career not knowing by the way what type of career that would be. I'm from the era where, don't tell any of my potentially future employers this, but where I would probably have done the same work for a lot lower salary than they paid me. And yeah, I started doing that. Was a software engineer for about two decades before I switched over to becoming more of a speaker. And that started with a conference we did for a few companies ago in Holland where we introduced a new product that I had invented and created. That's when I first learned that work goes into preparing for talks. And then from that I discovered that while I was a perfectly fine software engineer, my speaking abilities were somewhat more unique. Being able to combine technical expertise with also being able to explain that to almost any level is something that essentially I've made my career out of since. **David DeRemer:** So started as an engineer, found the passion for talking about what we do and then communicating and then getting into DevRel. And when was that? Was that when you started getting involved with the Firebase team or was that before that? **Frank van Puffelen:** I already did smaller public speaking before I joined Firebase, but it mostly ended up being my job there. And yeah, there's a fun story. You might've read it already. Essentially, my job before Firebase was not that interesting. Let's put it like that. So I had brain cells to spare on smarter things, and I was at that point just looking on Stack Overflow and answering questions, random questions that came up in technologies that I knew a bit about. So because that allowed me to learn more, that was the reason I started in doing that. And then I saw questions about Firebase popping up and I'd looked at Firebase probably 2012 when they did the first rollout of that, the first beta. And I found it interesting, it was my second time working with a NoSQL database, so I essentially saw that people had the same concerns that I had the same questions, how do you model your data in a NoSQL database? So I started explaining that and it was the type where Saturday morning I'm just sitting here, it's like, "How would I model this thing that they're asking about?" And at some point then I started getting votes, which early on it is like, "Oh, look." And then I got a comment on one of my answers that said, "Nice answer, Frank," from somebody else, and it was Andrew Lee and I was like, "Who is this that he thinks I need his validation?" And it turns out that Andrew Lee was one of the founders of Firebase, so it was actually really good. So they reached out to me and a bit and a bit later we were able to make me join and I joined in the developer relations capacity there. I could have joined as an engineer. We were just trying to figure it out, and the biggest need was still in developer relations back then. To people explain, this is around the time that Firebase joined Google. So I'm essentially the only person that interviewed for the same position at both Firebase.com and Google for the same Firebase position. And yet I've been answering as many questions as again about Firebase since then until I left Google mid last year. But even after that, I still spent as much time as I can on Stack Overflow to help people there. **David DeRemer:** So the sort of push to answer questions originally sounds like was driven out of an opportunity to learn because by answering questions, it makes you think about new problems or questions and come up with the answers? **Frank van Puffelen:** Yeah, very much so. Back then, Firebase was much smaller. It was a database, pretty much, a NoSQL database, and so there's so many things you can do with a database and I didn't always have the ideas, but then somebody else had the idea and a problem implementing that idea. So that made it much more interesting for me. And I learned not just about Firebase, I learned about so many technologies there. It's how I got interested into geo-type stuff. So geocoding, geolocation, distance between two points on the earth with the Haversine formula, all these types of things that I learned because people were having trouble with it, and I was like, "Let's see if I can figure it out and then explain it in a way that works with it." **David DeRemer:** So as you progress through, and you progress through this activity becoming DevRel engineer, you're answering still questions, but now you're doing it professionally, is the personal quest to learn still at the core of a lot of your activities? **Frank van Puffelen:** I think it is, yeah. Not always, I must say. It depends a bit. It's a mix between wanting to learn and wanting to teach. I would say for things where I know a lot already, I'm more just trying to transfer that knowledge. I still enjoy that very much. For example, I used to classify problems that people would have with Firebase early on, and we had internal trackers at Google for that, but one of the types of questions was always, async is hard, right? Dealing with async APIs is difficult, and NoSQL is hard, is not modelling. NoSQL data search is hard. And for example, the async is hard type questions. I think it took me probably five years to get to the perfect explanation for that, which is it actually got a name afterwards at Google, they started being known as the 1-3-2 problem, which is right. Normally when you see three lines of code, they actually 1, 2, 3. But when it's async codes and you have a callback that you're in line, it's 1, 3, 2. I tried so many ways of explaining this to people, and you see the success ratio of your explanation. Then finally, when I had a certain way of explaining this, that success ratio, it plateaued pretty much. It was a great success ratio. That's what I love seeing, because then I started giving that explanation and right slowly, you then start closing people's questions as duplicates or whatever. But for me, the most satisfying of that is essentially seeing other people then using my explanation. And that's where it's like, "Oh, I love that," because I, okay, you shouldn't copy paste, but I don't mind them copying a style. It's like, not at all. It's like if my explanation is the best one we found, then by all means use better explanation. Of course, if there's a, right, keep looking for better explanations. But for this one, I think we pretty much got it covered. **David DeRemer:** I mean, I've heard in my career that sometimes the best way to learn something is to teach it. And so it's interesting that you've had a learning kind of teaching dichotomy in your career. And even that example you were giving around sort of taking years to refine how to explain it, it's like finding new opportunities, new ways, seeing it works, like learning how to be better at even that craft is kind of interesting. How did you, as it progressed through Firebase, like Firebase gets acquired by Google, I'm sure that was a whole huge set of changes and cultural shifts and all of that, and the community grows. How does your role shift throughout that trajectory, and how did you keep the passion going throughout that whole journey? **Frank van Puffelen:** Oh, that's fascinating. So I actually joined Firebase after we were acquired by Google. Now, anyone who was at Firebase when we were acquired swears that I was there too. I always joked that my bank account shows that I wasn't, but I was interviewing before and I was a friend of the team. I was one of the early users, but no, I joined afterwards. But yeah, you could see it very much. And honestly, one of my favorite stories is one of my best friends now, she was hired at Firebase pre-Google as the office manager pretty much, but mostly to keep the developers at Firebase happy. So nothing external facing, but making sure there's the sodas and the cookies and things like that. And honestly, I later read her application letter and it was hilarious to see. I totally got why they hired her, but that was her role. Then Firebase was acquired by Google and that type of role doesn't exist and definitely not in a product team. So she had to choose whether she would become somebody's administrative assistant or become a program manager, and that was a hard choice to make. And she became a program manager, and only then I sort of started working with her. So for me, she was always my program manager, the person on my team that would help me program managers and Google two things. It's create order out of chaos and amplify the people that they work with, and that's what I know of that role. So I started working with her and she came in as an L3 at Google initially, like Google has these levels. And through, she was there for a decade, she became an L6 program manager, one of the leading program managers at Google. And that coming from somebody who was hired to make sure they had the right type of cola in the office, there's such growth that you can see there. I found that amazing. That's a talent that clearly was discovered by Firebase and then grew into one of the better known programme managers at Google. But that of course also comes with lots of, when I joined Google, it was 40,000 people. When I left Google, it was about 160,000. 40,000 is a lot, 160,000 is a lot-a lot. And I still see that when I talk to Google friends, they pretend that Google has grown since I left or something. It's like, no, it always was this huge company, right? It's hard to get things moving. If you want something done at Google, it's typically best to either be the boss for everyone or to do it yourself. And I typically found that latter camp that suits me better. **David DeRemer:** Awesome, awesome. So you started out answering questions, they hired you and were you still sort of answering questions? What else? How does DevRel evolve? Because I think the story arc you're describing, I think DevRel changed over time too, right? You start doing more presentations, more online content and videos. What was the evolution of the DevRel function that you kind of pioneered and pushed as well? **Frank van Puffelen:** You have such great questions. So it was fun for me when I joined Google, I didn't really know what I wanted to do and I'd been to a few conferences before that and I'll be completely honest, I didn't enjoy that. I was the quiet engineer sitting in the corner, so that was very interesting. Even after I joined Google, I went to my first Google I/O, and Firebase was not that well known back then. This was the old Firebase that had just been acquired. I was walking around, it was back then still in Moscone Centre, and I was not having a good time. I was like, "You know what? I'm going to walk back to the office," which is around a corner, "because this is not my thing." Somebody comes up to me and he says, I was wearing a yellow shirt, but he says, "Wait, you're with Firebase?" It's like, "Yeah." "Hold on." And he grabs his tablet. "I have a few questions for you." They had a list of eight questions about how to use the original Firebase database in a telco company for making phone connections. And I had a blast, and suddenly I realised it's like, ah, what I want to do at the conference is be helpful. The recognized part is not the important bit, but being able to help somebody is what's important to me. And from that, I went to, and DevConf was a conference series back then in Boston, and I was going to attempt to figure out if I still would like that, but somehow my programme manager now best friend, set me up to also do a talk there. And it turned out that I also really enjoyed that. So from that, I became a conference speaker, an online writer answering questions, and then later I started doing video That took a few years. Honestly, I don't enjoy doing video nearly as much as I enjoy being on a stage, you'd be forgiven to not recognise that when you see me doing video, because my lowest level of enjoyment is typically a lot higher than the highest level for a lot of other people. I tend to enjoy the heck out of everything I do. **David DeRemer:** That's awesome. I definitely want to learn more about how you have you do that, keep that enthusiasm, excitement, but I wanted to zero in on you were saying you started as an engineer and even you'd go to conferences and you're that kind of quiet engineer and then all of a sudden you're able to unlock this ability to get up on stage and talk to a lot of people. And I've seen firsthand the power of an engineer who you encourage to take the leap and they go out and they give their first talk. Maybe it's at a local meetup or something, and they don't know how to, they're embarrassed, they're nervous and fearful, and they're worried about the content and then they do it and they feel so relieved and proud and happy. And then the next one's a little easier, the next one's a little easier. What are your tips or tricks or things that you've learned, advice you would give to a young engineer who to get out there and get up on stage and be comfortable doing that? **Frank van Puffelen:** Oh, it's crazy, right? No, I'm always so proud when somebody does their first talk and seeing them before and after, and the self-doubt is there through the entire journey, something that you and I probably have a lot less because we've just done this more. Also, that doesn't mean that all my talks are perfect. One of the things that you learn through experience, by the way, is dealing with problems. And I love Scott Hanselman. He picked the best words for something I experienced, which is if a demo doesn't work, we don't have a failed demo, we have a shared debugging experience, and I've done so many of these including at something like Google I/O. But there's a few things. There's for public speaking is one of the biggest fears that people have, and there are some cases where I get that doing a TED Talk, I would also probably be nervous, but getting in front of 1,000-2,000 techies does nothing for me. It's like I love doing that. There's no anxiety. There is, by the way, a energy level that I need to control better. I noticed often when I start speaking that my voice is a bit wavering. It's like, that's not nerves. That is purely excitement, but it's better if I control that better. Preparation is key for that, by the way. But then I've done lots of, so at Google, I didn't just do the things that people have seen me do. I also built with my program manager friends and ran the events programme for Firebase for many years and a program that was pretty consistently inside of Google described as the best events program they've ever had. And honestly, that was just because I approached it as an engineer, so it's like, why do we go to events? And it turns out there's many things that people don't realise why you go to an event and part of you, this very good ventures sends people to events that they come back much more motivated after an event than they were before they were signed up. And this is so normal. And with Firebase, we just started using that at some point, getting people to go to events, but then they're first time speakers, so you want to make sure that they have a good time and that means that I was always available to mentor people, but as I got more senior, I was still available, but it got more scary for the people that I will mentor. And then it's like, okay, so let's build a programme where we have mentors and where I just show up one time to say, "Oh, this looks awesome." And that is a part of what you do. It's also a part of showing people that they know everything, and it's one of the things I've learned from doing the videos that I did for Firebase for a long time, the Firebase release notes, so the monthly videos of new software updates, I would quite often reach out to, I would make a list of like, "Hey, what do I see as new," from just reading written release notes and all kinds of repo commit logs, and then you need to tell the story. And I would often just reach out to the engineer and say like, "Hey, what does this thing do?" And I recall that so often. For one, it's like, "Oh, the Pf reaches out to me asking for that," where I'm like, "Yeah, well, I need info dude." But then it is also so they tell me something and I write it down and then I read it back to them and I wish felt a bit know from Harry Potter, Rita Skeeter? **David DeRemer:** Yeah. **Frank van Puffelen:** She has this- **David DeRemer:** Yeah, the reporter. Yeah. **Frank van Puffelen:** I always felt a bit like this, "This is what you told me." I didn't make this up. I just sort of skipped a few words, rearranged some things and turned it into a better story. But the interesting thing is always in the change they make already, and this is one of the things you have to believe as developer relations is I believe that maybe not all, but let's say 80% of the changes that engineers make are interesting because otherwise they wouldn't spend time on them. My specialty is telling that interesting story about them, but I honestly believe that most changes they make are interesting. So that also means that I can go up to them and ask like, "Hey, why did you make this change?" And when they explain it to me, there's a story I can tell, and that's definitely a unique skill, but find essentially your storyteller. If you're an engineer having to do this for the first time, you have the information you need to give. There's no doubt that. You just need somebody to help you turn it into the story, and that doesn't take a lot of time. I've mentored people where it was a 10 minute mentoring session for a 30-minute talk, and that's all they needed essentially, pretty much for just taking the bits that they had. And it's like, "Hey, if you position them like this, then suddenly you have an interesting story to tell." That's so common that I really hope who still does this, but it's definitely something that I always enjoyed mentoring speakers and just giving them confidence. It's why I love doing talks together with first-time speakers is essentially I have a hundred percent faith that anyone, almost everyone can be a public speaker, a successful public speaker, they just have fear. And since I have no fear, for me it's very easy to just offer like, "Hey, want to do a talk together? And you pick the topic, honestly, you're the expert. I will just be the dumb ass who's on that stage and who asks you the not necessarily dumb questions. It's the questions that make it clear for the audience what's happening." But in a way that is comfortable for the other speaker also. **David DeRemer:** Yeah, I love that. And it's just finding ways to storytelling, like you're saying, right? You want to tell a story and everything we do, communication is really important. I think as an engineer being able to communicate an architectural change you want to make and be able to explain it in a way that it's not mired in the details, the right levels of people can understand it. Or maybe you're trying to explain something to our junior developer to teach them or answer a question on Stack Overflow or something like that. It's all kind of the same thing. And I think as developers get further along, I think they're closer to being able to communicate. If they're already explaining to their colleagues and coworkers how something works or why they want to do something, it's really no different getting up on stage. It's nervewrecking because they have all these people staring at you and there's this fear of what if I mess up? But it doesn't matter if you mess up. Most audiences, no one's going to heckle you. In fact, one of the bravest things I've ever seen was we had a developer on our team, Kevin Grey, where he was early on for us at VGV, and he was kind of a very quiet person, and I remember he volunteered to do a presentation at the Flutter NYC meetup. This is years ago, it was probably like 2018 or something, and we were all like, I don't know, he's kind of quiet guy. And he went up there and he fumbled almost immediately and he just paused and he was quiet for almost a minute. It was kind of this awkward pause and everyone, but you know what he found himself, he recovered and he gave this great talk and everyone applauded really strongly at the end, and I always go back and think about that. It took a lot of courage to get over that moment, but it also shows you that it's only 60 seconds, 30 seconds, you get past that, even if it's an issue, you have to debug live because your demo failed or whatever. That's all part of it, and people actually love it and they love when you overcome that, or even if you don't, you have to pivot to something new as long as you're kind of continuing that conversation and parting some knowledge. So yeah, I think people make it harder than it is, I think, and it's really not such a big deal. **Frank van Puffelen:** So much pressure on ourselves for public speaking where indeed in all these situations where you and I speak, it's like, these are tech people. They chose to come to this talk. They are not the enemy. It is one of my basic rules in life, just assume positive intent. Very much \[inaudible 00:23:47\] there's this thing where you need, imagine the audience naked or something as one of them where it's like, oh gosh, no, because they're all my friends. They're either current friends or future friends and accept that. When I live coded early on, I would honestly be building. When I joined Firebase, I asked him, "How do we demo?" They said, "Well, we live code a chat app." So I was appointed the Android expert, and so I started live coding a chat app on Android. So I got it down to, it took like 25 minutes to live code a chat app, and later I found that the Android team was very surprised because you cannot live code an Android. That was their statement. Well, nobody told me, right? It works. But I would always start at that type of talk. I explain to people, "Look, I don't have any snippets. I don't have anything that I copy paste, unless it's from documentation that you also have. And I am going to type everything. In return for that, I need from you that if I make a typo, that you yell it out to me." And the moment you say that, people lean forward on their chair, the front row, and when you make a typo, you feel it in your fingers, yelling stars because now they're part of the experience and you always do it as a joke. I would always say, because otherwise we might be here for a long time if you don't call out the typos, but it's a great way to keep people engaged, much more than when you do a pre-recorded demo. I never have a backup. If I do a live coding demo. I don't have backup recordings. You should. Really, you should. I just don't. It's like, yeah, if it doesn't work, guess what? We'll do something else. It's fine. That doesn't apply for everyone equally by the way. There it helps if you've been on stage enough that you know can pick some other story out of the air. **David DeRemer:** Well, I think you said something in there too, that is just fantastic advice for anybody who's looking to get up on stage and talk to people. Just assume everyone in the audience is your friend. Assume good intent, assume it's just a room full of your friends who want to see you succeed, and if you fail, they're going to not give you a hard time. They're going to pat you on the back and lift you up and go again. And I love that sentiment. So you also said that your objective is maybe there was this idea of helping people and so you've had such an impactful career already. When you think about the mission and how it's evolved for you personally, and as you look ahead, what is your mission today and how do you see that evolving into the future? **Frank van Puffelen:** It's a good one. I had to think of this when I decided to leave Google, which is way earlier than when I left Google, actually at some point I was at Firebase and everything was still fine, but I realised that I'd sort of reached the level of impact I could reach in that role. And I was like, "So what do I want to do next?" And that comes from what makes me happiest and what do I still want to accomplish. And I noticed that what makes me happiest is seeing people believe that essentially I probably 150 times over 10 years, I live coded the chat app from zero to a chat app on screen. So at some point that becomes butter smooth, and that also means that you can look at the audience while you're doing the thing and see how they're reacting. It's fun because when you see an engineer sitting there, I'm building a chat app and at some point, this is with Firebase, so real-time data synchronization, and I can see at some point where they're like, "Oh, I can do a counter app with that, or a to-do app," or one of them is in their mind building in multiplayer game. And that's fun to see. But what I noticed, what makes me happiest, if there's somebody sitting there who is not a real convinced software engineer yet. They sort of might be doing the education but not have the confidence yet that they can build an app. And at some point when I'm building that app, because I take it from zero to a working app in 20, 30 minutes. But then I could see that in those eyes it's like, "Wait, if he can do this in 20 minutes, then I can do it in a day." And that's, seeing that belief for people popping into their minds is so empowering for me because that's what led to my mission in life is to help more people build more apps. That's also when I left Google, it was to join FlutterFlow, a company that had a drag-and-drop type, low-code IDE that would generate flutter apps and very much a different audience, not traditional coders, but more founders and builders, we would say there. And I very much there noticed that you get a very different type of person building, and I think that is amazing. I want, again, my goal is to have more people build more apps, and that requires that we have tools that help more people and people like myself and others that show them that building an app is not nearly as difficult as we make it out to be. It's also not nearly as easy as it sometimes seems when I do it on stage, but I think a lot of more people can build apps. In fact, we talked earlier about teaching and my poor wife for, I say she's a tech writer by the way, so explains technical stuff to people, but she is often the first audience that I try explanations on. We are doing something new at the company where I work at Firebase, I would be explaining no SQL databases to her, something she has zero interest in. But first off the point, if she understands the thing and asks follow up questions that are good, then I've explained it well, I've always taught her, she would say, "I'm not technical," and that's phrasing that I really don't like. So I told her, like, "Honey, you can be a developer. You can learn to program. It's really not something you cannot do." So at some point she was in between jobs and she started to program and being a tech writer, she of course built a little bookshelf app and she worked on that for a bit and we talked through arrays and stuff like that. It was all fine. Then at some point she says, "You're right, I can program. I just don't find it interesting." That's fine. No concern there, but now you can never say that you can't program anymore. And I now see it, because after that I saw that, for example, she works at Apple and does a lot of their language type things, language work, so language QA, and I noticed that her spreadsheets for comparing things got so much more structured after she learned how to program. And I've seen the same with my program manager friends where if I would show her a formula once of like, "Hey, this is how a formula works," I'd come back a week later in the same spreadsheet, and that formula was like now six times longer. I found it hard to debug, and these are both non-technical roles, but it is like, I don't know, for me, a lot of program programming is not about the text that you type, but about how you think about data and data structures. And I want people to acknowledge that, and we're living in an age where more and more people can build apps without being a traditional coder like I am. **David DeRemer:** Yeah. Well, let's get into that because that's probably the big AI is a new thing coming out and to that mission of helping more people make apps. I mean, certainly AI has that promise. I think that there's a lot of discussion around that on both sides around how easy it is versus how hard it is. Does AI create work slop or these other new terms that are being coined? What is your take on it? And you've worked through Firebase, which Firebase was such a big change. It was like all of a sudden all this work that that was required to build an infrastructure, build a back end and get all that stuff working. All of a sudden I had these tools accessible to me and then things like FlutterFlow and Dreamflow and stuff where it's like, "Okay, I used to have to do it away and this is an alternate. It maybe doesn't give me all the things that I could do, but jeez, is it easier in a lot of ways to get things up and running," and now you have AI in this story, so where are you on this? Where are you going? What's the software or the technology that's exciting you next? And is AI part of that? **Frank van Puffelen:** It's definitely a part of it. It's not all of it, I would say. Now I love how you're comparing it to Firebase, right? Firebase made everyone a full-spectrum developer because the back end was handled for you and you only wrote front-end code. And of course you've seen them. I've had to support them. All the news articles about these databases that are not protected where all kinds of personal information is leaked. It's like, yeah, when we make everyone a full-stack developer, there's going to be an educational gap where not every one of those new full-stack developers realises that they need to secure their data or they realise it, but they can do it later. And it's like, okay, people make mistakes. And while at the moment when I had to support those people, I was not happy with those choices. I am in history, I'm very happy that they could do it wrong because if the alternative was not being able to build that app, that is for me not a good one. And I often compare to, I've done this for a while, so when I started programming, my first professional programming was \[inaudible 00:33:23\] Pascal, and then they started doing Delphi. Microsoft did Visual Basic 6 around the same time, and Visual Basic 6 was the first time when you could drag and drop these widgets onto a canvas and you would then double click on the button and write. Or for some people, copy paste a line of code from somewhere and you could make it do something. And every salesperson, every manager at every company suddenly started building apps. Those apps were mostly automating their own workflow, their own professional life. So most of these apps had one user, and most of these apps were, we'll be honest, but were horrible. They looked so bad, they functioned so badly, but maybe 1% of those apps actually were usable to a lot of other people. And of that 1%, maybe 1% actually was a type of app that nobody had ever come up with and that now was being built by somebody who couldn't have built it without that tool. And I want that 1% of apps to exist. That is key for me. I don't care that much about the 99.99% slope that gets created. I'll help those developers, but I do that so that we get that one snowflake app that wouldn't exist without that tool. And that's what we're seeing with AI now. And I have great discussions with friends online about what's the value of AI tools, and I'm trying to be clear there. So normally when I talk about them on the stage, I keep saying LLMs. I intentionally don't use the words machine learning or AI because for me it's a tool. I don't have tool lying around. It's a hammer, it's a new type of hammer. And I enjoy very much trying to figure out what this tool can help me do. For me, what it helps me do more efficiently, for other people, what it helps them do that they couldn't do without a tool. And for example, I am not a DIY person. I'm not very handy, so if anyone invents a better hammer, well, I'm still not going to be front of the line because I don't have a need for hammering, but it's like I would be their primary audience for these types of tools, and that's how I see LLMs. A very famous one is Peter Levels with the Fight Simulator on Bolt, right? He had built tonnes of startup ideas. His problem was not that he couldn't do it without a tool like that, his advantage was that he could do it a lot faster with a tool like Bolt. And that's I think a key where it's like, and honestly, I've been considering this, somebody from Google was reaching out. If I could talk, at a DevFest, I'm not sure if I'm supposed to say that, but I think it's okay, and I asked this, "Why do you need me essentially?" And it's like to get the people that use Firebase studio that don't know anything about Firebase to help them there. It's like, "Oh, I love that. These are people that want to build an app that can do the prompting, but we need to get them into a mindset where they don't necessarily think about the code as much, but they do think about data structures, security, maybe scalability." I don't know that one yet. It's like, and this is a part where I'm fascinated. I can see when I'm talking with my wife about apps that I see people building or that I build something, I can see that she's this close to typing these prompts herself. And that is for me, just fascinating. Also, when we started at Flutter Flow doing DreamFlow, we had something interesting. The company is called Flutter Flow, so very much tied into Flutter. And Flutter Flow itself is a, like I said, low code builder. So you're building an internal data structure that that is proprietary to Flutter flow, and from that it generates a Flutter app. So most of them, what you're doing is actually not Flutter. You're just dragging and dropping some widgets around that happen to look like one that Flutter. Also Dreamflow, the new tool is a Flutter ID. It's a hundred percent backed by your Flutter source codes. And on top of that, there's an AI, and what I found fascinating is to see for the company, it was like how much do we lean into the Flutter name there, right? Because it's essentially DreamFlow is more Flutter-based than Flutter flow, the product is, in a sense. So, picked the wrong name for the product. But we also realized that for a lot of people that are building with these tools, the fact that it's Flutter is not important. Flutter was still the right technology to use for making that tool, but for the users of that tool, it didn't matter all that much anymore more because they never looked at the code a few months ago, a few months, something like that, A month and a half ago maybe I was doing a comparison of taking one prompt to build A, Everyone is building habit trackers nowadays. So I was like, I had no idea what it was, but I first asked ChatGPT, what's a habit tracker? And then I told a bunch of these five coding tools to generate this, the main screen for a habit tracker, and I did that initially, honestly, we needed to send out a newsletter to our users about DreamFlow, right? So I did it in DreamFlow first and then I just asked all the other ones. What I saw is that the Flutter-based tools generated consistently what I thought were prettier UIs than other tools. The other tools had a lot of variability by the way. So between the tools that use the same technology, there's quite a few that do react, for example. You could see that there was variation, but the Flutter ones were on average, in my opinion, prettier. I must say that the ring for one, in my opinion was prettiest. In part, it might also be because it used dark mode by default, which it picked my system settings essentially. But you can see that these tools allow a lot of people to build stuff that they couldn't build without them. That also allows a lot of people to build apps that they should not be able to build, and that is a balance. But I've been dealing with that balance on Firebase where they're all suddenly full-stack engineers for a decade. And before that too, I've never lived in a world where there were too many software engineers. That doesn't mean that it's now possible for every software engineer to find a job. It's actually one of the things that I am worried about, but I hope that that is a wave that we're just going to come back to realising that while there's lots of things we can use LMS for as software engineers, that there's a lot of other skills. Also, I used Cursor for a bit for building a multiplayer game at some point, and I used it very much knowing what I wanted to output, but it was just a faster type and a faster search tool for me. It's like, that is fine. It's very different from how a non-traditional coder might use this tool. And that's why I asked non-traditional coders, "How do you validate your output?" And the best answer I got by the way, was from somebody who was using FlutterFlow for one app and then Cursor for something else. I said, "How do you validate that because you don't read a code?" He said, "No, I just tell it to add another test." So where for traditional engineers, adding another test is a unit test with code that we know how it executes. For him, it's just another checkbox in a table. That's like, "Okay. If it's just a checkbox and you sort of know what it validates, then it doesn't really matter what code it is." So moving forward, one of the biggest things that I'm curious about is when I was using the LLM for generating code, what I noticed at some point is that I don't think code is the best mechanism anymore for capturing the output of the LLM for me. I have no idea what the best output, what the best format is. The standard would be an AST, an abstract syntax tree. I don't think that's it, but I don't know what is. So that's one of the things I've been trying to get folks at Google interested in. It's like, hey, that non-traditional engineer with, for him, it's not a checkbox in the table of tests. What is the output of an LLM? How can I best validate the output from LLM essentially as an expert and a non-expert in that domain? **David DeRemer:** So many interesting comments in there. I mean, I love the framing of LLM is just a tool. I think so many people are getting caught up in this existential crisis of AGI and it's going to replace all these jobs and all these things, but at the end of the day, it is still just a tool and somebody has to go in and prompt it and somebody has to deal with the output. And you're right that we been doing this all along? How many people have gone into Stack Overflow and just copied code that they didn't even know how it worked, but the answer says this is the accepted solution to the thing. So let me just do this. In a lot of ways it's kind of the same kind of output or a tool. You could have a hammer and some saws and you could make something, or you could have a 3D printer. And the 3D printer is a really powerful tool. It can make anything, but it still is up to you to decide, well, am I going to design the shape that I wanted to print? Is it a useful thing? Am I going to manage all the right supplies? It still requires intervention to make sure that you're actually getting something useful at the other end of it. **Frank van Puffelen:** That's the thing. You see, a lot of the negativity now route around AI is I think not necessarily on the tool, but on the over promise. I've been trying to get out of people's like, "Why are you so aggressively against this?" And I see it, and I honestly have to look no further than the GPT-5 announcement. I was watching that keynote, and I'm sorry Sam, you're not going to like this, but worst keynote I've seen in many years, many years. I was excited. The first demo they did was the engineer was giving a prompt to demonstrate in an animated SVG, the Bernoulli principle of airlift. I got so excited because I honestly learned most about that in the science museum in London. Remember we had the Flutter 1.0 launch there and they had a demo setup for that where they had smoke blowing essentially, or water vapour. And you would move the wing of the plane and you would see how pressure essentially builds and how an aeroplane gets lift. I was like, "This is going to be an amazing demo for an animated SVG." Now what I liked is it took eight to 10 minutes to generate that animation and they didn't hide that. So that was good. But then the animation comes out and keep in mind I was biassed. I expected that science museum thing, and it was honestly a rather sparse SVG animation. I was like, okay, not what I expected, but still this is from a product, a tool that generates working codes. Impressive. So I was like, okay, not what I expected, but still it's impressive, that can do that. But language they then use right after the first sentence is, "See, it can generate anything you tell it to." And I'm like, "Oh my gosh, that is such an over-promise based on a very limited demo you just did." Right? Instead of saying it's like, "Hey, look, with this input, we got working output," just like I just did, and maybe it's not the exact output we want, but this was just the machine outputting something like what can we do in the tool, in the inputs to get the outputs slightly more the way perfected it. And they've done this, I think there were four or five of these moments in the keynote where they did the exact same thing and I was like, this is so bad and I hadn't caught that the charts didn't line up and things like that, what everyone called out. But this over-promise leads to so much hate against AI now that a lot of folks that should know better are just not willing to even try it. And when we were talking about doing this podcast, you sent me your document of how you prepared it, and part of that was essentially I think ChatGPT, you told it like, "Hey, give me the background of Puff, my guest, in the podcast," and that is awesome. How much time does that save you? That's one of the things these tools are really good at. **David DeRemer:** Super good at, yep. **Frank van Puffelen:** Honestly, I did the same. Lots of companies reach out to me for help and one of them was... they want me to join, and one of them was LLM Arena, and I was like, "Hey, let's see what it does when I type who is Frank Van Puffelen?" And it did ChatGPT 4.5 and 5 or something next to each other, and both of them were embarrassingly kind to me, but it's one of the things these tools are really good at because the information is publicly available. It's also one of the things where, I love this metaphor. Somebody said, I made this distinction at point in talks using an LLM for something I'm a newbie at or using an LLM for something that I'm an expert at. And else it's really different. Somebody else said that it's like, "An LLM knows 40% of everything. You just never know which 40%." And this is very much true. So I make a clear distinction between those cases when I'm walking around, I live in San Francisco, and when I'm walking around the city, I sometimes look something up by pointing the camera at it and say, "Hey, what's this thing?" And there I often know very little. So the 40% is great, and when I'm having it right, security rules for a Firebase database, I know 90% of that. So I know the output I expect, so I use it as a faster type pretty much. I make sure that all the data it needs is there. **David DeRemer:** Because you know what good looks like already. **Frank van Puffelen:** Yeah, exactly. It's like I could have done this myself. I don't have to do it myself, and that's still nicer. And that's sort of the distinction you need to learn to make. And I think with a lot of the marketing around AI is taking everyone's job. That whole messaging is so destructive for their own market where look, you have a tool and the tool is going to be better. Tomorrow's version of that tool is better than today's for the simple reason. Otherwise, why would you release version? And that's exciting, but somehow you're promising so much. A lot of smart folks are not looking to give the tool the light of day. And that is sad for me because again, I want more people to build more apps, but I also want more people to get faster help in all kinds of other things. **David DeRemer:** I totally agree with you. If you could separate the vision and like, Hey, what this could be one day, but we're not there yet, but in the meantime it can do this thing 5% better or something like that, and they got to increase their valuations and build a lot of hype and energy. It costs a lot of money to build all these data centres and buy all these GPUs. So kind of get it. But I totally agree with you, and I think it's funny, even those demos you're talking about in the keynote, if they just did something that was maybe not as complicated or I don't think people are expecting it to be this miraculous thing either. I mean just a little thing that helps me and is better and just does something better, but if I put energy and effort into it and it kind of makes something that's just kind of ho-hum or kind of not great, then it's not really going to solve my need. But if it did a smaller thing really well and you could demonstrate that to me, then I think it helps people win. But I love the framing of it as a tool and I think getting the sense that this is just something that has a lot of knowledge, 40%, and it can help you along the way. And that distinction of like are you a beginner or you're an expert and you got to go into how you're using that tool with different set of lenses on. So given that you've contributed so much content to the training of these LLMS and these platforms are out there where you could ask questions, get answers. Now that content has been consumed and you expect us to go to the tool and ask a question, have it be answered. I mean, first of all, how do you feel about that being the source of all those answers, but also how do we continue to build answers for the next generation of questions? **Frank van Puffelen:** Yeah, it's a really good question. It's one that actually part of it keeps me awake at night. So one of the things I tried to do early on with LLMS was like, what are they trained on? And working at Google, of course, I was involved with the body of training material, but it was also very clear of tools that I was not involved with like ChatGPT, was trained on Stack Overflow answers. And a few months ago, I actually spent some time on like, Hey, can I prove this? I took one of my oldest answers that essentially I've seen people then copy paste into their own questions when they couldn't get it to work correctly or something. One of my proven answers with a track record, and I asked ChatGPT the same question, and I got my answer, right? It's like, "Ah, okay, so it's trained on that." This is not to prove that they copied my data, right? That's not the point at all. My point is, "Good. The information is not lost." One of the things didn't notice there, by the way, Facebook, when I asked ChatGPT, he was like, Hey, did you copy my answer? And it said, "No, no, no. There's just only so many ways you can do this." Got a bit defensive there, which I enjoyed. But the other part there is that I had written this originally in Java, but then later in old JavaScript, because it's 10 years ago and it rewrote it to modern JavaScript. It's like, this is awesome. It had all my variable names in there, which is how I recognised that it was mine. It was like I used some not ideal variable names back then, and I could see them reappear, but then with much more modern codes, that sort of is great. It's the right answer that might help train it on, but then apply to current needs of developers. It needs to be time scrapped, needs to be, not with callbacks, but with async awaits type things. That was really awesome to see. So I love that. That just leads to the fear indeed of with the decline of questions on Stack Overflow, and there's really hardly any questions coming in anymore. It's like, where do we create new training material? And I see that some developers are going to other platforms, so I see an influx in Reddit of these types of questions that honestly, they should ask on Stack Overflow because the helpers are still there \[inaudible 00:51:49\] on Stack Overflow. We're still trying to do that. But yeah, I'm still looking for the better location. I write more blog posts than I did in the past, but I also know that the training, these are less likely to be used as important training material than answers on Stack Overflow, simply because they're disassociated from the 30 million eyeballs that I get on these, and that is rough. I really am wondering what the next tool is going to be. So if anyone has an idea, then reach out to me on socials or something, let me know. I'm really curious about where we can as a community create more of this knowledge base that Stack Overflow is. **David DeRemer:** Yeah, well, I hope they figure it out because you've had such an impact on so many people over the era where that existed and obviously continue to make an impact going on into the future. So obviously we could talk all day. We might have to have you a second time or as many times as you'd like, really appreciate you sharing your story with us and for all the things that you've done and just the commitment to teaching and helping the developer community grow, and like you said, making the ability to make apps accessible to more people. Really thank you for that effort and for being a friend and someone to come on and impart your wisdom with us. **Frank van Puffelen:** Thank you, David. Thanks. So much for having me. I look forward to doing this again. **David DeRemer:** Awesome. Sounds good. Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review and don't forget to subscribe to get our latest episodes. Thanks again, and see you next time. ### AI and Product Leadership in Enterprise Growth Source: https://verygood.ventures/podcasts/michael-harker-ritchie-bros-the-future-of-product-leadership-ai-customer-empathy-scaling-legacy-enterprises/ On this episode, we sat down with Michael Harker, former Vice President of Product Management at Ritchie Bros., the world’s largest marketplace for used commercial equipment. He shared what it took to transform a 70-year-old company while supporting customers that span from hands-on workers to global financial institutions. Michael also unpacks how they framed technical debt in terms of customer problems, why speed became the guiding principle for transformation, and how strong product principles made alignment easier. {% audio_embed url="https://player.cohostpodcasting.com/2be31cce-c9b5-48d9-a391-e5c546ba15b4/f4e60fb3-0034-47b3-912e-448b1c08e253" /%} ## Transcript **David:** Hi, I'm David and this is Build to Succeed from Very Good Ventures. Today we meet with Mike Harker, VP of product at Ritchie Bros., a leading omni-channel marketplace for heavy machinery. In this episode, we're going to get into the nuances of product management within a large organisation with very complex user needs. So let's get building. All right, Mike, thanks for joining us today. How are you doing? **Mike Harker:** Of course, I'm doing well. How are you doing, David? **David:** I'm doing wonderful. To get us started, give us a brief intro, maybe introduce yourself, what you do and get us started. **Mike Harker:** Yeah, yeah. Mike Harker. I'm currently VP of product at Ritchie Bros. We are the largest marketplace for used commercial equipment, so think of us like eBay for all the construction stuff you see on the side of the road that makes your commute terrible. **David:** So all the big equipment, big construction machines. **Mike Harker:** Big yellow iron trucks, tractors, all the way down to government surplus items. We've got a whole website devoted to that. **David:** Super cool. Did you play with big trucks and stuff as a kid? **Mike Harker:** I did, although I never got the chance to go to... there's a place here nearby in Philadelphia where I live called Diggerland where you can pay to play with some of those toys and now I can just go to one of our yards and drive them around. **David:** That is pretty cool. I'm going to have to look into that for a team building exercise, sounds pretty good. **Mike Harker:** Oh, yes. Or with the kids. **David:** Yeah, great. And I know from our previous conversations that you're big into IRONMAN competitions. **Mike Harker:** I am. **David:** And so to get us started and want to hear a little bit more about that experience, and maybe how doing something as crazy as that has shaped you personally and professionally. **Mike Harker:** Yeah, it definitely is a core part of who I am. I would say it's more than just a hobby. I've got one of my race placards right there. It's funny thinking about this and how IRONMAN relates to product work. It's all about consistency. It's about breaking things down into manageable chunks. I remember the first race I ever did, I'd never done a marathon and you run a marathon at the end of the triathlon, and it was just one mile at a time. I didn't think about the daunting task of 26.2 miles. It was like one mile to the next state station, one mile to the next station. And I feel like product's a lot like that, especially going through transformation where you've got to treat this thing like a marathon and not a sprint or you're just going to burn. **David:** So how do you then reconcile that with sprint being a common language term? **Mike Harker:** I did not think about that. You've still got to show progress along the way. That's the key is you've got to put in the legwork. You've got to make sure you understand what's going on, and you've got to have a way to be able to adapt because inevitably things are not going to go the way you would plan no matter how much you thought you trained or planned. In a race, you may have a rainstorm that comes through, you may have crazy winds that come. It could be a lot hotter than expected. You've got to find a way to adapt your strategy to it. And I think the same is true with the transformation, especially right now. I joined Ritchie five years ago and where we are today with generative AI and the tools at your disposal, the plan we thought we had five years ago, completely blown up with the new things we can now bring to be able to accelerate that transformation. **David:** The other thing I think of has been interested in triathlons, which IRONMAN is obviously a very extreme version of is it's not a single thing. It's not just running a marathon, it's not just running a bike race, it's swimming, running and biking. Do you think that sort of multi-skill, multi-focus has changed how you think about things? **Mike Harker:** I do. I've definitely gone through ebbs and flows in my life, both in my product career and in training where you say, "Right now I'm feeling like I'm in a season of wanting to focus more on... I want to learn about the technical side," or, "I want to get better at cycling." So the other two, they're still going to sit there. You still need to do them as part of your job, customer research, but right now I want to get a little bit deeper into this and so there's these ebbs and flows of, "Where do I as a person and as a leader want to focus my time into making sure I continue to upskill and be able to stay competitive both in the work environment and in racing?" **David:** Nice. Yeah, I think you mentioned AI and how that's changing things and people are talking these days about the rise of the generalists and how to be successful in the future when you have all this capability at your fingertips. Being a generalist, being able to do a lot of different types of things is going to be increasingly important. Has that kind of mental framing for you helped you in your career navigate some of these changes going on right now? **Mike Harker:** It has. I think about my own career history, what got me here and where at times I think all of us as humans feel a little bit inadequate in one area because I didn't come from being a developer in, and so maybe I feel like there's a need to hone some additional skills, but I think one of the areas that it excites me the most is the fact that this is still so highly unknown of what a future career is going to look like as a product manager. So while today it is skewing a little bit more towards generalists look like they may be the most successful, another two years with these tools and who knows, it may be people who are extremely good with linguistics, who can write the prompts very crisply, don't need technical knowledge, don't need design knowledge. It's hard to say. **David:** The product world especially I think you have the skills to the point of being a linguist to actually articulate what a stakeholder wants and what an end user wants. And so I think the linguist thing is interesting. Right now we have to be able to learn how to talk to these tools in order to get what we want back out of it. And I'm curious, have you been able to pick up some of the AI tools and your team faster than other parts of the business? **Mike Harker:** Yeah, we're definitely using them a lot faster than some of the other areas within Ritchie Bros. Mostly I would say we're using them for a lot of the discovery and creation of... I mean, we're still doing a decent amount of writing because transforming an organisation, one of the things we found is we need to find a way to bring stakeholders along. And having a document people can read is still critical for us at this stage of where we are in our evolution. So we're using tools like ChatPRD, ChatGPT, Claude, kind of a mix of the different tools for people to help craft one-pagers, PRDs. And then we're starting to work more and more with tools like Lovable to do that initial prototyping where we run a very lean team and we have an extremely lean design team. So the more a PM can come with a visual concept of what they're looking for, the easier it is for the designer to get a sense of, "Okay, directionally this is where you want to go. Let me take it from here into actual Figma mock-ups all the way through to development." **David:** Definitely want to dig into this more and I think especially because Ritchie is such an interesting company, what you guys do, it's the fastest way that I've come to understand is you guys auction marketplace for very big heavy machinery cranes and dump trucks and all kinds of heavy hardcore stuff. And I think that there's a lot of nuance that I know about your industry and your company that definitely want to get into. But to get us up to that moment, tell us about your journey. How do you end up where you are now working for Ritchie, doing what you're doing? How did you get to this moment? **Mike Harker:** Yeah, it's a really interesting question. So I started out my career at a small startup company where we were trying to be a marketplace for carpooling for people. "You and I live in a similar location, let's share a ride." It was around when Uber was really starting to take off and our model was you're not getting paid, you just happen to be sharing a ride together. A lot of learnings, a lot of failures there. Ultimately Waze ended up implementing this and then shut it down during the pandemic. But I took that, I went back to business school because I knew I wanted to do a slight pivot and break into a little bit of a larger company. And I ended up going to SAP after that, really focused on enterprise software, learned a bunch of different areas of the business and found that my passion always was something in product. It's what I did in the startup. And so I had the opportunity to go to Comcast and build products for the multifamily space, which was a small group, so we had the ability to be nimble like a startup, but within a much larger organisation that gave us the backing. During the pandemic, just talking through my network, found out about Ritchie Bros., had never heard of it before. Now occasionally I see it when I'm driving down the road. I just did a cross-country move and so drove past a couple locations. But the opportunity here was to get deep into understanding who our customer bases of a 70-year-old company that has been built on trust and friendship with our customers and how do you take that and modernise it so you can continue to scale for the future and bring the next generation in who maybe do want to be a little bit more tech-savvy and a little bit more technology forward, unlike their parents who were, ultimately the ones, a lot of our business is with mom-and-pop shops. **David:** When you first took the job, was that the part that was the most exciting to you or has that changed over time, sort of that working with the mom-and-pop? **Mike Harker:** That was definitely an exciting part of it. Being able to work with the customers and see how strong of a voice of the customer we have here at Ritchie Bros., where we go above and beyond. I've truly never seen a company where employees are willing to do whatever it takes to be able to meet the needs of a customer. It's an incredible thing to see. The flip side to that is, well, when you're willing to do whatever it takes for a customer, you don't really have standard business processes that you're going to go and rebuild as you try to transform as an organisation. So how do you still allow that flexibility without allowing the one-off requests to always meet their way on the roadmap? One thing is I was thinking about career. I was a DJ for about 10 to 12 years. I did that on the side and I was thinking about this. People always come up, "Oh, you need to play this song. Everyone's going to love it." And it's a lot like product, being a DJ, you need to know how to read the room, understand what people want, because one person asking for something does not mean everyone's going to enjoy that feature or that particular song. So that was the challenge here at Ritchie Bros., is how do you take a company that really does cater to any individual and find a way to standardise it so as a business, you don't need to continue to hire heads to be able to scale? **David:** I could see how as a product person, that opportunity is very compelling because a lot of times you're balancing the needs of the stakeholders and the business with the needs of the end users. And I think the product... At the end of the day, you're trying to really deliver the thing that the user really wants. And so to have that sort of cultural company mandate to help the customer must be very attractive and exciting. **Mike Harker:** Yeah, it was. And look, there was also the great opportunity to be able to build a product culture the way that as a leader you would want your product engineering and design teams to be able to work. **David:** And let me dig in that a little bit. I think product is one of those things as a function, as a discipline that these days I think is pretty firmly established, especially for tech companies and digital product companies. It's a vital thing we need to know how to do. I think that there's still some confusion sometimes around product and even that whole stakeholder versus user and what actually do you do? How do you define product in what you do and in the world at large? And what is the highest value that product provides for the type of function that you provide? **Mike Harker:** Yeah, I hate to say it depends. I think it really depends on how the company wants to view products because I could view it in a very different way, but ultimately for me it's empathy and understanding of your users, whether they be employees or and customers, and being able to bridge the gap between the two. Because you're going to have competing interests no matter where you are, even within your own product organisation. And ultimately, we're here to help be a sounding board and a voice for all of those people because it's back to one person says, "I know if you play this song, everyone in the club is just going to go crazy. They're going to love it." It's your job as a PM to understand deeply, "If I do this one thing, is this actually going to be the thing that gets everyone excited or is it only going to serve one customer?" And now we've just distracted and left the other 99 completely on the side of the road with no enhanced value and benefit for ultimately, at the end of the day, we're a publicly traded company. What matters to us as a company is meeting our stakeholder and shareholder expectations, which is continued growth, continued improvement in our operating margin. And so when you understand that's the context within which the company's operating, you can better start to say, "Okay, where do I focus my and my team's time and energy to make sure we're building towards ultimately what's going to help lead the company to success?" **David:** I love that metaphor. That's a great way to think through product, the DJ, right? Where you can read the room and kind of understand the vibe and make choices live on the fly because I think maybe some people are okay with just a Spotify playlist, but of course that can't react. Or you could go the jukebox model where everybody can manage the queue and put in the next song, and that could be all over the place. And you can imagine how following those different models would fundamentally change the type of product you put out in the world. **Mike Harker:** Oh, yes. Yeah. If you've been to a bar that has a jukebox, you've heard how drastically different the songs are that come on. It can be fun, but it's a little strange. **David:** Yeah, I love that notion of having the knowledge, the expertise, the ability to read the room and the vibe and make the right choices. Yeah, no dancing queen for this reason, right? Well, let's put that to work then. So the skills you developed building products, I mean one of the things that we're all trying to do out in the world is create digital experiences that connect with our end users or employees or customers, whatever they are. Can you take us through some real world situations where maybe you really felt your expertise had been honed and you got to a point where you were able to lead your team through complexity and maybe a major transformation that Ritchie Bros. was going through or something to help figure out how your product leadership evolved? **Mike Harker:** Yeah, absolutely. So a couple of years ago, Ritchie Bros. has grown through acquisition over the years, and we found ourselves at a point where we knew we wanted to bring different brands together and unify our go to market approach. We couldn't do it with the technology we had. So the limiting factor for us was basically our tech stack and being in the industry we're in where we have a lot of long-time customers who are not tech-savvy, the worst thing in the world we can do is introduce a bunch of change. And I think a lot about the Amazon two-way door, we wouldn't have the ability for people to opt into a new site or not. The reason why is we didn't have the technology to support it. So what we had to do was think kind of deeply about it and say, "Look, are we willing to maybe put in twice the effort to re-platform our current website without making UI changes and then be able to go and make those changes later?" Ideally, you just do it all at once. You get the most bang for your buck, you already opened up the car and you're working on it. But ultimately we ended up compromising and saying, "What's in the best interest for us and for our customers is, let's make sure we are stable as a business, we can continue to scale and grow as our volume increases, and then we can focus on the actual user experience itself." Because the hard part with these transformations is people say like, "Oh, well, it's just tech debt. It's tech debt." It is, but it's also in the framing of the problem. Instead of saying, "Oh, it's tech debt." Look, our customers can't get an accurate invoice or an accurate ability to see how much money do they owe, how much is remaining on an invoice. That's a real customer problem that you can go and solve rather than trying to ascribe it to this giant bucket of just "modernization." **David:** Yeah, that's interesting because I think you're right. A lot of times when people are talking about a transformation, there's a lot of things tied up in that. There's tech debt and how do we migrate from one system to another, but there are all these other adjacent changes. So you guys were able to keep essentially the customer's expectation of how to engage with the platform and the tools while building the underlying layers? **Mike Harker:** So we basically rebuilt the underlying foundation, went API first, before everything was very tightly coupled and the biggest surprise and the thing that ended up taking longer, Silicon Valley Product Group put out an article a couple of weeks ago on this, there was so much business logic baked into the core stack of the website itself that no one knew was there. And you have this site that was built over a decade ago. Most of the engineers aren't here anymore. And you come to find out there's things like, "Well, okay, after an item sells, you don't want everyone to see all of the photos. You want to hide the photo with the serial number." There was this logic that lived in every area of the website that our teams didn't even know about. So it was almost a learning experience for the engineers and the PMs as we went through because no one knew that this was a thing and should it still be a core requirement. So in some ways, it also, as we went through, it wasn't just technology transformation, it forced us to ask some of the questions of, as a business, how do we want to operate? Do we want these rules in place? And that's one example. There's hundreds across the board of things that we did. **David:** Can we dig into that? I think that's really cool. When you think about product, right? The exciting part that everyone gets all jazzed up about is like, "We're going to do something brand new. We're going to design a new feature. I'm going to join the startup. This thing doesn't exist. We're going to figure it all out." But I think that the majority of the world building stuff at scale is building on legacy tech where like you said, there's no roadmap. No one even knows it's there, and it's almost more like you have to do some sort of forensics investigation to dig in to what actually is going on under the hood. Was that the engineers doing that? Was that the product team doing that? How do you actually unravel that ball of yarn? **Mike Harker:** Yeah, it was a combination of the two. We started this a couple of years ago. If we were starting now, I would be more excited because I've heard that some of the tools that are out there now can read the code and tell you where there's logic. We had engineers going into the code. We had PMs combing through years of old documentation to see if they could find the original PM who wrote this 15 years ago or at the time, might've been a business analyst, did they document what it was? Because the thing that we could not do, our principle going through this was do no harm. So what we couldn't do is lower customer expectations of the service we were going to be able to deliver. We had to at least meet it, if not jump above it with better performance of the systems. So it was a painstaking process. And I'll be honest, there were little things here and there that we missed because even with people combing through, they're needles in a haystack that if you don't know you're looking for this thing, it's easy to miss. **David:** How do you prevent that going forward? What are the best practices you recommend to product and engineering teams to communicate with that 15 year down the line? **Mike Harker:** Yeah, it's all communication. It's in the communication, it's in the testing, making sure, in this case in particular, our stakeholders all had a voice. Because there may have been one person in this company that knew that that was a feature that existed on the website. We didn't know who that person was. So if you don't give stakeholders and the company an opportunity to test things out and to say, "You know your customers best." Ultimately a huge part of our employee base, they're on the phone with or they're out in the field with customers every single day. So they're the ones representing, this is what they expect. The more we can give to them, "This is what things will look like in the future," the more feedback we can get to say, "Wait, did you think about this one thing that you've always done for my one customer?" Which back to the point of it then gives us the opportunity to say, "Hey, is that a feature we still want to support going forward, or can we shift the customer expectation of how we're still going to eventually meet the same outcome maybe in a slightly different way?" **David:** So when you started this, it sounds like there's the re-platforming transformation side and then there's maybe the opportunity to redesign or improve some things. Your team made a conscious decision to focus more on the re-platforming- **Mike Harker:** Yeah. **David:** ... and then do that later. How did you set up the guardrails or the rules of engagement? I would imagine as you go through this, there's opportunities where you're like, "Well, we could just improve this little thing while we're at it." Right? But I would imagine that would be a slippery slope. So how did you set this up internally in terms of the North Star of what you're trying to accomplish? **Mike Harker:** So the North Star was to get something out the door that looked like the current website as quickly as possible, where we were willing to make trade-offs or where we thought we had the opportunity to say, "Let's enhance this or increment this a little bit beyond what we have today," basically came down to a framework of, "Okay, number one, is it going to take longer? If it's going to take longer than just rebuilding what we have, we shut it out." We said, "We're not going to do it. Our North Star is speed time to market." Number two, is it somehow going to improve stability? If so, trade off of how much longer it would take because we did want to make sure we didn't have any impacts to our auction events. And then number three, if there were visual changes we thought about making, we went to customers, we went to customers, we had employee groups that we would come to with a very quick mock up and just ask the question, "Hey, what do you think about this?" In some cases, if it was a deeper feature, we did some user testing with them, letting them walk through to say, "Are people still going to be able to navigate the site the same way they did before?" Because even moving to, I'll tell you with our customer base, if you move to a hamburger menu on the upper right-hand side from what we had previously, people don't understand. They don't know that that is something you can click to get an expanded field. So you learn those insights, the more you can put designs in front of our actual customers. In some cases, we made trade-offs and we ended up saying we're going to innovate things a little bit and try to stand up a bit of a design system so the pictures may have rounded edges instead of square edges like they did before, because we know that's a stepping stone towards being able to do a full redesign later. **David:** Yeah. Gotcha. Gotcha. Yeah, I mean, how do you balance then that speed versus quality versus user familiarity? And were there moments as a product person where you had to make a decision where, "Okay, the user is familiar with this, but God, is this a tough user experience"? And how did you balance... It sounds like you went to customers, but internally, were there moments where you did have to make a change or a choice and how would you work through that process? **Mike Harker:** Yeah, I think it was hard for all of us when we look at comparisons to other companies out there, and everyone talks about Airbnb as having beautiful designs and you look at them and you crave, "I want to be able to bring that in here." But what we needed to understand and what we know the most is that our customers want something that works. It has to be utilitarian for them. They don't care as much about the actual design language or what the website looks and feels like so much as making sure it helps them with either buying or selling equipment. And so again, we make trade-offs, there were areas where I was challenged by my team deeply, and this is one of the things I love about building a team, is having people that are willing to challenge and not just take from the top down and ultimately tie it to outcomes. If we felt like it was going to increase or improve conversions among our users or clarify some area that we've had complaints from customers for a long time, we made a decision to say, "Yes, we know we are making some changes, which is a little bit divergent from our strategy of get same side out the door with a new tech stack." But it was a very purposeful change that we made to suit the, call it 80% over the 20%. **David:** I think this would probably be a good moment to take a step back and talk about why you are making some of these affordances given the nature of your business and the types of customers you have. Can you maybe explain a little bit the nuance around your particular type of end customer and that idea that they don't recognise a hamburger menu and some of the choices you have to make? Maybe kind of catch us up a little bit about some of that. **Mike Harker:** Yeah, absolutely. So we've got a whole range of customers, which is one of the most exciting things about this company. We have everyone from farmers in remote areas of Alberta, Canada who don't have internet, don't have cell phones, all the way down to financial institutions, large rental companies who are extremely sophisticated with software and tools that they use. So how do you build a product, a digital products, when people don't even have an email address? The most basic thing that you come to think everyone has, when you go on site to some of our auction events and you see it, it's not just older generations. I remember talking to a couple of 26-year-olds from Idaho who had flip phones. They didn't have internet, they didn't have anything on it. And when I asked them, they said, "We don't need it. We're out on the farm and then we're home with our families at the end of the day." So it's challenging to be able to build for a whole range of customers there. In particular when then you look at for some of these people, what we're doing with them, it's not just a transaction. It's not like me trying to resell iPhones. This is their lifeblood and the one that gets me the most, and you hear our CEO, Jim Kessler talk about this a lot. We're trusted with people's retirement. You talk about selling the farm or betting on the farm, people are literally betting their entire retirement on, "I have a package of land, I've got my house, I've got some equipment. I'm going to give this to Ritchie Bros. to auction off, whatever they get is what I'm going to have for my retirement and to pass on the future generations of kids." So it puts a lot of trust in us as a company and it forces us to say, "We need to make sure we can deliver on the promises and commitments we make to our customer." Not just from a business standpoint, but again, these people are literally trusting their life and their future generations with us. You take all of that and you boil it down to, it's not about the most fancy technology or anything here. It's about being able to say, "I have made a commitment to a customer and I'm going to do whatever I can to make sure we fulfil that." **David:** I love this story, and I think one of the things that's been most compelling as I've gotten to know Ritchie Bros. and you and the things that you guys are focused on is that obsession with the customer and the fact that you have these unique customers, that they need this, they need your service. And obviously there's a digital component that you need to follow, but in a lot of the things that I think people are doing with digital products and technology, there's a lot of assumptions of a certain level of technical proficiency that people just gloss over. And I think it's a good reminder. I mean, I think we get into this sometimes when you think about accessibility or are you deploying your service to a country where maybe the infrastructure is good or you don't have to sell bandwidth expectations that we have here. This is really cool to think about going that extra step down like, "What do I need to do to make sure that this is usable for all of the right people in the right ways, even if they don't have an email address?" There's basic assumptions that... I mean, when was the last time someone built a mobile app or a website where you were actually transacting and there was a requirement that says they might not have an email address? It's kind of wild. **Mike Harker:** It is, and it makes it difficult when... I'm a firm believer in, you focus on your core competency for when you're going to build and then you buy the rest off the shelf. And look, we've been fortunate that we have a large portfolio of some very strong commercial software partners, but to your point, they're not necessarily built for this. And look at some of the payment providers who were out there where Uber, Lyft, everything, they're easily under the assumption, if you're going to provide a service of driving, you're going to get paid digitally. Great. We have customers, I remember one in particular, we launched a version of our digital checkout products and we were learning a lot through it. And one of the things was a lot of people didn't want to give us banking information. All they wanted was to get a check cut to them. And so I got on the phone with a bunch of customers to genuinely understand why is it that you want a check cut? And this guy, somehow his business had gotten beaten for about a quarter million dollars of fraud. And so ever since then he does zero online banking. He doesn't want anything that could potentially get out into the ether where he could get beat up. And so he wants a check cut to him. And it's those types of customers and examples of how big is that population of customers who want something like this versus how much are one-offs? Because if you have a good relationship, are you really going to, "Hey David, I'm sorry, but we're no longer going to support this." If so, you got to give him something in return. "Hey David, we're still going to support it. Maybe we're going to have to charge you a fee for it in the future." We haven't gone that far. We're still issuing checks and we ended up having to change a lot of our roadmap. But this is the thing, if you're not talking to all of these different customers, you may take a handful of them who are willing to take online transactions and say, "Okay, good enough, we're going to build for them." And only once you actually get it out there do you learn, "This is not going to work for 40% of our customer base." **David:** I love that notion you threw in there that you changed your roadmap. I think the other thing is that sometimes we get so laser locked on what we think the most efficient or best path is, we stop thinking about how do we solve the needs of some of our customers. If it's a bell curve and we're kind of always optimising for the middle of the bell curve that most people are engaging with or that meet the needs of the most people, we're leaving a lot of people on the fringes in the edges. And that might be somebody trying to sell their farm for a future generation. **Mike Harker:** Yeah. And the other cool part about this, without getting too deep into the specifics, when you're a big enough company that has some of these strong software partners, just like we are taking inputs and feature requests from our customers for what we build, they're willing to change some of their products to be able to build what we're asking of them. So it doesn't have to be an all or nothing or necessarily always changing our roadmap. Sometimes it's putting the burden on our partners to say, "We need your help. Can you make these couple of changes?" Which will dramatically simplify our ability to go to market with this particular offer. **David:** So you have this notion of this particular customer base that you have. I'm sure there are plenty of people who are also very digital savvy and can navigate this and they probably want your tools to be even more efficient and set. But then you have a segment of your user base that is very maybe distrustful or not familiar with a lot of these tools, but you have a kind of company desire to really put the customer first and put the individual customer first. How do you balance, as you think about an industry like this and trying to transform or trying to bring it more tech forward, how do you balance those modern expectations with what's welcome and how do you and your product management decisions on a regular basis, like set up a framework by which everyone's agreed about how you can make these choices? **Mike Harker:** Yeah, it's not easy. The easiest example is probably the email address one. So fortunately for us, COVID shut down our in-person businesses for a while and pushed us online. So you had to have an email address to be able to do business as a buyer. It was a great thing for us as a company as much as it had a lot of impacts all around. But for our consignors, we still don't mandate an email address. And so the easiest way to think about, we'll use this as a specific problem, here are the implications of us continuing to operate without forcing our sellers to give us a unique email address. And this is where partnering with our engineering and technology partners is critical because people don't think about this. And I talk a lot about in transformations, the butterfly flapping its wings in Texas that causes the hurricane on the East Coast. The unintended consequences, unless you're able to clearly paint that picture of it seems simple, but missing this one piece of data has all of these implications, which then result in we are going to have a 20% harder time trying to give sellers access to their sold items immediately after they sell. If we can connect that initial problem to the actual impact and outcome on our customers, it's a lot easier than saying something, "Well, it's easier if we collect a seller email address because it's unique." That doesn't really do a lot for the people who are out talking to customers. And so you really have to take every single one of these one by one, and our teams go and do the investigation to say, "Number one, is this a problem we have to solve? Is there another way we can solve it or offer optionality? And then number two, this is where we partner a lot with our business to say, 'If it's our support teams, we understand you want to continue to allow customers flexibility in this particular area. What we need to agree to then is that what we thought where we were going to cut 50% of your workload by being able to automate it, we're not going to get to that goal. So we have to readjust our expectations of what the product team's going to help you to deliver. You're going to continue to need a staff that's going to sit there to handle incoming calls from customers for things that the rest of the customers can go and self-serve.'" So it becomes a really a lot of give and take to say, "Where, as a company as a whole, a collective unit, are we willing to make sacrifices versus continue supporting our employees the same way?" And I think that's the area when you think about what a product manager's job is, my job is not to make that decision. My job is to collect the information and paint the clearest picture possible to take to VP of operations or a chief operating officer for him to then feel confident that he can go and make the right call. Do we keep our staff levels the same or are we going to make some changes in the way we go to market and work with our customers to be able to hit some of our operational savings that we're expecting? **David:** Super insightful perspective there about taking all of this information that you collect, getting the needs of the customer, the needs of the business, all the things, and putting it in a position where the stakeholders, the executives, the business leaders, can make the right choices for what they want to do. So much of this is tied in transformation process. You have to update that underlying technology. You have to re-platform, you do have to iterate user experience. You have to meet the needs of the customers. And being such a storied business that's been around for a long time, that's built tech over many years, and then you have COVID and you got to swirl and jump really quickly into new technologies. As you've kind of managed moving a product forward, moving the technology forward through all of these headwinds, all of these different needs and kind of things pushing, what have you learned to help you be more successful when you go through these transformations in a large business like this? **Mike Harker:** Communication, you can never have enough communication or enough alignment. And alignment does not mean that everyone is always going to be on board or agree, but if you can set some foundational principles from the start and agree on the framework by which you're going to then go solve and tackle the problems, it becomes tremendously easier when there are disagreements to be able to have people, again, Amazon principle disagree and commit and say, "This is the path we're picking." The other one is, I call it infinite curiosity. I like this concept of seeking to understand rather than be understood. While it's important for me to have a perspective as a leader, especially, there's a lot of humility that leaders need learning from your teams because I know I'm nowhere near as close to our customers as some of the teams who are focused in a specific area like search. And so you have to learn how to trust people. And when you take all of that within your own organisation and are able to start sharing it out within your operations, your sales, your marketing partners, it becomes a lot easier to form a bit of a cohesive unit who can go and help decide, "Which are the problems that are worth us solving now and which are the ones that we are consciously going to kick the can down on the road, accepting the implications of not changing?" **David:** I love that. What's something, if you could go back in time before you start at Ritchie Bros. or maybe earlier in your career and give yourself one bit of advice, what do you think that would be? **Mike Harker:** Never get comfortable, chase learning opportunities. And I'm not saying to chase learning opportunities if it means that you're going to do something for free for others. But I find, and the reason I found myself so drawn to product, I had a career mentor who was... he was the head of corporate development at SAP when I was there. And we were kind of talking about where I saw my career taking me next, and ultimately what landed in Comcast. SAP is a very heavy sales-driven organisation. You basically have to carry a bag there if you're going to get credibility, respect. And he asked me one question. He said, "When you wake up in the morning, what motivates you the most? Is it winning at all costs or is it intellectual curiosity?" And I said, "It's intellectual curiosity." And immediately that's what really helped to focus my mind on. I love learning. It's one of the things I love reading books. I love learning about new technologies, biographies, doesn't matter what it is. I'm a curious person. And I think that's what makes the best product managers is people who are just genuinely curious and want to poke around and find how stuff works. They want to take... It's hard to take an iPhone apart these days, but I used to take my BlackBerry apart growing up and change parts out just to see how this physical device works. And it's harder to do that in the digital world, but it's not that hard to figure out how software works. And so if I went back, I would tell my younger self probably to even be more curious than I was and to take classes, take things that are going to stretch me outside my comfort zone, knowing while it may not seem like it's directionally aligned to a particular path, I'm a firm believer that at some point in my life... I look back on the last five, 10 years and I can see a story as to how I got where I am. If I had started there, I would never have thought that this path would take me to the same outcome. I think that's the key, is it? It's hard to predict life. You don't know what series of steps is going to take you somewhere. **David:** That's super cool. I totally agree that committing to that learning. And also sometimes you get the best learning in situations where on the surface it maybe doesn't look obvious that that's going to be the most fun and the most opportunity for learning. So for instance, if you were to go back to early, you would be like, "Hey, how about you lead product management for a company where a lot of the customers are not into digital products?" You might be like, "Well, I don't know if that seems right." But also, there's so much to learn from those types of environments. And we work with a lot of gigantic enterprise companies and big companies have a reputation for inefficiencies and complexity and bureaucracy and all these things. But one of the things that's really cool is you can really make change. It's like a rich environment of opportunity for ways you can learn, get into that old code someone wrote 15 years ago and kind of figure something out. When you're building something new. And I think we get all excited about startups a lot of time in the tech community, you're building something new from scratch, that has its own opportunities and areas of excitement because you don't know what you're building. But I think that there's a lot to learn from working from very large companies that have a lot of established patterns, processes, behaviours, impediments sometimes, and obstacles, right? Figuring out how to deal with those and optimise around them can be massive learning opportunities. And that's just something I thought is so cool about your story is you guys are moving forward with technology or it's a story business. It's been around for a very long time. Selling, not big, hardcore machinery that you see out there making the world move by building complex structures and doing things. And all the limitations you face creates so much opportunity to do interesting things with product new technology. **Mike Harker:** Yeah, it really does. The ability to have an outsized impact at various levels in a company like Ritchie Bros. There are other organisations that are old companies going through transformations. To your point, you're not going to get that opportunity if you join an extremely large enterprise company that's functioning well unless you're coming in at the highest of levels. And even then, we talk a lot about innovation, how quick it is. Yes, organisations like OpenAI are shipping things incredibly quickly, but what we can do with what seems like a rather small change can end up having a huge impact on our customer base. **David:** Awesome. Well, thanks, Mike. Really appreciate your time today. Learned a lot. If people want to learn about Ritchie Bros., maybe get a job or explore what you guys are doing, how can they learn more? **Mike Harker:** LinkedIn I say is always the easiest place to go for myself. We're often sharing open roles that are going on. We're sharing a lot of our wins. Our sales team does a phenomenal job of highlighting a lot of our customers. Same with our marketing team. Especially when it comes time for some of our larger events, there's a lot of content that's on there and people can feel free to reach out to me on LinkedIn as well. Always happy to talk about Ritchie Bros. **David:** Awesome. Well, it sounds like a great place with a lot of vision, a very customer-focused mindset, and obviously great product leadership. So thanks, Mike. Thanks so much for sharing with us. **Mike Harker:** Thanks, David. **David:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review and don't forget to subscribe to get our latest episodes. Thanks again, and see you next time. ### Developer Relations at Google in the Agentic Era Source: https://verygood.ventures/podcasts/andrew-brogdon-google-developer-relations-in-the-agentic-era/ In this episode, we sit down with Andrew Brogdon, Staff Developer Relations Engineer at Google, whose unique journey spans creative writing, software engineering, and developer advocacy. Known as one of the earliest champions of Flutter, Andrew has played a key role in connecting the SDK with its global developer community. We dive into how storytelling powers DevRel, why the definition of developer relations is changing in an AI-driven world, and how agentic apps could transform the way teams design, build, and deliver software. From the challenges of scaling resources for both humans and AI, to what leadership means in highly technical environments, Andrew shares invaluable insights for developers and engineering leaders alike. {% audio_embed url="https://player.cohostpodcasting.com/13753f16-e309-4294-a4d1-37b549b7b163/c8f79a60-6b76-4eb5-a039-8db7df88e829" /%} ## Transcript **David DeRemer:** Hi, I'm David. This is Build to Succeed from Very Good Ventures. Today, we speak with Andrew Brogdon, DevRel lead for Flutter and Dart at Google. In this episode, we'll discuss the unique nature of DevRel, Andrew's unique journey, how DevRel is changing in an AI-enabled world, as well as some exciting and inspiring ideas for the future of software and app development. I hope you enjoy it and now let's go talk with Andrew. Hi, Andrew. Welcome to the podcast. **Andrew Brogdon:** Hey, thanks for having me on. **David DeRemer:** Yeah, thrilled to have you. I've known you for a while. You're obviously a face of Flutter, which we're big in, and so it's a great pleasure to have you on and be able to talk with you. Maybe to get us started, get us warmed up, I want to know a little bit more about you. So, is there a non-tech hobby or interest that you've picked up over the years that maybe people wouldn't expect from someone in a technical role? **Andrew Brogdon:** Sure. Previous to my current career, I was actually a poet, which is it's always the first question people ask when they see my resume because I have two degrees in creative writing rather than computer science. Long ago before I had children and ran out of free time, I used to write poetry. I went to grad school for it. I don't know how much time we have in the podcast. I can actually give you the 30-second, 60-second story. **David DeRemer:** Let's hear it. I want to hear it. **Andrew Brogdon:** Yeah, sure. So, I originally went to school a long time ago. It was a physics major, switched to computer science, left that, and went to work as an engineer. This is back during the .com era, which will tell everybody immediately how old I am. It didn't seem that dumb to leave school to go to work, and that's what I did. Then it became clear that I needed to go back to school and get a degree. Around that time, I was reading a book on the Java programming language and they were showing how to print text to the screen and the text that they used in the example in this just 400-page Java book was a poem by a Beat poet, a guy named Lawrence Ferlinghetti. Some of the folks listening to this may have heard of him. He's the patron saint of San Francisco Beat poetry. I read that and realised I was more interested in it than anything else in the book. So, I went to a very early version of Google at the time and I threw the first line in. I found out what poem it was, who had written it, what book, ordered the book, then ordered a bunch more and a bunch more and eventually went back to school as a poet. So, then met my wife, realised we were going to get married. Poetry doesn't pay the bills. Even famous poets like T. Elliott was a publishing executive and all of them seemed to have side hustles back in 100 years ago. So, I went back into software engineering, moved out to California for my wife's job as a professor of English, and ended up working at Google. **David DeRemer:** That is an amazing story. I love that. So, you have this interrelationship between your love of poetry and software engineering based on even the origin story of that. **Andrew Brogdon:** A little bit. Yeah. **David DeRemer:** That's very cool. **Andrew Brogdon:** It sounds too cute to be real, but that is the true story. **David DeRemer:** That's awesome. I mean poetry, right? It's writing a succinct text that creates meaning and value, not software, right? **Andrew Brogdon:** Yeah. The smaller it is, the better it is. When there's nothing else that you can take out, that's when it's finished. I like that, that same thing with software engineering. **David DeRemer:** Do you feel like the poet in you still gets to express itself in the profession you've taken on? **Andrew Brogdon:** Very rarely. My creative outlet these days is the jokes that I manage to squeeze into our YouTube videos, I think. So, when we did the thing with Rivers Cuomo last year, for example, I got to write that intro. So, I was writing jokes for Rivers to say, and I have those occasional moments where I'll have a creative outlet like that. Maybe if we do it again, I can get them to read an epic poem or something about Flutter. I don't know. **David DeRemer:** Well, knowing you and getting an opportunity to see some of the work that you've done, the content you've created over the years, things like that that you did with Rivers, it's actually really interesting background and insight to know that you come from that creative writing and that poetry background because now that you mentioned it, I see it in your persona and how you communicate. So, it's something to be said for that and it seems like it has helped you in your career. **Andrew Brogdon:** Yeah, I am by no means the world's greatest poet. I was far from even the best poet in my class in the programme that I was in. So, it's not like going through the MFA programme for three years was setting me up for a poetry superstardom, but I do think there's something useful in my whole identity was about creativity and craft for three years, those three years. What can I come up with that nobody else has come up with? How do I make something new out of the same words everybody else is using? I think being focused on something like that for a significant length of time helps you develop some new skills and a new way of thinking about what you're doing for your own work. So, even though I'm never going to go win the Walt Whitman Prize or a Pulitzer or something for my poetry, I definitely benefited from it. If I am providing value to our community and to the world through the work that I'm doing now, the work I'm doing is probably better for those three years that I had. **David DeRemer:** I love it. That's super interesting. I mean, I think one of the things that's been really fun in my own career, given what we do, meeting in a lot of different people in a lot of different industries, the backgrounds that lead to what we do, everything in our space, we are building software, building applications, but there's so much creativity that goes into it. So, creativity is a skill in and of itself, and you can practise and learn that in many ways. It doesn't just mean designing a file or writing some beautiful concise bit of code. It can come from creative writing and everything else. **Andrew Brogdon:** Yeah, well, I'm sure you need it in your job. You have to lead and motivate and inspire not only the people in your company but also the people that you're talking to as potential clients. This is the future that we can get to together and stuff like that. I'm sure being able to convey that succinctly and correctly is important. **David DeRemer:** Communication, conveying ideas, and in a way, software applications are communication and conveyance of an idea. You sit down with an idea, creativity. I actually want to rewind back to that statement you said because as you were describing what you were doing in those three years, it sounded like you could have been describing a CTO at a startup bring that creativity and thinking, "How do I create something interesting no one's ever done before with words?" Same idea. So, you're in DevRel now, which actually seems like the perfect intersection of these things, of being able to actually convey ideas to people effectively. We know you started in creative writing and poetry and then you got into Google. What's been your journey through Google to your current role? **Andrew Brogdon:** So when my wife got her job offer, we were living in Ohio. The first thing I said to her after congratulating her was like, "I'm going to go work at Google." I made that a life goal. Specifically the thing I wanted to do was be one of the people on YouTube. I had seen the folks that were... I think it was Polymer at the time that they were advocating for and teaching people how to use and the idea that I might be able to do that and have that scaled effect on the developer community, that was something that I wanted and be one of those people that was helping everybody was something I very much wanted. So, I applied as DevRel to Google. It took me two tries to get in the company. I was so nervous for my first interview. I \[inaudible 00:07:19\] screen. I could not have told the guy my middle name if he'd asked. I was so nervous. But the second one, I did a little better. DevRel just seemed technical, but it also just seemed fun and it seemed like you would be able to see when you helped somebody. In a lot of ways, you would see somebody be like, "Yeah, that helped. Thank you for that." That was the thing that I wanted more than anything to know that I'd done something to help other folks. So, I started in Ads DevRel a long time ago. This is like 2014. I was a wide-eyed Noogler and I was hired by a great manager, a guy named Adam Rogal who immediately left for Uber and now I think is in Door Dash, but he taught me the ropes. I had another good manager there. At a certain point with Ads, I think a lot of people, they get to a point where they're either going to stay in Ads forever or they're like, "I've done this for a bit and I want to go do something else." I got really, really lucky and saw an internal job posting for Flutter. So, this was winter of 2017 and they were looking for an engineer on the DevRel team. So, I went and talked to Matt Sullivan at the time, who you would remember. **David DeRemer:** Oh yeah, Matt Sullivan. **Andrew Brogdon:** Great manager Matt and schemed out a way to work on the AdMob plug-in for Flutter as a way of testing it out and also trying to play my way onto the team. So, as soon as I started coding with it, I was like, "Well, I don't want to code with anything else. I'm ruined for other forms of development. I just want to do Flutter now." So I stuck with that and joined the team as the first peer engineer on the DevRel team. Eventually, Matt moved on to another role and the management job opened up. This is right at the start of COVID that happened. So, I ended up in the manager role. So, I stopped working for a living as I like to say and just started doing spreadsheets and emails and reports and stuff. Now the job is very different because I ended up enjoying the DevRel work vicariously through others. There are a lot of wonderful folks in the DevRel team and I get to watch what they do and occasionally give them advice on how they can get more out of their work and stuff like that and then go tell people how great they are, get them promoted. **David DeRemer:** So as the poet Andrew, do you miss doing that actual work? One of the things about being a manager, you do have to live vicariously through it, and I like that phrase scaled effect and that idea of what you were hitting on there for DevRel, how you can have this scale and impact on there. I think the same thing applies to management, your ability to magnify and amplify through your team and make your team more productive than what you alone could do, but sometimes we want to get into the work. I mean, I struggle with this. I haven't written a line of code in a long time and I miss it. I miss it. So, where are you in that journey? **Andrew Brogdon:** You bring up a good point. There's a positive and a negative to it. On the positive side, I love the access to information that being a team lead gets me and the ability to be in certain conversations about where are Dart and Flutter going in general or what is Google doing in general about this thing and being able to know what's up and to earlier in a work stream be able to affect it and to be able to slot people on my team into it in a way that's beneficial for them and whatever we're trying to do. That part, I'm really grateful for. What took me a long time to get used to is while you're doing that, you're slowly losing your knowledge of certain other things. There was a point where I felt like I'm a really good expert level Flutter developer, and now I've had to give up some of those skills. I haven't had as much time with hands-on keyboard. Similar to you, building things that way. So, if you asked me to do certain things that are very recent Dart language features or Flutter features, I'd have to stop and go read one of the docs that my team created to review it and then go in first. I was joking somebody the other day, it used to be if you asked me how to do something with Flutter, I could give you an answer. Now if you asked me, I'm like, "Well, I know somebody you can talk to," and my job was to make sure there was somebody that you could talk to who's an expert in that thing. But I have to deal with the fact that I am no longer that person and that's been a challenge. **David DeRemer:** Yeah, it definitely changes, but you need people like you to be rising up so that the next generation can be amplified and know how to be successful effectively. We've also here talked a little bit about DevRel in general and I think people can maybe piece together a little bit about what the role is. Maybe take a step back and how would you define DevRel in terms of what you do and its impact on the world? **Andrew Brogdon:** Sure. I think every company and even teams within a company as Google, they all have different definitions of it, it seems like. I tend to think of myself as the engineer closest to the door. So, if you come in to talk to Flutter as a thing, at least the Flutter team at Google, I'm the first person you would see and I would be there to say, "Hey, thanks for stopping by. What can we do for you?" and figure out what problem you're trying to solve and how we can help with it. Hopefully, as a metaphor, that's not too bad, but I think the only reason Flutter matters in the world is that people use it to build things for actual users. There's a joke that sometimes goes around the office that the folks working on Flutter don't actually spend a tonne of time building apps because they're not application engineers. They're SDK engineers. That's just the job that they have. The thing that, like I said, makes Flutter matter is and causes it to be like how many minutes a day are you spending with Flutter on a device as a user? The only reason that's not zero is that people outside the Flutter team have built things that matter. So, they can't do that unless we connect the people with the technology. So, there's outbound ways of doing that where we're making sure that we can explain the value proposition of Flutter and help you make a decision about whether you'd want to use it and we can help you get started as quickly and easily as you can. We can do things to help make sure the community that you're joining either explicitly or implicitly when you start using Flutter is a healthy one that you can be proud of being a part of. Because there are certainly tech communities that people aren't proud of being a part of and things like that and then there's also internal work that we do. Every time that we go to an event like a FlutterCon or a Flutter event like that, we would want to hold a listening session. We have notes that we take from conversations. We talked to somebody who had this problem or where they were happy about this change and things like that. All of those get funnelled back into our engineering and product teams to make sure that that connection from the person who we're trying to help by making a tool like Flutter and the people making that tool are as in sync as we can make them. So, it's a lot of communication. Now as I'm stopping and thinking about it, it's a lot of two-way communication and a little bit of teaching and a whole lot of engineering thrown into the same time. **David DeRemer:** Sounds like a lot of care too for the community, making sure you do right by them and you listen to them and engage them. Kudos to you and the others on the Flutter team, both current and past. I think one of the most exciting and interesting features of Flutter has been the community and the general positivity of it. You mentioned that some communities, it's maybe not as aspirational to be a part of it or they're a little bit more confrontational or something like that. For the most part, I think Flutter community has been very welcoming, friendly, and engaging. That has to be by no small part or from no small impact of you guys. **Andrew Brogdon:** It is funny, like Philip and Emily who were two... They're no longer on our DevRel team, but they were a few years ago. I remember they were working on a blog post to define what made the Flutter community a little bit different and what were the principles that we'd want to see. It's like 2018, 2019, where everybody in Flutter just looked at each other and they're like, "We're not going to screw this up. We're going to spend time actively making sure it stays this way." We all just agree and everybody agreed, and that's not just DevRel. The engineering team, the product leaders, everybody knew that the folks that had already started to gather on Flutter, they weren't a typical developer community. They were far more welcoming. They're far more creative and fun and all of those positive things. It was pretty clear even at that time inside Google that it would be a crime not to make sure that we kept that going. **David DeRemer:** I love that. How has it changed over the years, DevRel as a profession and with the tools that you use and the approaches and how you engage the community? How has that changed and how do you see it changing into the future? **Andrew Brogdon:** It's changing a lot right now. That's a good question. I think a lot of things we do still would've been the same. We'll do them slightly differently now, but they're the same things that we would've done eight years ago. I wanted to make YouTube videos the day I got to Google, and I've mostly been making them this whole time. The facilities have gotten a little bit better definitely. The first studio I worked in at Google, we'd occasionally have to stop in the middle of the take because someone would walk by in hard-soled shoes outside because it was just a random meeting room that they put sound insulation in and a camera. Now there's a dedicated studio and stuff like that. So, some parts of it are more sophisticated than they used to be, but the core of it is still just getting out and talking with people and those things have not changed. I think one of the interesting bits now is the entire team exists to help solve developer problems. We want to make developers happy and fast and to help them improve the quality of their software, Dart and Flutter specifically. In the last year, two years, it has become very clear that that experience as a developer is going to be more and more defined by AI powered tools. So, now we're thinking about things like, okay, if a certain percentage of the code is going to be generated by an agent and then reviewed and modified by a human being, that's the near term feature, I think, speaking for myself, just to be clear. That's the experience that we need to make as good as we can possibly make it. So, one of the things that we're thinking about now is how do we make these agents code better and not just humans code better. We have a bunch of developer resources for human beings to read and use to build their own skills. All of these models were trained on it because it's all out in the open. You can find it all on GitHub and on our website and stuff like that and on YouTube and things like that. But an example change that we're talking about right now is should we be shipping a markdown version of our release notes so that if you're somebody using an agent, hopefully Gemini, but whatever anybody would like to use, Claude or any of the others, that's not going to be trained on a new stable release of the SDK the day that release comes out. So, for something like dot shorthands, which is a new feature or well on its way to coming out where you won't have to put the class name for an enum, let's put the enum name, you can just put dot and then the value for something like cross axis alignment when you're building a column or a row in Flutter. You won't have to put cross axis alignment:cross axis alignment.start or something like that and you just put .start at the end of that. If your model hasn't been trained on that, it's going to have no idea that you don't have to do that anymore. So, you're going to generate code. You're going to have a whole bunch of analysis errors where it's like, "Hey, you don't have to do this. You can fix it," or we can get ahead of that by giving you a markdown file with every release. It's like here's the three things your agent needs to know in a markdown format. Go paste this into your AI rules and now we've done that work for you and made that experience better. That is an entirely new challenge for us. We've never been in the position of needing to upskill AI agents before, but if we're really trying to help developers be happy and effective, that stuff is going to start coming into our work more and more. So, that's a challenge to figure out not only how to do that stuff, but what are the things that would actually matter and will actually help? **David DeRemer:** It's fascinating. So. much of the work that you guys have done before and when you think about tools, how would developers learn things and what were the communities where DevRel would maybe play? The YouTube videos you put out, the content, the blog posts, talks you give in an event, questions answered on Stack Overflow, all this stuff has helped to train these AI models, but it's fascinating to think that in the future, developers will be agents and agents supported. So, even regardless of where this all goes and obviously it's a hot topic, let's assume that a developer for some class of app will just be AI. Now your target audience is no longer just human developers, but agentic developers. So, the content you want to deliver to them has a different format. What's more efficient for them to ingest it effectively? **Andrew Brogdon:** Yeah, I think it'll also be really interesting too when we get to a place like that and suddenly the bar for software experiences just starts going up. There's that interesting question of, okay, software as we know it, we can now automate more of it and make the same quality up to a certain point more chiefly and efficiently. Do we then keep that model for what we expect of software or does that allow us to now have much better software and just the top end of the quality bar goes up? Now Flutter needs to make sure that we're supporting people all the way up there with things like more workflows that are more adaptive to users and things like that, more personalised stuff like that. You might remember from Flutter Interact, this would be the way back machine, where Grant Skinner had his vignettes and some of the demos that people were doing. These are radically new UIs that at that moment someone would've to sit down and really work on to get them right. Maybe somebody has time to do that, to really get those bits right, because all of the rest of it that's the well understood part has been done with an agent. So, now people come to expect things that are that much more custom and interesting, something that looks like a television programme written just for you or something like that. It'd be interesting to see if we suddenly need to have resources for this is how you make software that just wouldn't have existed three years ago or something like that. **David DeRemer:** There's a word I've been using recently about abundance. These technologies create this opportunity for abundance where you can just do more things and more interesting things. I think I saw you in the audience at FlutterCon when we had the panel with Lawrence and Michael from Universal, and they said three things about their adoption of Flutter that really stood out to me. One was the opportunity to invest more in animations that were these delightful little things that Flutter's efficiency gave them time to invest in those types of user experience enhancements that maybe they wouldn't have otherwise. It doesn't really necessarily have a huge business case on paper, but it improves the user experience, increase the magic and the things they're looking for. The second thing was the expansion to the kiosk work where it's got an incremental impact on the business, but to maintain a brand new software application and a different thing with another team, the extra cost of creating it and then maintaining it indefinitely, the ongoing cost, maybe it wouldn't have happened, but because of Flutter's efficiencies and the portability of that code, they could add that extra business value. Then the third was they had space now to maybe explore ways to get agentic experiences into their app because Flutter creates this space. I just heard you also come to then talking about those same ideas that I've been connecting with Flutter, and I think those of us in the Flutter community have been talking about for a long time, to AI, which is like that efficiency, it allows us to do all this extra new stuff that otherwise we wouldn't have time or resources to do. It's super cool. **Andrew Brogdon:** Yeah, I think it's been a constructive way to frame it, and I'm not the only one doing this where I work. A lot of people have anxiety about how AI is going to come in and change things as we go forward and stuff like that. I think a useful way to frame it is could we use this to make it possible for us to do all the things we wish we could have done over the past several years, all those things we wish we had time to get to? How do we automate away the stuff that we're not excited by so we can work on that? **David DeRemer:** So when you think about the future of DevRel, I mean in a way, on one level, there could be this little intermediate gap where all of a sudden, your workload is doubled or increased a little bit at least because you still need to do things for developers. It's not like you can abandon the human developers and not create content for them, but now you have to also create maybe new types of data or MCP servers or different formats or things that allow the agentic developers to get involved. What's the crossover of that look like? Does that go on forever? Because one of the things people talk about is you even said in your description, we'll probably be sitting on top of this and observing. At some point, I think for some unclear time horizon, I don't know if it's one year or 10 years, we'll still expect a human to be able to get in there and understand the code. So, you still have to educate them. You still have to make the content available to them. How do you plan that out over the next... It seems like no one knows. **Andrew Brogdon:** If I had a roadmap for that, I'd be promoted immediately, but no, I think of Waymo and when do you take the safety driver out of the car? When do you trust what it's doing enough that you don't really need to have a human being getting into it and checking out the underlying code and at what scope can an agent do that? If you have a top app, and again speaking for myself here, you could not turn that over to an agent right now and just be like, "Yeah, well, there's an agent out there that can go code this top 100 app for iOS or Android." But there is a scope where you can do that, analogous to the little geo-fenced area that these cars go in where they're like, "Okay, we're good if we stay in this box and that kind of a thing." So I think there will always need to be people and these agents working together when you get to a certain point of quality and size. For my remaining career here, I anticipate there will be a need to be human beings that can get into the source and look at it and diagnose it and a large number of those folks. What I'm hoping is that some of the things that we do in order to make agents better will also make people better in the same way, and I'm going to give you some rope here. I'm going to compare this to accessibility. I remember having a conversation with somebody once about the curb cuts that they have at corners now, which didn't really exist when I was a kid 40-something years ago. You'd go to a curb and you go to the corner. It was just a drop-off. Now most of them in my neighbourhood have the little ramps with the bumps. Those were put in as an accessibility measure to help folks who use a wheelchair to get around. They can get around more easily, but that also helped everybody with a baby in a stroller, which has been me over the last 10 years, everybody that was carrying boxes on a dolly, something like that. It also helped all those people even though that wasn't the official purpose of it. So, what I'm hoping is maybe by needing to take a step back and say, "Okay, now we're serving these two slightly different populations and trying to upskill them both," how are we going to change our resources and in what ways do we help both audiences at once? So we have been looking at ways to refactor some of our existing resources, and it's forcing us to think about what were the original principles that went into this six years ago. When we made the Flutter Cookbook for example, which is a resource that's been around for a long time, what was it we were trying to do then? Do we still need to do this? In this new world, what is the right way to do it? And if we plan that out and execute it correctly, we're going to help both of these audiences. That is what I'm hoping anyway. **David DeRemer:** How do you show people what's possible when anything's possible? Because the cookbook, I think part of that was to illustrate, hey, you can do these hard things and here's some ideas maybe to get you started and to illustrate some code patterns and things like that. To some extent, I wonder if when you get to a point where I can just sit down and type something, well, how do I know what to look for anymore? I remember not so much anymore, but when I lived in the city and you'd open Grubhub or DoorDash or something like that. You go to order and you just have this abundance of choice. There's a million restaurants you could order from. There's 37 Indian restaurants you can order from. You're like, "How do I know which one's good?" They're probably all good. Almost sometimes my wife and I would just be like, "Just forget it. We're just going to cook the chicken that's in the fridge." We can't even pick. You know what I mean? **Andrew Brogdon:** Yup. **David DeRemer:** And so it's interesting to think about when you get to this world where everything is possible, how do you then create inspiration for people? How do you give them a vision of what they can do if already they already have the tools at their fingertips and making sure that those of us in a position to create that inspiration, take that job with respect and honesty and the importance that it is? Because if we stop showing people what's possible because we just assume that they can just do it because everything is possible, that could maybe come back around to stifle innovation. So, it's interesting to think about. DevRel is just such an interesting spot in this whole thing because you guys are educating developers. There's this new era of developers coming in. Everything's changing. When you think about so much of the anxiety and so much of the change, part of it's on the developer workflow. How do we make things? You're doing that, but also around a framework that has become really popular. A lot of people are using it. How is it changing how Flutter is being thought of and how you think about this going forward in a world where maybe developers aren't directly writing the code as much? What is that impact on Flutter's future vision and where do you see Flutter heading, or I guess another way to think about it would be what's something that excites you the most about where this is all going? **Andrew Brogdon:** That's a good question. I mean, there's some big things and there's some little things in there. I remember having a conversation several years ago with the UX lead for Flutter named Tao. He was very, very smart. This is two and a half, three years ago where he was talking about a lot of code is going to start being generated by agents and it's not actually going to be typed into a keyboard. Does that make readability more important than code terseness, like in the way that Dart's going to evolve as a language? Because do you care as much about how many characters are in use if you don't have to physically type them? And it makes it faster and easier for you to read the code and understand it quickly because we're going to be doing less typing and more reviewing and reading. He was that far ahead because he's a really smart guy, but I think there's going to be a lot of little details like that that will start popping up in some of the planning and already have to a certain extent. So, that opened my eyes at that moment of how things are really going to fundamentally change as we go through the next however many years. One, I think Flutter's core value proposition is still going to matter. I think Flutter's ability to have a consistent experience across platforms from one code base, that's going to matter whether it's an agent that creates it or a human being that creates it, right? You can go right now to one of the code generation sites and there's a lot of great ones. I use Firebase Studio all the time. There is still a stochastic thing. If you give it a prompt, even creating the same prompt twice with the agentive prototyper, you'll get two slightly different results. So, if you go and ask the same platform, same tool, you'll get two different results. So, if we're looking at a future where people want to be able to have a vision, express that intent to the machine, and then have that come to life across devices, the core value prop of Flutter is still going to play into that because you're going to have inconsistencies. You'll have a bug over here that doesn't exist over here. By the way, the underlying code that you were trying not to pay attention to is different and all that thing. So, I think there are core things that will absolutely stay the same. Hopefully, what will happen is we'll start seeing Flutter reaching new types of places and things like that. People have talked about how application models themselves might end up changing. You see every day it seems like the folks working on AI, they have a new way to make mini apps and stuff like that. That's a whole new ecosystem potentially. I have no idea how that's going to inform Flutter and vice versa, but that's probably a thing that will happen as we move forward. So, the reach that you can have as an application developer will change. So, that all I think will be really interesting. **David DeRemer:** Yeah, what's fascinating for me personally about AI is how similar to human interactions and thinking, it's very similar. Even when you think about teams of agents doing things, so I mean, I'm biassed, of course, but I totally agree with you that I think it doesn't make sense for AI to write the same app multiple times either for different platforms. To some extent, I think it actually illustrates the value of Flutter in a way that now I think people can understand because we have new ways to think about it. People understand now that if you ask an AI to do something, it's going to do it differently each time, that it's going to have hallucinations. There's all these things I have to check and make sure it's okay. When you think about it, that's not really any different than if I go to an iOS and an Android team and I say, "Hey, I need a screen that does this thing." If I give them no more information, they're going to go off and they're going to do their thing. They're going to do their product work, they're going to do their designs, they're going to make this app. The thing I'm going to get back, it's not going to be the same thing in both places. So, agents are doing the same exact thing as human teams would do in terms of permutations and variations. So, I tend to agree with you that I think actually AI plus Flutter I think is a massive win. I think at some point, the world will... I mean I think us in the Flutter community, we have a bit of a head start here, but I think people will understand the value of, okay, there's hallucinations, there are non-deterministic things that are going to happen here, and I need to reduce my surface area and increase my ability to review what this thing has produced. To some extent, I had never thought about that, what you're saying about Dart before. It's interesting to think about the programme languages evolve to become really, really useful for humans to review the output, not to write it in the first place. That's a fascinating thing to think about. That's really what it should be optimised for in the future world. Now that you've said it, it makes obvious sense. AI's going to write it. I need to review it. It should be optimised for me to review it. Cool. **Andrew Brogdon:** Yeah. Building on that, the whole thing about specification driven development, that's starting to become a new buzzword, spec driven software. That's going to be interesting, I think, too because now you have the source code that is being generated by a model for you to review the actual code that's going to become the executable, but you also have an agreed upon format for this spec that defines what the application is supposed to do and the intent behind it and stuff like that. Then you and an agent collaborate on so that another agent can then go and turn that into code and stuff like that. I remember another smart person I used to work with, Adam Barth, one of the original engineers on Flutter, talking about Flutter a long time ago. It was like, "How quickly can you express your intent to the machine about what you want it to do?" I think now we're going to end up in a world where we have two different ways that we do that. We have a natural language way of doing it, and then we have the code that is the step where you have to say, "Okay, no, but actually I meant this other thing, so let's tweak this over here." You'll have a code representation and a natural language representation and both of those combine to define the behaviour of the application. But there again, being able to review one set of code to go with your spec is better than three sets or six sets, right? **David DeRemer:** Totally agree. I think actually the Flutter mindset in terms of what it's done, it's very aligned. So, anytime I talk to somebody and they're sceptical or worried about AI and where this is going, I think it's helpful sometimes to root in I think AI in terms of tooling that helps an engineer be more productive is actually the same exact thing as Flutter. It's helping us to be more productive and express our vision and our intent way faster and more productive. Whereas before it was like, okay, how do I reach my users? Well, some of them are iOS, some of them on Android. I have to do that. So, the old way was to do things twice, but now if I can consolidate into one place, it's far easier, more efficient for me to get that to the people I need to have it. But if AI is another toolkit on top of that that makes it even faster and easier for me to express my ideas and get them to my end users, ultimately, it's solving the same benefit that any framework that makes developers more productive is solving. Yeah, it's super interesting. Yeah, lot of interesting. I know you're also interested in agentic apps in general, and so I know just personally you're interested in those things. Let's talk about that. What are agentic apps actually and AI and agentic behaviours in app experiences? Where does that go? What does that look like? **Andrew Brogdon:** Yeah, yeah, sure. It's another thing. I think if you talk to 10 different people, you'll get 10 slightly different definitions or explanations, but I can give you mine. The best definition I've heard is an agent is a reasoning capability, usually an LLM these days combined with tools and a goal. So, you take an LLM like Gemini. You give it tools to access the outside world and have side effects basically and then you give it a goal for a thing you want to accomplish and you package that up and that's an agent. Then an agentic app would be an application that uses an agent like that for some or all of its functionality. I think we're at different stages right now of what people have actually begun putting into production for what those can look like. I think this is a thing I'm particularly excited about because I think it's going to be fun and I'm a fun driven developer. Right now, a lot of agentic experiences are a wall of text where you're talking almost directly with an LLM and maybe it might have a function call or two for things that it can do, but you're conversing in natural language by typing things into a phone or stuff like that. There's times when you would still need to do things like that and use text to talk with an agent. But I think in the future, we're going to see things where someone has defined an experience in such a way that the AI can express itself and ask questions using more UI than just text and a text view. So, we had a demo that we did a Google Cloud Next this year that was a plant debugger. That was the internal name for it, where you could describe what was going on with a plant that was troubled. You could take a picture of it and then it would guide you through what looked like a wizard workflow that was being come up with on the fly based on what you were saying and what you were telling it. So, rather than forcing you to just have an ongoing conversation like you were chatting with tech support or something like that, it was displaying buttons. LLM was given the ability to say, "Oh, I need to ask the user to pick one of three things." The application knows how to ask the user to pick one. So, the LLM would tell the app. The app would throw up some widgets that had buttons and off it would go. I think we're going to have more experiences maybe where an agent is tracking progress on a workflow and deciding what needs to be done and how to work with the user in order to accomplish that goal. You can imagine I was looking at cars today and I went on a car manufacturer website and was just pricing out if I wanted to buy a new sedan. You can imagine something like that where rather than having a strictly defined workflow, it would guide you through something where it knows what questions to ask at the right times and stuff like that and can on the fly present UI, because the LLM has a chain of thought going. It knows how to ask the user things because it's been given tools to do so. It knows how to define success and whether it's close or far away. I think you'll have more things like that in the future where the state of the app is both some local state and also the state of what an agent either running on device or in the cloud has stored within its current chain of thought. **David DeRemer:** Interesting. So, app state- **Andrew Brogdon:** Hopefully, that was an answer to your question. That was a bit of a ramble. I don't know. **David DeRemer:** No, I like it. It's interesting to think about app state as context to the user experience that you're creating. There's a bunch of things that I've done and things I've done in the past and maybe something where I'm at right now and feeding all of that to something that can actually influence the user experience as you're using it is pretty exciting. I was at a tech event recently and a prominent member of the community named Larry, he had this interesting insight around as engineers, what do we want to do? We want to give every user the optimal experience, and we have all these tools that have been invented and even like A/B testing tools and things we can do. We're trying to figure out what's the best experience, but all of those tools, because the way we write software is deterministic and we write things and if-else statements and we got to ship it. Generally speaking, you're optimising for what is interesting to the most people. So, I'm going to test this colour button or that colour button and I'm going to pick the one that the most people like. But what that means is by definition, generally speaking, there's still people that like the other colour, but we just opted to go with the one that the most people liked. Imagine, you get to a point where these on-device agentic UI stuff can really be hitting every single person with exactly what they want from a personalization perspective and giving developers the ability to actually create experiences that really are one to infinity. I'll put this thing out there with intent and some tools that will help to meet your exact needs. I think that's super inspiring and cool. **Andrew Brogdon:** And if you'll pardon me, a callback, it's that idea again of does the quality bar go up? It used to be that we could just go for some local maximum in terms of pleasing the most of our users as we could. Can we get to the point where everybody has that same experience as you're describing? And then some of the new problems that are cropping up, can you measure? How do you measure whether it's better or not effectively? Because there's a lot of pressure right now to just slap AI onto whatever you're doing, but that's going to fade away over time and it'll be like, "Okay, is this measurably better for our users and therefore for us? Are they able to do what they want to do more efficiently?" So they're happier. Our business is stronger as a result and stuff like that. Or did we just put AI on it for the sake of putting AI on it? And where are the guardrails, right? If I'm putting an LLM in charge of an experience, I'll be honest, I have a bunch of apps, these chatbots keep popping up in them. The first thing I do is how can I break this? What can I do to get out just to see if I can do it? That's important. How do you secure it? How do you keep it not only in terms of typical information security, but also in terms of your brand security? How do you make sure that you don't end up on Hacker News for your agent driven experience just did something horrible? Those are still problems being worked on right now that I think are really interesting. **David DeRemer:** Well, and this is where we have this dichotomy of things are moving so fast, but there's so much change because I totally agree with you. All the tools we have for testing, analytics, like A/B testing, app performance, all of these things are based on a deterministic paradigm. So, when you think about, okay, I'm going to build this app and I'm going to set up my analytics events and I'm going to track shopping cart conversion flow, right? That's just based on a sequence of events triggering. But if all of a sudden, I'm handing off large parts of my experience to this agent that is non-deterministic, no longer can I rely on these deterministic based tools to evaluate whether the thing was successful. It's almost like I'd have to hand this thing out to the world and then at the end of the user state, almost ask another LLM, "Here were your business goals and customer experience goals. Based on the state you've observed, was the goal achieved?" Because otherwise, I don't know how you could have a more legit logic-based tool have those outcomes. So, it's fascinating because all parts of the stack, everything about DevOps and testing and customer engagement and analytics, all that stuff has to be updated for things that are non-deterministic now. It's pretty fascinating. **Andrew Brogdon:** It can be unsettling, but it's also exciting, right? **David DeRemer:** 100%. So, I want to ask one more question just about general, your experience. One of the things here on what we try to do here is us understand a little bit what helps teams to be successful. I wanted to take some of your experience coming up through all the way from being a poet, which we learned about today, all the way through to being DevRel, getting into management positions, observing the Flutter team grow and the leadership changes and things and then where we go forward. As you think about going forward, what are some leadership principles that you've observed or that you believe in that help teams be successful with technology? **Andrew Brogdon:** Oh, wow. I think empathy is never going to not be important. That is something that I could definitely say. There are lots of parts of being a manager I'm not that great at. I am a creative scatterbrain. I think the team that I lead sometimes reflects that in certain ways, but really knowing the folks on your team and in your community and genuinely caring about knowing them goes a long way to you making smart decisions. I remember chatting with somebody once and about how one of the things that I've learned now that I've been in various ways in and out of software engineering that I would go back and tell myself when I was 18 if I could is that your ability to work with, inspire, and listen to people will be more impactful on your career trajectory than the technical skills that you're so carefully trying to develop right now. That is something that I definitely had to learn. That's not to say you could be a slouch on the tech. You have to understand the ground truth of the industry that you're in. But if you know the folks that you work with and especially your reports well enough to know where they're trying to go with their lives and the things the way they tend to conceptualise and think about their own work, you're going to be in a better position to help them by lining them up with the right opportunities. You'll be able to anticipate what they might need from you a lot better than if you just don't take the time to do that. So, I think of the managers that I've had, the best ones, they've always had a really good beat on that. That's definitely something that not only has helped me in my career because they're giving me a good advice, but it made it very easy to trust that they understand me and that they're looking out for me. So, if that's an answer to your question, that's probably the first thing that springs to mind. **David DeRemer:** Yeah, I love it. I think that's great. Empathy is increasingly hard to find in the world too, unfortunately. I think you're right. I think we might need it more than ever with the machines taking over because at least us humans, we got to get along and got to help each other out along the way. So, I think that's well said. I think that's maybe a good place to stop. So, thanks, Andrew. That was awesome, so many tremendous insights. I think the DevRel side of this change of AI is something you don't hear about as much because everyone's focused on the models and the pace of change of the developer experience, but I think you guys are right in the wheelhouse of the relationship between the people making things with this technology and the technology itself. So, I really appreciate your insights today and the time spent. **Andrew Brogdon:** Hey, thank you for having me on. I really appreciate it. It's great to talk to your audience as well, and it's always nice to chat with you. **David DeRemer:** Likewise. Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review, and don't forget to subscribe to get our latest episodes. Thanks again, and see you next time. ### Applied Science and the Future of AI Source: https://verygood.ventures/podcasts/pratap-ranade-arena-the-future-of-applied-science-and-humanity-with-ai/ In this episode, Arena’s CEO and Co-Founder, Pratap Ranade, shares how his team is using AI to solve tough engineering challenges in industries where failure isn’t an option. From aerospace to automotive, discover how smart systems are reshaping how real decisions get made. {% audio_embed url="https://player.cohostpodcasting.com/b8a9c832-19c2-4c05-8242-b1102d1898a8/014679bc-7377-432b-8432-e3f1c8a17a41" /%} ## Transcript **David DeRemer:** Hi, I'm David. This is Build to Succeed from Very Good Ventures. Today we speak with Pratap Ranade, co-founder of Arena AI. Pratap shares his incredible journey from studying advanced physics through Y Combinator, McKinsey, Palantir, and ultimately co-founding one of the most interesting and innovative AI startups that are out there. It's packed with insights and I think you're going to really enjoy it. All right. Pratap, welcome. Thanks so much for coming. **Pratap Ranade:** David, thanks so much for having me. Great to see you as always. **David DeRemer:** Likewise. I was thinking the other day about how you and I ... You're one of the ... Not oldest in terms of age, but longest tenured people I've stayed in contact with because all the way back to early 2000s our kickball league, but I think you were still at Columbia at the time and it's just been really cool to see your journey and all these years later still have an opportunity to connect with you. **Pratap Ranade:** No. Thanks man. Yeah. It's awesome having longtime friends who've seen so many chapters and you've seen so many chapters of their lives too, so this is a privilege. **David DeRemer:** Yeah. Likewise. I think it's a great place to start because of all the people I know you have such an incredible set of chapters that got you to where you are today, and so I was wondering if you could just to get everybody in the audience caught up with who you are and the things you've seen and environments you've been a part of, take us through that journey from physics major to now running an AI startup. **Pratap Ranade:** No. Totally. Happy to. So I had a meandering walk, like a random walk a little bit through this as you alluded to. I started out thinking ... I was the kid who didn't grow out of the, I want to be an astronaut phase, and so that just continued through college, Stanford blinders on, just do physics. And then I think it was my junior going into my senior year, the second space shuttle at Columbia exploded on re-entry and you had no more manned spaceflight programme. This is pre SpaceX. And I'm like, well, I only really have a physics skill set. Everyone goes into grad school. So continued on to grad school to be a researcher. Which is actually where I met Ryan who connected us in the first place. He was my grad school roommate. And so continued in grad school for a long time and that was actually in a strange way now today Arena is finally solving problems that I had as an experimental physicist. **David DeRemer:** Wow. That's cool. **Pratap Ranade:** I spent most of my time building physical hardware and I was bad at it. And if you think about the way experimental physics works, you have theoretical physics where you think about a theory and then do the math and you're like, "Great. This is my idea." And then what the experimental physicist does is they build the equipment in the actual experiment to go and sample the universe and see if that's real. You basically, if you think about in my case, you're like a shitty hardware engineer, a shitty electrical engineer, a shitty mechanical engineer and a shitty plumber, all to try and create this little experiment that can tell you about quantum mechanics. And if you think about where this goes to is you publish a paper and hopefully the results are good in telling you about physics and 99% of the time the problem is that you screwed up your experiment. And so I was a bad hardware engineer basically trying to build advanced hardware. That was chapter one, which will become relevant when we get to today. But then of course after grad school, I was in New York and I was inspired by all of the business world. It was moving at a million miles an hour around us. And so actually I went to McKinsey to do some consulting. I'd never done econ at all and I figured this is a good way to learn about the business world. Was there for a few years, got to work in a few really exciting countries in Africa and China and Europe and then missed technology and I left and actually with Ryan, our mutual friend and my old roommate, we started our first company. So that was out on the West Coast. Technically the first few weeks were in Vietnam, but then we went and worked out of his parents' basement for a while in Portola Valley. I still remember we interviewed ADA users, they all said they didn't want it. We panicked right before our YC interviewer were like, "Well, nobody wants this." We pitched it anyway. We got in. And then I remember Sam Altman pushed us pretty hard to launch. I still remember the first meeting lasted seven minutes with him and he was like, "Have you guys launched yet?" We're like, "No, not yet. We have bugs." And he said, "Well, fix the bugs." We're like, "Look, there's a lot of bugs. We're trying to build a general purpose, no code web scraper for any website on the internet. We're not going to fix the bugs any time soon." And he was like, "Well, I want you launched in two weeks." And then we seven minutes into the meeting, he was making an expression at the time. He was like, "Why are you guys still here? I thought you had bugs to fix." And so that was our first lesson of, okay, ship, launch. That was a really momentous experience. We went back. We launched in two weeks. It went viral on launch. We grew over the course of the company, well over a hundred thousand users. Sam and Peter Thiel and Max Levchin all invested. So we had a very crazy a chapter and then a few years later we're acquired by Palantir. So that was the next interval commonalities between Palantir and McKinsey now is we saw again another lens into selling into large complex organisations. And so fast-forward to Arena, we started in late 2019, 2020. And the principle is very simple. My co-founder Engin who I met at Palantir and I basically said one is we want to work on a frontier technology. And broadly speaking, I feel like in any 10, 20 year window of human civilization, there's a couple of technologies that define that era. You can think about the mobile phone, you can think about the internet, the early web. And for us, AI was one of those. I'd taken a bunch of machine learning in grad school. He was an AI scientist. We're like, "Look, there's something interesting cooking here." And this was before LLMs. This was a deep mind era, AlphaGo and early open AI with emergent tool use and things. This is super cool. And then we're like, "What are we good at?" We're like, "We're weirdly good at selling to enterprise." And we said, "What would it look like to do a company than frontier AI for enterprise?" So chapter one of Arena was very industry agnostic and let's go solve hard problems with advanced deep learning and reinforcement learning. As you mentioned, a lot of that early problems were very different from the problems we solved today. They were still very much at scale having a real impact, but there were things about sales and pricing for CPG companies. That was a lot of the early traction. And then we did one pilot in the hardware engineering industry and it went insanely well. It was smaller initially in revenue, but we were like, "Wow. This is really exciting." And we just personally were drawn to it. Fast-forward a couple of years later, post ChatGPT, I think a lot of things changed obviously. Two major- **David DeRemer:** The whole world changed. **Pratap Ranade:** The whole world changed. Thank god. Two major implications became clear. One is ... And then if you go a year or two years after ChatGPT now you see agents emerge. We're like, look, this is a new paradigm. So basically by default, I think in an agentic world, if you aren't agent first, you probably are on a legacy stack. So we're like, "Well, embrace it or call ourselves a young legacy companies. So screw it, we're going to embrace it. We're entrepreneurs. We like risk." And so built agent first. That was the first thing. And the second thing was on a business level, we were agnostic. We had the hardware engineering product, we had a CPG product and we were like, look, in our opinion, to win, we needed to pick a vertical. And if we looked at it honestly, we were deep in two verticals and the question became pick one. And I think now in a world where the cost of developing software is rapidly declining, who knows where the software engineering agents go and these models become more and more general and they can take on more and more ground. I think the question of differentiation that tended to be a question that startups can kick can down the road for quite a while because you build user base and deal with it later. I think that question we encountered earlier. And so we basically said, look, the hardware engineering problem is a very difficult problem. It requires a lot of applied physics. It requires systems. The agents help a lot, but by no means solve the problem. You require additional machine learning systems and the value is very fundamental. It's like can you speed up the work of a hardware engineer? It's not like a blog where you're going to author something and like, oh, it's subjectively good. It's like this is a flight computer for a rocket. It needs to work and you're not going to lie. It's either going to work or it's not, and it's going to be very spectacular if it doesn't work, so you got to get it right. So there was that determinism so we doubled down and we doubled down on hardware engineering. It's been going incredibly well. But yeah. That's a little bit of the story arc until here. **David DeRemer:** Yeah. I love it. I definitely want to dig into that focus, that thing you did. But before we get into some of those details, given that you've been at this for a long time, you've been exposed to these things even in your history, you spent time with Sam Altman, you worked at Palantir, very data-driven. And you were involved in AI and machine learning from their early days and were building foundational models before anyone had heard that term or LLMs were a commonly understood concept. I'm curious from your point of view, where do you think we are in this new life cycle? You said it's a new paradigm. There's a lot of hype out there, and then there's also a lot of scepticism and there's a lot of people like the legacy companies where it's like there's all this crazy stuff where the whole world seems to be changing and then you have tonnes of big companies that are legacy or are slow, are not really ready to make this leap yet, but even their core, basic technology needs to catch up. From your point of view, when you think about the hype cycle or general life cycle of a technology, where are we right now? **Pratap Ranade:** Yeah. It's a profound question. I'll be the first to admit, I evaluated my own projections and decisions over the last two years of AI evolution, I've been wrong a lot. So I was like, okay, wow. Updating the principle. I see a few things that I think point us in a direction of where we're going. So one which I think is very important is what we're looking at here is not so much an invention as a discovery. If you think about what we've built, we've built systems now that are exhibiting emergent properties and emergent behaviour. It was not very unlike computer science of the past where you coded a system, a determinate system, and a deterministic system and executed what you want. You've now built a large neural net put in a tonne of data and it's now finally exhibiting emergent intelligence. This was always the hope with AI is now what kind of emergence is it? It's not an exact copy of how our brains work. It's like an artificial neural net. It's a very simplified approximation and you have different types of data, so you have something very different. Emergence is happening and it's different. And I think that's meant this thing which even the people architecting the systems themselves don't know. And I think there's a belief here that there's this huge ... I think as Sam Altman mentioned in a recent interview where there's a huge technology overhang, which is the capabilities of these LMS are far beyond the imagination of the products we can build. I think that's certainly true. I think what we see right now is you have incredibly capable, almost like ... I see the foundation model industry evolving a little bit like the chip industry. Very high CapEx. Very good for us as consumers because price competition, it means the cost per token is dropping exponentially and the capabilities are going up. But you almost have a new, well almost like X86 tile processor. You have a new kind of computer that can do general purpose processing for you. And so I think us as programmers and engineers, you want to be using it, you want to be testing it, you want to be seeing what it can do. Did you have to write the algorithm or could you actually put this arbitrary brain in to solve it? And you know that brain is on a glide path where it's getting better and better. And then what we've seen really, really interesting is now we're understanding the limits of current agents, but now you're having multi-agent systems and agent swarms and agents using tools. And that's starting to become a really interesting ... If you think about these as almost artificial people, but they're aliens, they're not people like us. They think slightly differently. What we're seeing now as building little Petri dishes, like a multi-agent agent swarm systems which are some of the systems we use. They're like Petri dishes where you have a small society. You have a society of different agents interacting with each other using tools. And so you have these emergent systems and you're putting some structure around it and boundary conditions to see how it evolves. What's interesting is it's a new field R&D that I think is happening. And so the short answer is I think we have to play with them. We have to push them, see where their limits are. But I think we are in this thing where something ... If you think about the internet or even the mobile phone, there were people that were like, "Why do I need a computer in my pocket?" I remember Ryan, we're going back to our old roommate, he launched a tip calculator. That was amazing. He launched a game. And you had these things that became popular. Mobile gaming is an industry. Who would've thought you're going to sit with your thumbs and play games on the subway, but that's a huge industry. So I think we don't know yet how we're going to use this new system. I think a lot of what I read I think is a little myopic because what we're thinking about is a very zero-sum paradigm. We're saying, "Oh, these are the jobs that we can do today. This is what humanity can do today. This is going to eat our jobs." Now I think what we are seeing is this huge capability uplift. And so what does that mean? A company 10 years ago, what a company a startup would've taken on is probably now going to be what is a feature of a new company? So companies are going to be more ambitious. If one developer is 10 times more productive, you can actually do more. If we look at it at a very scifi kid human level, no, we don't have space elevators, we don't have jetpacks, we don't have flying cars. Why? Economically those problems are too hard to solve? I'll actually ship a small app that helps you ... I don't know. With a productivity tool for planning your meetings every day. Okay, fine. Did you really need a company to do that? That's finally easy enough that each company can quickly wire up its own and that's not a product anymore, which okay, on the one hand maybe scary, that's the products you're building, but on the flip side, the human potential, those humans can take on much more ambitious problems. So I think what we're seeing is I actually don't believe we're going to be very zero-sum in seeing a lot of this loss. I think we're going to see things that we couldn't do start to become possible for the first time. Even we're seeing this. There are problems that we're like, "Oh, this would've been a 10-year problem. This is too hard. There's no way we can take it on." We're like, "No. Actually we can take it on now. We can ship that in the next six months." And so that is actually I think really powerful and you can get really excited and creative about where your product does. But where this goes, I'm not sure what we've built is really artificially general people, approximate intelligence. I don't see that at least in the current systems that we have. I think we're building a new kind of intelligence that's going to be available on tap and that we're going to use as tooling. I think people are very much going to be critical. I think I'm very excited about embodied intelligence and getting robots into the workforce. I think that's also going to be really amazing. You think about elder care, how awesome would it be to have robots for that? I think that would be great. So I do see a tonne of these. I think I'm very bullish on robotics. I'm very bullish on this. Personally coming as a physicist, I think what I'm most excited about is I think we can push the frontier of applied science actually significantly forward. Both in terms of materials research, in terms of aerospace work, in terms of drug discovery, and I think that's going to be incredible. But I don't think what we've built here is just like ... This is not a thing that's going to replace David. If anything, I am placing already a huge premium on the analogue interaction. We're in person every day. I value more my time with my friends and my family so much more. I value that analogue handcrafted experience. And so I think we're going to see just this new world. I would be hesitant to predict more than that, but that's- **David DeRemer:** Yeah. No, I get that. That makes sense. I love how you took us back to the ... What was it? Tipstar I think it was that he created? **Pratap Ranade:** Yeah. **David DeRemer:** It was right after the app store came out, and I think that's maybe a good frame for us to think about where we are. People forget when the iPhone came out, a couple of things. Didn't have an app store. It was only Apple apps, and it used that horrible AT&T Edge network with very ... You had to connect to wifi. Do you remember this? The bandwidth was so terrible, right? **Pratap Ranade:** Yes. **David DeRemer:** I want to test a thing I've been thinking about. How I've been trying to contextualise where we are in this AI boom is actually thinking about mobile networks and bandwidth. I think AI is a new kind of bandwidth. We don't quite know how to talk about or measure yet, which is in the early days, you had Edge. It was good for basic things. You couldn't really build businesses around it. But now we all have ultra wideband 5G in our pocket where we all expect at any moment to be picking up our phone and instantly streaming high definition video. And you think about the types of experiences that has created and businesses that has spawned and the types of things we just expect and take as normal now. And I feel like AI is in that just starting to launch the app store mode. And we haven't even contemplated what it's going to mean when those app stores really take root and the supporting bandwidth and other supporting technologies around it fuel that innovation. I feel like there's a bandwidth story here that there's something to just the speed and the processing, the amount of things that you can deliver to someone quickly has gone totally crazy exponential here. **Pratap Ranade:** No. I think that's super interesting. It's this thing that we now have intelligence on tap. It's literally electric utility. It means that there are things that we were not very thoughtful about before that everyone can apply. You can apply that. Or things that didn't make sense for a human to do because it was dangerous or it was too expensive, you can think about anything on tap. And then I think at the advanced level, it's an powerful assistant. But I think in this general human replacement, I don't really see that. I see it as this ... I love your bandwidth point, which is, yeah, I expect my LTE to be on and me to be able to stream. It's crazy. I expect I should be able to stream video just walking around. What an absurd expectation. And that's normal now, and it became normal very quickly. And you think about the number of like, "Hey, I want you to think about something. I want you to use simple tools to do something." It's still actually quite a pain for me to book a dentist appointment. **David DeRemer:** Right. Yeah. **Pratap Ranade:** Why. That should be easy. There's a lot of things that just aren't happening. Yeah. I think it's going to be one of those things where if you have a ... I don't know, a data centre goes down or something or intelligence brown out or black out, I think you're going to have a big ... We're just going to be like, "What the hell? Why can't I book my appointments?" We're going to all get ... I think Yuval Noah Harari talks about this in his book Sapiens, where if you look in the long arc, civilization has become wealthier. What any common person can have access to now is more than what a king had access to a hundred years ago. And so in a weird way, everyone's going to have basically a bunch of sidekicks doing work for them, and that's going to be the basic expectation, which is cool. And especially if some of them are physically instantiated with robots. Yeah. There's actually a tonne. Go get my groceries. That level's probably already solved by Amazon, but you know what I mean. **David DeRemer:** Yeah. Yeah. Yeah. Well, I think we could probably keep talking about this for six hours. I do want to get to your story with Arena and I want to make sure we tackle that and then we can circle back to this. I love it. Thinking about the future is so crazy, especially right now. It's such an interesting time and also scary for a lot of people. I think there's a lot of really hardcore things. I do have some questions that I want to get to around the dystopian futures. Let's go back to 2020, 2019. You had done Palantir, you had done Enigma, I think right after that. **Pratap Ranade:** Yes. **David DeRemer:** And you met your co-founder. I'm curious when you guys thought about starting that, what was the spark behind that idea of getting started? And then obviously you've been through some pivots, there's been a lot of change, both what you guys have focused on as well as the market and the industry around you. And what are the consistent things that you've pulled through that to today? **Pratap Ranade:** Yeah. The starting impetus ... Now since it's the second startup, I think there were some learnings we had. We had a couple of principles that guided us on this, and we actually realised that a lot of startups will iterate and pivot. And so weirdly, we were less sharply opinionated on exactly the product. We were more opinionated on what the company did, the market, the general technology direction and the business model. So actually we had fixed constraints in places where we didn't before and we relaxed a constraint around weirdly, the core product. That was the evolution between my- **David DeRemer:** I feel like a lot of start-up companies don't start that way. They focus with a product they're obsessed with or a user need. Was that your McKinsey or Palantir background? **Pratap Ranade:** Coming out of McKinsey, the first thing we did was build a very product centred company where this is the problem that we have personally, this is the product that will solve it and we'll just launch it. I think that there's a tonne of really positive stuff with that. But one of the things I think I learned at Palantir is ... I think Palantir taught me a lot of things. Incredible company. I have so much respect for it. I think that there's a Maslow's hierarchy of problems worth solving. And I think at this base level of the Maslow's hierarchy ... So if you think about this and you take that and you say, "Look, where are you in your career path?" So you're more likely to take a risk and start a company generally when you're younger. You have less financial constraints. So in that case, what happens is you're probably what, four years, five years into your career? So when you're four or five years in, there's certain problems in ... If you think about B2B, the technology SAP that you've seen, you've seen payroll, you've seen onboarding, you've seen HR, you've probably seen outbound sales, you've seen some of those things. And that's where the companies are. That's what the problems other people go solve. Then there's two other layers. There's a layer above it where it's like, "Hey, I'm 50 years old. I'm running GPUs at one of the giant semiconductor companies and I have this bigger holistic view and I'm seeing problems that actually aren't just problems shared by an individual, but problems that happen with teams." Okay. I've got a design team, a test team, a validation team, a production team , a fab, and I'm actually getting all these teams to work together. I've chosen ... And actually usually chosen as I've inherited from 50 years ago a job structure and job definitions below me. So there's the designers as the test centre, there's the simulation guys, and that's the construct in which I operate. Now very few people, especially few people who are going to be entrepreneurs, have witnessed that problem set. And so what I think Palantir taught me is if you're willing to be patient and do something that looks ugly and unsustainable, flying to customers forward deploying, which I think is also often poorly understood to mean solutions engineering, it's absolutely not. It's actually just deeply understanding a set of problems that most people don't have access to, to then find products that solve them. That's really more what it is. And it's like that problem set is relatively under attack. And then at the apex of this pyramid, you have problems that are highly valuable, that are hidden, but they're so bespoke that you shouldn't do them as a product company that should be consulting. And so that's like the stack. So we said we have built a skillset now for myself, McKinsey and Palantir at getting deep in and solving enterprise problems. This allows us to access the second tier of the Maslow's hierarchy, and that's good because there's less competition and we're more differentiated. So that was principle one. We wanted to take that motion, that forward deployed motion and do it with enterprise. So that was a principle that has remained true even through our pivot. We just change the industry, but the motion and the muscle and the culture around that is the same. The second piece is if you zoom out any startup which is successful, you've got this beautiful hockey stick, but most of the time when you zoom in, it is like this. And why do you and your team go when it's in a dip? Because inevitably the dips will happen. And when you zoom out, like Steve Jobs I think said, you can connect the dots looking backwards. But in that moment you can also, if your mind is having a dark day, you could draw the line and it points downward and you're like, "Well, why am I here?" So you need something that genuinely excites you. And I think for us, from both of our backgrounds intellectual interest that frontier technology was AI. And again, we started before the LLM boom. So it was broadly speaking, deep learning in deep RL and we were like, "This is really exciting. This concept of emergent intelligence is incredibly exciting. What could you do with it? What could you build with it?" And so that principle guided it. So we were like, "Look, that's how we want it to work." And the last piece was in the new constraint set was monetization, which is ... At kimono we had a tonne of usage and then we thought about monetization much later. We wanted to think about monetization earlier to help us qualify the problems that we're chasing. So okay, yeah, how much is this worth? Can we get revenue early? But I think for us, revenue's a bit different. We're not looking at revenue specifically as this is the thing that's going to ... That's not the indicate. That's not actually not our primary metric. We're still developing a really powerful product that I think will command crazy revenue. We're looking at revenue really as validation of the problem. This is a hard problem. We're going to spend a lot of time solving it. In an AI world where the marginal value of software is decaying so rapidly, I think it's even more important to think about what is a very hard differentiated problem. But you don't want to go and solve one that's not valuable. And so that was the other constraint. So if you think about the update between Kimono and now, it was revenue early to validate problem, go deep and access that problem and this deep Maslow's hierarchy with enterprise and pick a frontier technology you're rationally excited about. In our case, it was AI. So that became the founding kind of problem statement led to Arena. **David DeRemer:** Awesome. Great recipe there. I think that's something that maybe a lot of people, if they're entrepreneurial and they're thinking about starting a startup that hierarchy. A lot of times I think people who are just starting out, they're thinking about there's a problem I face doing something. How do I book a better dentist appointment like you said before, or something like that. And it's a problem that I've solved. You're like, "Okay. Lots of other people like me have that." But I really love that thing of taking it up to the next level of teams and companies and industries, having that bigger picture perspective and then putting those real constraints and guardrails. I love the idea of revenue as validator too. Totally agree. I started a consulting professional services business where it's very, very clear if you're successful or not because either clients sign up with you or they don't. You get feedback pretty fast. \\But I think you're right that revenue is a validator and sometimes people lose sight of that. It's the if we build it, they will come mentality. And I feel like if you're building it in a product experience and you don't have something where you feel confident someone would actually pay you for it, probably not a very good business. So let's talk about then ... I know you guys have been through a bit of a journey here and you mentioned earlier that you had focus on CPG and hardware. How did those emerge as ICPs or targets or industries you're going after? And then you guys made recently this real clear focus onto hardware, and I think that's just a really interesting thing of that exploration phase of pursuing a bunch of different fronts and figuring out where to go after it. And then identifying the one that you're like, "We're going to go all in on this and really own this category." Take us through that sequence and how you made some of those decisions. **Pratap Ranade:** Yeah. Great question. The first thing, which is funny is ... Again, connecting the dots, looking backwards, this makes a lot of sense. I'm finally solving a problem I personally had when I was a physicist. And two is I looked back at our very first website that I made on a Saturday right when we were starting the company. And actually the website basically said, "Hey, we're going to go and build out these large AI systems initially for markets to prove out that they work and can make money on real data in order to then do them for robots and spacecraft." And so literally hilariously, we're actually doing exactly what we said on the website because that was where our passion lay. But to your point, actually, the journey was interesting. And so the early traction, again opportunistically, the first question we had ... Again, this was before language models, was, well, it's all well and good to say you're going to have revenue, you're going to go build with advanced AI that's incredibly data hungry and you're going to solve this class of problems, but now suddenly you're a couple of guys with some MacBook Airs trying to convince a enterprise that has that data to trust you to deploy an active learning system. In the wild, we were like, "Okay. This is just a showstopper if we can't convince someone to do it." And so we were lucky enough to have some great early partners. They were in CPG that said, "Hey, that's an interesting enough idea. Let's let you try." And I think as we've evolved on the market, I think we were incredibly lucky that we had some specific executives who were quite visionary, very deeply thoughtful, who took that bet on us. And I think in that industry, that's actually a very rare thing. You commonly will have that more in an advanced technology industry where you have to think about that. But you think about the CPG business writ large, you're preserving large brands and you're optimising on something that's existed for a long time. And so it's rare to have that kind of leadership. And so we were very lucky to have that. And that actually got our business off the ground. We were able to develop a lot of these systems, a lot of real world reinforcement learning for full automation, prove out value, AB tested in real markets, prove lift, build robust systems that had hundreds of millions of dollars flowing through them. And so that built a tonne of the muscle in the DNA. I think that is still true today. Post LLMs, I think a few things changed. So one was just I believed from a business perspective, it would be very hard to be vertical agnostic because I think if you're already a very large company, so you're a Databricks or a Palantir or a large info provider. You can be very vertical agnostic or you're open source. But in a world where that much capital is going into AI systems, how do you just win a vertical. Sorry. Win horizontally. And so we were now in two industries. And so then it begs the question, okay, if we believe that and we believe startups need focus to win, then we shouldn't be in two places. If we believe we need to pick a vertical and we're in more than one, that led to an immediate intellectual conflict where now you have to choose. And then the hard thing is, well, there's a very simplistic version where you're like, oh, choose the bigger business today. And I actually worked through this and wrestled with this a lot because there were two things that felt off with that. One is the nature of impact you're having. We're not actually concerned about today. We're concerned about how big this company can be. And whether you're talking about single digit, double-digit million dollars, no one's actually investing that a company as a double-digit million dollar revenue. No one actually cares. You're talking about something that could be incredibly large. And so you care more about the projection going forward. And so then the question becomes how much real value are you creating? And if you look at where the industry, your earlier question about is going, how is that value creation going to increase with time? And when we saw what we were doing early days with hardware we were performance optimising the most cutting edge semiconductors on the planet and making them better and actually beating humans at it. And on the other hand, we were getting paid a lot more on the CPG side. If you ripped out all of the technology from a CPG company you still have a pretty great CPG company. If you're a big beer company, probably the number one concern right now is not technology. It's you should probably buy a non-alcoholic beer company. It doesn't matter that much in that industry. Whereas if you're in an advanced hardware engineering industry and you're allowing someone who's shipping advanced technology to ship faster or better, you're directly solving a very material problem for them. And also going back to what we said about zero-sum versus abundant worlds, if you're a hardware company, you are rate limited by how difficult the fundamental physics of the problem is. If you could do more in that time, you wouldn't be like, "Oh, my jobs are going away." You're like, "Oh gosh, I'm going to ship a 10X better product and I'm going to win." And so it's a very strong incentive alignment. If you can make them faster. There's not a shortage of stuff to do. They're like, "Oh my god, great. My next ship is going to be better." And then what was really interesting and what we found to be the case is there's conventional, let's say old B2B SaaS thinking, which was great, let me sell to my 100 other friendly startups and I'll mature my product and then I'm going to go and convince the enterprise. Now, let's think about it. AI systems have a reputation for being unreliable. If you're trying to go and convince ... I don't know. Whatever you think is the most advanced hardware company in the world. You're picking a giant space company or a giant aircraft company or a giant car company, you're trying to convince one of them to buy your product. Are they ever going to look at your and hundred other startups and say, yeah, because so many startups use you, I think it must be ... No. It's actually probably going to look a lot more like Nike, where you need Michael Jordan to wear your shoe and then everyone else wants the shoe. No one actually is a Michael Jordan level basketball player. They still would like to wear the shoe that he wore. And so it's a very different mode and that's a much harder problem. And so we realised, we're like, "Wow. We have a few things." Smaller revenue, which made it hard, but organically growing. While we were paying attention to the other business, this thing quintupled, and we're like, "Wow. We're doing something else, this thing quintupled." Macro forces are driving adoption. We're highly relevant to them. And then we were earning the trust of probably the most demanding ... The Michael Jordans of the world had started working with us and we said, okay, there's less money today. But these guys are just solving ... We're at the early innings, especially with what AI can do. It's only going to get better every six months. The percent of the problem we're going to be able to solve is only going to increase. And these industries ... Just look what's happened with Nvidia. Look at the AI data centres that are constructed. Look at the new space race, the new geopolitical world with defence. Those industries are going to get propelled forward through massive macro forces. And then the final thing I'll say is if you look at where everyone went as individual labourers, we had a 90% uptick in the last 50 years in software engineers, but a similar downtick in hardware engineers. So for every dollar going into one of these hardware companies, there is an overworked 55-year-old hardware engineer with a tonne of expertise that is trying to ship really fast, really advanced things that can literally explode with not enough time, not enough team, and is using tools from literally the '80s. We are using Copilot and Claude code and advanced IDEs and linters and debuggers. Most programmers, they're a programming language that's abstracted away so much of the hard stuff, they're not even dealing with how the computer works. On the electrical engineering side and the hardware side, you literally have people wrestling with electrons and a pretty raw level with old tools. In a weird way, we were just like, this opportunity is so big, very difficult problem. But again, a lot of our team is weirdly, somehow organically, even though we were in CPG, ex-physicists, ex-space flight engineers, and we're like, this is what gets us up in the morning. So that was ultimately what led to the decision. **David DeRemer:** I love it. I love it. And this is Atlas. **Pratap Ranade:** This is Atlas. **David DeRemer:** Maybe for people who haven't come across Arena, do you want to do the quick pitch of what Atlas does, specifically how it works and how it actually helps these electrical and hardware engineers you're describing? **Pratap Ranade:** Very simply, Atlas is J.A.R.V.I.S. What we're trying to do is to make hardware engineering easier. Hardware engineering is much more difficult than software engineering. The software, we have a REPL loop in software that's very tight, it's very easy to write your code, run it, works, breaks. In hardware that REPL loop is slow and it's expensive. And especially if you think about modern hardware. Compare a modern fighter jet to a World War II or a fighter jet. The main change is the electronics. You had hydraulics and metal, and that part is, yeah, it's evolved, but it's the same. Now you have advanced electronics, avionics, flight computers, sensors, and it's perfect if it all works. And all of that is dictated by basically electrons. So you've got your sensor is sending a signal, which is an analogue signal that will then be converted to a digital one that then will go to a flight computer that will then run your code. That is probably subject to a lot of stray electromagnetic fields, especially if you're going into space and through the Van Allen belts. So you have a lot of physics is happening here and you just need that to work. So ideally our code runs, but whatever your input to your flight computer is wrong data because the electronics of your sensor are screwed up or uncalibrated, your flight computer made a beautiful decision, but it thinks your plane is nosing up, but actually your plane was level and it puts it in a nosedive. These are real problems. And so you think about those are the problems we're solving and what does it look like today, Atlas looks like today? Today basically our focus is largely electrical engineering at three levels of the stack. One is you're working at the chip or the PCB level and you're trying to functionally understand the thing that you're building. So you're checking a design, you're actually understanding its functionality, you're interacting with Atlas to ask questions about the design, to understand it, to traverse and find connections. In a complex design, you're trying to actually calculate tolerances. You're trying to run simulations like SPICE to understand functional behaviour. You're trying to generate a test plan that can take you days. We generate that automatically. And then you want to execute the test. You want to interface with your oscilloscope, execute the test, interpret the data. So that's really where we start right now is a lot of it is verification, validation and testing on the electrical stack. What's interesting is what you're building is this EE partner expert. So you're helping these EEs in expert mode, but I think a lot of you have played with language models now. And what's one of the cool things is you could give it a book and say, "Explain this to me like I'm a literary professor of literature," or, "Explain this to me I'm an ESL student," and you're going to get two different answers. And so the same paradigm is true here. So for example, at the systems level in automotive, we have one of the top three automotive companies in the world is using Atlas and they've assembled an amazing, a beautiful car. It's coming off the line and there's a small light that's not working. 40% of the time that error is electrical in nature. And now you have a high schooler, a high school graduate who's sitting in a plant ripping out carpet in seat to try and trace kilometres of cabling and figuring out where it's coming from. And again, the same brain now operates here and is saying, okay, it's most likely here. It's like laparoscopic surgery for a car or an aircraft. And then in the case of a fully autonomous system, you can now combine the fact that you have the deep understanding of all the cabling, the electronics, how things should flow, the simulation. And now you just add in a little bit of sensor data to make sense of the flight physics. You're like, "Oh, my drone fell out of the sky." Okay. Now your autonomous system. So a lot of it is basically, okay, assuming the mechanics are fine, you could probably visually check the mechanics, is the rotors intact and stuff? But then probably electronics are a firmware failure. And now you can actually root cause it. But what's interesting is this isn't some backwards facing anomaly detection or causal ML that's comparing statistics. This is actually you know what CAN bus you're using for communication, you know what wires are connected to what wires, where the sensor is, you know what code's running it. You actually can intelligently debug. And this is where our long-term vision is basically self-repair, self-healing. The drone ideally should tell you how to fix it. I would love my AC unit to tell me how to fix it in simple language versus me having to call a tech. With AI, we can put that expertise into the AC unit. And so that's actually long-term. Where this goes is from Star Wars R2D2. So Jarvis today, R2D2 tomorrow is how I would simplify Arena. **David DeRemer:** I love it. It's mind-blowing, man. It's really cool. I remember seeing when you guys announced Atlas and where you guys were going, you had some videos and things on your website and social media that you put out. I had this moment where I was like, "Oh, great. Wow." There's something crazy happening that you guys are doing and helping to drive a vector of AI that I think most people not seeing because they're focused on ChatGPT. And I read somewhere recently, somebody said, "We have figured out how to shock minerals with electricity to think like a human brain before we even understood how the human brain actually works." Because a lot of these elements simulate a lot of the things about human cognition, how we work, memory prediction, all these other things. But when I saw your stuff, it was like, whoa, now we're actually coming up with AI that actually helps us to create the very machinery, silicon chip sets, all the hardware that actually those things use and are built on top of. I had a little bit of a Terminator moment there where I was like, oh shit. But I think we all have that, right? And I like to think how you said before of no, what this really going to do is unlock a lot of potential and new experiences, new opportunity. I think it's incredibly cool you guys are applying AI to the physical hardware side of this, which I think if you think about it, is a necessary condition or a loop for these things to really get these scientific breakthroughs, to get this technology breakthroughs. As much as we're doing with user experience and how gen AI and things are changing software and how people interact with information and data, applying this stuff to the underlying hardware systems, electrical architecture, all these things seems actually like something we don't talk about a lot as an industry, but seems a very important critical essential step to keeping pace with the rate of change. So what do you think has been the hardest part of bridging this software and AI with the physical systems? When you think about getting into that substrate and that focus versus focusing on how do I build AI to build websites? It's a very different outcome. What's been the most surprising or difficult challenge? **Pratap Ranade:** The physical world is really hard because it's unforgiving and it doesn't lie. There's no, oh, this is a good blog post. I'm going to start. There's no subjectivity in the physical world. All of us, our human bodies. Our world is still governed by the laws of physics and the laws of physics still supersede what mankind has built. That's still our limiter. Why don't we have space elevators? Why we haven't solved the applied physics problem. Well, we haven't. We know it's solvable actually. Physics tells us it's solvable. We just haven't figured it out yet. And so if you think about that, there's a couple of things that are very hard with hardware. One is the behaviour of these systems is actually rooted in applied physics, which I was actually surprised by this. But I would say on average, the average software engineer is slightly scared of advanced math and physics. There will be many, many words for it, but there is a problem avoidance that happens. And so what you have is like, oh, here's Maxwell's equations. These are four PDEs. Okay. A you have to engage with a very difficult problem and a very difficult field that is just ... It's not easy. You have to use complex numbers. You can't just live with real numbers. It just starts very, very basic where no, you have to use partial differential equations. You have to use complex math if this is actually just the way the world actually works. If you don't find that fun or aren't curious about it, not even a starter. So I think the first thing, which is that is a hard thorny beast. I think the second thing is there's the toy problem. The joke about a physicist is you basically are like ask a physicist to describe a cow, and the physicist will say, "Assume the cow is a sphere." And so we love to simplify these problems into toy problems that are solvable, but the real world is messy. So if you look at this as you've got ... For example, we have a small hardware lab here where we actually field test Atlas to dog food it. We have a small flight computer, it's connected to a small IMU. The goal here is to be able to enable anyone with a no hardware or EE experience to build a drone and actually field test ... And not just a drone with your Arduino but make the flight computer, make the sensor, make sure the whole system is working. And so here's a very simple case where, okay, you have an IMU, it's telling you your position in space and acceleration. So what you expected is your IMU sitting on the table, no movement in X, no movement in Y, and you should feel acceleration of free gravity in Z. And that should be 9.8 metres per second squared. And your very simple example is, I've got my flight computer, I put some wires, I connected to the IMU, and I should be reading 9.8 metres per second square. Actually, both of these things look fine. I should connect them, it should work. And weirdly, that doesn't always happen. And this is not because those two deterministic parts of the system are necessarily broken. You have wires that connect them. Now every wire is an antenna and the wires are picking up stray electromagnetic fields, and that's creating interaction. And now you're seeing these weird parasitics that are interfering with your communications protocol. And so you might not be reading 9.8 metres per second square. And if you want to put this on a drone, you pretty much need the sensor to tell the flight computer what's actually happening. And so just something that simple. Accelerometer is working right, the flight computer is working. But if you don't pay attention of how you wire it together and you don't know, you can't measure all of this. And so that's where you also have this additional complexity of you need to build systems that work, that are highly context aware of what's happening in that physical environment. And the physical environment is all of the electromagnetic radiation around us, which is significant matters. And so I think these are some of the problems that actually make, I think, operating in the physical world hard. One is you have this applied physics that you have to wrestle with, and then two is just so many things that can go on. And so your root cause debug processes very wide. The surface area of that is extremely wide. You're like, "What could it be?" It could be my fluorescent light is operating at a wrong frequency in my test lab. It could be anything. **David DeRemer:** Totally crazy. Well, that's wild. And that's a problem set that a lot of people are not at all considering or thinking or dealing with in their day to day. We get a bug, oh, reboot the computer, or something like that. Crash, restart. It's like, no, we're not figuring out. If I turn off the lights is in my flight computer going to work a little better? Totally crazy. It's super cool. It's very, very exciting thing. You mentioned something before about just this math thing. You have to find those certain things interesting. And these are really advanced problems. This isn't just a smart person with a CS degree can come in. There's a certain degree of focus and passion and energy for these things. As you've pursued this as a leader of a high performing team of very, very smart, capable people where you've had to navigate a bunch of changes, 2019, 2020 when you're starting, COVID happens. The guy who told you to fix the bugs is all of a sudden launching ChatGPT. And was probably both awesome because it unlocks this category, but also probably terrifying. And in this pivot, all these focuses. What are some of the management or leadership approaches you've taken to keep everybody marching in their same direction, keep everybody engaged and motivated and focused on tackling such a hard problem? **Pratap Ranade:** Yeah. I wish I had a silver bullet on this one. **David DeRemer:** You and me both. **Pratap Ranade:** I'm on a learning journey myself. And I think part of that is I feel like as a team that's investing a lot together in a startup, I've evolved my own leadership style. And I think a few things that I've learned, I feel like, and that I think hopefully served us well here is there's a degree of authenticity and transparency that you have to provide. You have to be an honest leader. And sometimes things are hard. Sometimes things didn't pan out the way you want. Fundamentally, startups are risky things. It statistically doesn't make sense to do a startup. You shouldn't. It's not an index fund. It's unlikely to succeed. So you are betting on yourself. And I mean that for every team member is betting on themselves and their teammates to be the outlier. And so what's interesting is you have to be incredibly direct and honest because you shouldn't selling your team on the vision. You should be presenting the opportunity that we have, why it might work, why it might not work, and then have folks opt into what the kind of hard looks like. I think the view I've had is look, if you're going to do anything well, it's hard. There's no such thing as their free lunch. You get to choose the kind of hard and you get to choose who you share the hard with. And that's the thing which has I think been a key element over here. Another principle we've had is very much, we just had an interview for senior engineer. One of the things we believe is it's the leadership muscle at Arena is a field commander mindset. There's nobody who's a pure manager. If you're going to send your team into battle, you're going into battle too. We had a team that basically for the last month was in an epic surge pulling late nights and weekends and it's like the team leads were on the ground with them. And as many weeks as I could be on that specific deployment, I was on the ground with them. And the idea is basically, look, we're not in this alone. You're never going to be alone. Our job as leaders is to be in it with our people, asking people to do things that we ourselves are willing to do. So I think that's been actually really ... Because especially you pivot, it's hard. There's a lot of new things to learn. There's a lot of intensity you need to bring. So those two have I think really been important. And the last one is just this principle that we have. It's one of our values on the website is kindness over-niceness, which is, and it took me a while to learn this, but it's like we're nice people. I think people at Arena are very nice, but what we want to do is you really want to be kind. And so the idea is, for example, let's say someone didn't do something particularly well. Now you might be, oh, I don't want to tell them about that. It's not a big deal. It'll hurt their feelings. But they don't hear that feedback, they don't get better. That continues to be a thing. And then later on some harsh external force happens, some big thing drops and you're like, now it's a very severe conversation. So something that my co-founder, Angin loves to say is he is like, "Look, it's all about giving feedback and doing things when the stakes are low." So instead the kind thing to do is like, "Hey, here's all the awesome stuff you've done. Here's something that didn't go well." And just do it quickly and do it right away and give that person the opportunity to grow into that. I think sometimes in our quest to be nice, we rob folks of that opportunity. So that's been another principle. I think that supersedes the pivot even. For us, that's just been a real ... And it requires continuous work. I think we can also often default into ... Because it takes a lot of work to do that because you have to think about it, you have to write it down, you have to pay attention. It comes from a place of caring about the person, but truly caring about the person is not always telling them what they want to hear. This also goes for customers, which is customers might want X. You actually are living inhabiting two worlds. You're inhabiting your world on the frontier of AI and the customer world. You might have a better idea, but it also might require you having a little bit of a debate with a customer that can be uncomfortable. So I think pushing into those uncomfortable zones is something that we've been doing and I think it's a muscle we want to continue to practise and grow. **David DeRemer:** Yeah, really, really wise advice. I love that framing to the commander. I think for different businesses you need different metaphors for leadership. Certainly some industries, some positions and roles you need a boss, you need somebody who really tells people what to do. Some roles, I think you need a manager, someone who's helping them, really managing, directing their work. And then some industries I think you really need a leader. Someone who's like, "Follow me because I'm running in first." And I think it's important for a business to find the right framework. The other thing, the CEO thing, it's like you're the journey. There's somebody I follow online, he's the CEO of this company called Stitch. And he posted a thing recently that the job of the CEO is to figure out who to disappoint, to be mindful of who you're going to disappoint, but then make that tough decision. And so when you're making these tough choices you just can't please everybody, and I think you have to have that conviction and I love that thought of that transparency and giving people like, "Hey, here's the vision. You don't have to agree. Who's going to opt in? I'm not compelling you to do this. You have a choice." I think that helps to drive empowerment. So really I love those lessons. You should write those down and it'll be the framework for your book one day. So just to wrap this up, I think first of all, this has been incredible. I feel like you're one of the most brilliant people I know and just your ability to parse through all these complex ideas and deliver them in a way that's both engaging and exciting is awesome. You've had such an incredible experience weaving through this thing and actually just unbelievably awesome that you started in physics and here you back are after all that journey and all those things that you did led up to making you better at solving those initial problems. Is there a principle or something that when you look back through that course of history that has been a core principle that you feel like has really truly evolved and grown for you over time, that has been a consistent thread or principle that's helped you to lead and grow and tackle these problems with enthusiasm? **Pratap Ranade:** I think yeah. It's very interesting when you ask it that way or what has actually been true consistently in all those situations. It's a very good question. I have to feel like it's the following, which is I think this is definitely true for me. I think it's true for many people. It's like when you don't want to go work out, but then you work out, you still feel better after. You're tired, but then you go and somehow have more energy. It's this thing about giving the importance of giving and giving, generosity for sure, but I'm not speaking about that. I'm speaking about giving yourself to a thing. I think this idea that it almost matters less what it is. We talked about this with changing the direction. But is making sure that whatever you are working on right now ... It doesn't matter if you're working as a barista right now, whether you're learning a programme, whether you're a student, whether you're an athlete, whether you're a founder, you have to be all in. And weirdly, that honesty with yourself about going all into it. And in some ways it's hard to say, this is what I'm doing for the rest of my life, but relax that criteria. No. Go all in for a period of I'm going to go all in for a few years. And I think what happens there is this beautiful transaction where you give so much of yourself to a thing and nothing happens realistically at 40 hours a week. We're talking about just that becomes who you are and then that gives back to you and you grow. And this magic happens where your deep investment in a thing creates this cycle where you grow and it gives more to you than you wound up giving to it. And I thing can be working with a group, it can be working with a team. But I found that the one principle that's been true is when I was doing triathlon, I was like, I want to go to the Olympics. When I was doing physics, I was like, I want to be an astronaut. Whatever the thing is, that should be the primary main obsession. And I think whether it works out or not in the end doesn't even matter. It's like that transaction is like the core essence of life and growth. Even with people giving to others creates deep friendships and value back in new ways. It's the opposite of short-term transactualism. It's like, no, it's long-term commitment, but repeated active choices that create long-term commitment and it's better for you and everyone around you. So I think that's been a theme that I feel like I've started to internalise myself as a principle. **David DeRemer:** That's awesome. Love it. Super cool. This was great. We could keep going for another three hours. I feel like if people want to find out about Arena, obviously you guys are doing really incredible work. If someone stumbles upon this and wants to learn more about how they could use Atlas or your team or if they're interested in joining your mission and proceeding, where can they find you? Where can they find you personally? Where can they find Arena? How could they get involved? **Pratap Ranade:** Yeah. Perfect. So you can find us at arena-ai.com. So you can check out the website over there. I have a substack, so it's again Pratap Ranade. I'm on Twitter, same name. You find me on LinkedIn. So again, feel free to message me, feel free to message the company. If you're interested in what we do careers at arena.com. You're going to get our fantastic recruiting team is going to respond to you right away. And yeah. We're I think a really fun crew. If you want to come build Jarvis, please come join us. **David DeRemer:** Yeah. Who wouldn't want to get on board with that mission? Sounds pretty cool. Well, are you hiring? No, I'm just kidding. Cool, Pratap. This was awesome. Just packed with insight. It's going to be very difficult for our marketing team to go back and figure out what are the key things to highlight. But can't thank you enough for being on here and for just being a good friend over the years. Really appreciate it. **Pratap Ranade:** David, thank you so much for having me. It was a real pleasure. **David DeRemer:** Awesome. **Pratap Ranade:** All right. So we'll hang out. ### From QA-Heavy to AI-Ready Product Delivery Source: https://verygood.ventures/podcasts/andrew-tunall-embrace-from-qa-heavy-to-ai-ready-modernizing-product-delivery/ Get ready to unpack what it really takes to modernize product delivery. In this episode, we sit down with Andrew Tunall, President of Embrace, to discuss how observability and reliability are more than a technical metric—they’re true business values. He dives into how AI is transforming product workflows and how organizations can rethink their approach to performance, culture, and tooling to ship faster, smarter, and with more confidence. {% audio_embed url="https://player.cohostpodcasting.com/9fb380cc-d44d-4408-9391-83b2b25dc339/6f02379a-c2fd-40d2-bb13-de25ae37a91a" /%} ## Transcript **David DeRemer:** Hi, I am David. This is Build to Succeed from Very Good Ventures. Today we talk with Andrew Tunall, president and chief product officer at Embrace. In this episode, we learn from Andrew about his experience as a product owner as well as the critical business impact of observability and modern applications. So let's go build some knowledge. Hey Andrew. All right. Thanks for joining us today. **Andrew Tunall:** Thanks, David. **David DeRemer:** So let's get us started. Wanted to get us loose and get us going. So a lot going on in tech these days. Curious, in your space, what's maybe something that's kind of new and exciting for you and your team at the moment? **Andrew Tunall:** Yeah, well, I mean obviously the AI boom is fully upon us. Everyone's paying a lot of attention to it. I'll tell you, it's been pretty exciting to really think about how we evolve the way we work as product managers. I mean obviously my title is chief product officer. I do a lot more at Embrace in what I do. I lead most of the organisation actually, but product I've been doing product for 20 years and is very much my passion. Inventing solutions to problems and building. And at no point in the past 20 years has it been easier for product managers to rapidly prototype and come up with ideas quickly, that they can vet with engineers. And as much as we are good as conversationalists and white borders, especially in a virtual world, there's nothing better than a really good artefact for people to go through. And that's been pretty cool from a work perspective. I'll say from a personal perspective, it's not technology at all, but I am enjoying the summers finally here in Portland. I spent a lot of the year really excited. My wife's family is from Central Canada and I'm suddenly a very big Winnipeg Jets fan, and they made it to the second round of NHL playoffs. There's nothing quite like Stanley Cup hockey, and that's been great to watch. So yeah, it's been a good year. **David DeRemer:** Totally agree. Big hockey fan myself. You got to see it live. Once you see it live, you're hooked. **Andrew Tunall:** We'll have to grab a couple of beers when I see you in New York in a couple of weeks and chat about NHL. **David DeRemer:** A 100%, absolutely. I think you mentioned a lot about product in your 20 years, in that space. I also know you kind have a unique background in terms of getting into this with your history in economics and political science. And I was just curious if you could give us the story of how you got to where you are today and maybe how some of those early experiences were formative for you. **Andrew Tunall:** Yeah, I mean, compared to people who are going directly into product management out of an MBA or a bachelor's degree in computer science today, I definitely have a non-traditional background, but 20 years ago, in the early two 2000s, and I know I look incredibly young, thank you for telling me. But 20 years ago, product management wasn't really a thing in the software world. There were project managers, there were programme managers, maybe a product owners at some companies that were just learning about Agile. Largely software was built. I mean the first gig I had was we had just done a web forms implementation on .NET and most of our software still built, that was installed software using VB.NET. And so it was very much design first, go to engineers and have them build stuff out. And I think everyone who's my age, or I'd say 30 or older, definitely had probably experienced a workplace where you had the engineering team and then you had the business. The business would show up with somebody who had a marketing background or a pseudo technical background with ideas that were completely ungrounded in the reality of what software engineering teams could deliver. And I guess I was lucky enough to get into private industry where I had a background. My major was in political science and economics, so thinking about the broader socioeconomic impacts of policy decisions. But I minored in computer science. And so, I had this weird background where I could talk to engineers. I mean, I grew up writing code, building my own apps, building my own machines, gaining. So all my friends were software engineers, but I also understood what the business was trying to achieve. I could talk to our customers and kind of interpret their non-technical desires into something our engineering team wanted. And that was foundational and kind of like sending trajectory. I did some product consulting a little bit after that. Eventually I joined a startup as a proper product manager. And from there, I mean the last 10 years I was at Amazon Web Services for almost four years as a technical product manager. And then I was at New Relic, where I grew from a principal PM, building their cloud observability practise through leading us at EP. And then that was really the foundation as I thought about a future executive role, to really grow into a chief product officer and president of a company. So it's been a fun journey. I'll say product management for people who join proper tech companies today, is a vastly different craft than it was 20 years ago where, and I imagine lovingly say it now, because I'm not 22 doing it, but the first companies I worked for, I was a turd polisher, honestly. I got past a turd and my job was to turn it into gold. And one of two things happened, it stayed a turd, in which case I got blamed, or I turned it into gold and my boss took all the credit. So it's been a really interesting evolution watching people come out of college or MBA programmes or computer science programmes and actively want to be product managers and really think about the craft in a good way. And I think it's been good for the profession. I'm really excited to see what people who now grew up using some of the lack of tools they had do and they start taking advantage of AI and stuff to really accelerate how we can innovate on behalf of our customers. **David DeRemer:** Yeah, I think it's a really interesting time. I've heard people say that the domain of product manager really should be at the forefront of AI, because they have all the thinking and tool kits and skills of properly defining what something should do. And now you have these tools that if you know how to define what something should do, you have these tools that can really help you get it there, which is really exciting for the craft and a profession. **Andrew Tunall:** Yeah, I would say not only product managers either, like designers, and one of the interesting things to me is the startup cost for us building a new app or a new idea was so intense historically, where you wanted to build a new API for your users, your engineers had to go spend weeks setting up a MVC service, all the routes, et cetera. Now that's minutes or hours for them to go do all the infrastructure work, that it really means we now have more time as human beings to really invest around the developer experience if you're building APIs, or the user experience and usability tools, you can go rapid prototype five or 10 different options and actually think about that data exploration in a way that you can build much better software, not just build software, which was traditionally a challenge. Now build better software, which is pretty exciting. **David DeRemer:** Yeah, that's awesome. So let's take us through a little bit more of your story, because we got to catch up to where you are today and background of economics and political science into AWS New Relic and getting into product making that choice. And now you're at Embrace and maybe take us through, maybe give us an intro of what Embrace is and maybe back to the early vision of Embrace and catch us up to where you are today. **Andrew Tunall:** Yeah, we'll say what we are today and then I'll go back in time. I mean, our product today is an observability platform focused on end users that helps mobile and web engineers basically build yet better user performance. Performance, from how does your app actually function? The company's about eight years old and it was founded by a team that had built consumer mobile apps primarily in the gaming space. And it was really born out of their frustration of the things we've all experienced, and I think some teams very much still experienced today, which was an end user, a relative. The founder himself would be playing a game or using an app that was in their portfolio and experience a problem and immediately ask the engineering teams what happened. Now, sure, it's a founder one-off problem, but we get support tickets all the time where people are like, what happened, and was frustrated that too often the answer was cannot reproduce or we don't know, we don't have the data. Everything looks fine from the metrics we see. And so, his kind of personal lived experience at which he suspected many users were experiencing, wasn't matching the data sets they had. And so, he kind of endeavoured to build this product that was helping, we call it's one of our core features today, but I kind of call it a play-by-play. It's very analogous to if you're going to a box score for an NHL game, you click the play by play button and ESPN, you'll see a text-based reproduction of everything that they recorded that happened in the game. And that's the notion of what he foresaw. Can we build the play-by-play of you as an end user, using whatever app you are using? And by collecting all of that data, can we then infer the state you are in, to therefore build patterns that help us understand the underlying problem you experienced and therefore resolve it faster. And he didn't use the term at the time, but in itself that is observability, which is collecting all of the outputs, the observable outputs of a system, so that you can infer state for a particular system or a user, and therefore understand what the overall problem that you're, and explore the overall problem that you're trying to solve. And so we've been through a number of evolutions. I think six, eight years ago, it wasn't wholly unreasonable to have a craft reporter that didn't record everything he needed. I think broadly speaking, a lot of that stuff are solved problems now. And a lot of the things we're helping customers solve now are very nuanced challenges with app startup time or render performance when they're processing different JSON payloads or in the web space, as they implement various business functionality in their React code, what are the performance impacts of that and the perceived user experience, because I think we all intuitively know that performance challenges to a user, impact their engagement with your app. Most teams just don't measure it in a way that they can say what that impact is. And so, I'd argue that's a whole world of reliability problems that most user facing teams don't have very good visibility into today. **David DeRemer:** So when I think about this space, you have crash reporting, which you mentioned, and I think that was, that's sort of the default one that everyone's like, oh, when the thing crashes, we need to kind of know, so that we can fix that pretty- **Andrew Tunall:** Yeah, this is an alias to the blue screen of death, which again, I also don't think younger people know, because most people are on Windows machines nowadays, but including me, I am a 100% Mac user, which is crazy, because 15 years ago I thought I was silly. But yeah, I mean it's terrible. The app literally goes away. You lose all your state, you start from the beginning. **David DeRemer:** Yeah. And then the other one is analytics, which is like you programme in some events and you can kind of see how you- **Andrew Tunall:** Sure, yeah. You're tracking where David's going to see whether or not he's doing the thing you expect him to do. **David DeRemer:** Yeah, right. And maybe conversions, did you buy a van in a car or whatever. And then this observability reliability thing, it sounds like it's deeper where it's like, you're right, there's this tracking. Okay, well in certain conditions, when I open up the app, it takes four. We've all had that experience where you try to get the app out to do something and it's loading, you're refreshing, like what's going on, how do you know if you're an engineer, if that's a network condition, is it because the payload was super big? So that's sort of the thing that you guys are really trying to help out is make sure that the overall experience for the user, even those little small moments that, yeah, cool. Got it. **Andrew Tunall:** Yeah, and I think the way I look at it is in the early days of mobile, the companies that were first to it, kind of built these experiences where using the app was compulsory. Like if Delta tells you the only way to check in for your flight is using the mobile app, you will tolerate quite a bit of discomfort, because you really want to take that flight. But if you look at what's happening today with e-commerce brands, everyone's got these apps out here, they're pushing notifications to you, they're giving you contextual hints, they're trying to get you to engage frequently with their digital properties, because that casualness of engagement with the brand makes it more likely over time that through many successful engagements, you're likely \[inaudible 00:12:28\] convert. Because maybe you don't come with a presupposition that you're actively shopping for that thing, but through many explorations, you become convinced that you actually are looking for it nearby. If you start introducing subtle performance degradation into there and it becomes painful enough for people to engage with that brand, eventually that translates to decreased conversions. And you think about the even app startup in different states, cold startup versus warm startup, if you impact your warm startup by half a second and people are kind of constantly flicking in and out, trying to do product research, and it's just long enough that it seems like the app's not working, the probability that they permanently disengage, and by that I mean close the app and you lose the state, so you've forced closed it, you're not crashing it, but you've lost the context that exists locally, or that I just background it and go do something else entirely, is actually quite high. And so, I think we're not trying to tell an analytics story necessarily, right? We're not trying to talk to product teams or designers and telling them people didn't like the green button rather than the blue button, but we are trying to tell them that when people follow the path that you've designed for them, they're encountering all of these types of friction points that are due to performance, that your failure to prioritise is going to ultimately impact your end business \[inaudible 00:13:46\]. I've been kind of using, I'll try it out on you and see how it kind of links with you, but if you think about, obviously they're not API's, because they're not consumed by an applications, they're consumed by humans, but if you think about the API's for your app, they are not slash resource. They are create an account, login, add item to cart, checkout, like view order. There are the things that your app does, and while you're experimenting and evolving those, and especially in the age of AI, evolving them even more rapidly, you don't have a handle on whether or not you are doing harm or impacting people's journey through those API's, through latency and errors. There's a whole world of reliability, things you probably don't know about. **David DeRemer:** Yeah, no, you're right. A lot of people are maybe not thinking about that level of detail. There's still a lot of addressable market for this, because it's a subtle thing and everyone has that experience where you're waiting for it to reload. Or maybe another case here, like in the example you were giving that led to this insight in the beginning, it's like it crashed. What happened? I think the other thing that happens is when you get a spinner, it's loading. You're like, oh, internet. The internet must be bad. And it's like, well, maybe not. **Andrew Tunall:** Maybe not, right. And to you, the funny thing is to a human being, that feels like a me problem and semi-permanent. Like oh, I must be in a dead spot. And so, I'll just course close that app and go do something else. And think about it in a, if you're Uber Eats or you're a fast casual restaurant like Chipotle or you're a clothing brand that's running a one-hour sale for it's most frequent customers. You hit people with a push notification and then for some reason there's a performance aspect on your web that makes everyone believe that they're in bad internet connectivity. You've just paid some vendor a whole lot of money to push out a whole bunch of pushes to your population. You're running marketing campaigns, you've built it out in your CMS to completely flush it down the toilet, because you've created disengagement friction for all your users. And I guess I would pause it and most teams don't have that level of visibility, so we think it's a huge opportunity. **David DeRemer:** Yeah. So it sounds like there's a big shift here where in terms of how organisations should think about observability, when you think about reliability, of course everyone wants their app to be bug free. You want it to be high performance. People talk about these things, but what you're really highlighting is that these things aren't just a thing that's nice to have. This isn't just developers trying to create really good stuff. This is real business value. **Andrew Tunall:** Yeah, real business value. **David DeRemer:** Yeah. **Andrew Tunall:** Yeah. I'd even say as you think about how your teams function from a software delivery standpoint, even if you take the end user effect out of the equation, and Charity talks about this quite a bit, Charity Majors, one of the co-founders of Honeycomb who really was forward-thinking in how you use her technology platform to go answer unknown unknowns. I think if you view all the telemetry and the representation of how your users are experiencing your entire system, your app, your API's, your databases, et cetera, into account, that shared context between developers building that user experience or the SDK, or the library, and the cloud-based systems that actually power it, is what leads software engineering teams to actually build really resilient, high performing excellent technology. If your front end teams have no idea what the API payload is going to look like and how to go design an SDK to properly retry or consume that data and can't have a two-way conversation about payload design, because they know how their app is going to process it, I mean it's two teams operating without entire context and at least a very predictable results. **David DeRemer:** Yeah, that's interesting. So when you implement these and you have these running in production, you have embrace going and you have this next level of observability and reliability, have you guys observed that it fundamentally changes some of the team behaviours, cultures and how they work together? **Andrew Tunall:** Yeah. So we work with a very large brand, and I guess there's publicly accessible information, so you can kind of look around where our SDK is, but they run lots of experiments, like thousands of experiments across their customer cohorts. We've had them turn off experiments and I think the team that originally adopted our product and now it's got much wider adoption, they wanted to stop being the janitors. They wanted to go from a paradigm, where they were putting the brakes on everything, because they had some tidbit of information that told them something was wrong, to a team that was instilling best practises amongst every engineer in their organisation, where they were measuring and improving and could run experimentation at scale with full confidence that they understand the after effects from a performance perspective around what's going on. It can make the right decisions for their users in business. And that's tremendously powerful. If everyone talks about wanting to do experimentation, but do you have the right data in place? Do you have the right best practises in place? Do you have the right culture in place to do it? Because if the answer is no, having a 1,000 different teams checking in code like the wild west and shipping it within an hour with no QA, when by the way for 15 years, front ends, mobile and web, have basically operated on a QA heavy, like zero bug paradigm, not a fast and fixed faster paradigm. And observability is really fundamentally in production, is really about ship it, because you're going to have the right visibility to know if it's broken, and then you can disable the feature flag and you're good to go. Instead of like, we're going to spend 12 weeks QA'ing this and hope we caught everything, but when inevitably it breaks, we're not going to know. **David DeRemer:** No, I hear you. That's \[inaudible 00:19:20\]. **Andrew Tunall:** And you just can't do that at scale. **David DeRemer:** Yeah, I think that with crash reporting, one of the things I've observed is people that aren't really super familiar with the ins and outs of doing these things, they want no crashes. If crash reporting is identifying something, it's like that's a problem. It's like, well, realistically, if we have a speed thing we're trying to do, we should be determining what level of error rate are we comfortable with that we can identify with these tools and fix. **Andrew Tunall:** That's been a thing that we've talked for in the digital transformation world for a long time. And you just have to live what they're going to exist. Especially in the Android ecosystem, you've got tens of thousands of potential device types from OEMs. You've got many active versions of the OS and then multiply that by the number of experiments you're running and teams actively checking and wanting to ship code and see whether it impacts user behaviour. And suddenly it's a number of dimensions you can't possibly control for in a QA environment. The only way to do it is to see a representative, and by the way, unpredictable user behaviour, people trying to engineer their way around some objective they're trying to achieve that you didn't anticipate. And so, you take all those things to an account and it's like, well, I don't know how a zero bug QA heavy approach can ever be right for that. If your objective is to ship fast. The two are really incongruent. And I mean this isn't, I'm not saying everyone should ship fast. The world right now is gravitating toward a technology world with AI code assistance, where the pressure on every business is going to be to ship fast. And so if you don't build the right culture and tooling around it, you will fall behind. **David DeRemer:** A100%. So you're out there with a really cool product that you guys have led the product vision and are leading the team in a lot of ways and have really positioned that forward, where what you guys are doing and leading this charge of observability reliability. So let's dig into that. I'm curious, let's get back to your product ownership and your history there. Because you probably put some of these ideas to work even in your own tools along the way. How did the embrace change over the years and how did maybe the strategy change? And as a product leader, how did you sort of validate each one of those inputs or opportunities as you went? **Andrew Tunall:** Yeah, so I guess I'll rewind a little bit to my strategy around finding product market fixed. I think that's important context for product managers and product leaders and when they think about opportunities. And especially in engineering driven organisations or as engineers ourselves, we tend to think of if there's a problem and I have a solution, everyone will want it. And that's just fundamentally untrue, because chances are, especially now, there's many solutions to that problem, all of which solve it in some particular way, but all of it look the same. And so, a big book that I'm a fan of is the Purple Cow, which is really a marketing book, but is about how do you stand out in a crowd of cows that all look the same? If you're driving, and the analogy is if you're driving down the road, you see a herd of cows, there will be small cows, big cows, brown cows, striped cows, modelled cows, white cows, black cows, but what if there was a purple cow? Amongst all those cows, the purple is the only one you're going to be like, well, that's unique. That deserves my attention. It's worth exploration. So especially as an early stage company, we have to be a purple cow. We have to solve a problem in a new and unique way for and a specific problem in a new and unique way, that deserves people's attention, because we are too small as a company to go compete with others as just a generalist solution. We can't look like the rest of others and just carve out our 10% of the market and be truly large. And I mean, we're a venture funded startup backed by NEA, like we're in the business of building a growth business. And so, just carving out a little bit of business and going about my day is not the game that I'm playing. The second bit is this notion of how do you find product market fit? And I guess I've thought like this for a while, but I was listening to Lenny's podcast a year or so ago, and I can't remember, there's a venture fund that does a product market fit workshop, and they kind of put it into the structure that now I send to people as kind of a good resource, because it mirrors how I think about it. And it's this notion of the four P's, which is it's not just the product, but it's the persona, the problem, and the promise. Which is to say promise is your messaging, the problem is the problem you're solving. The persona is somebody who has to care about the problem and resonate with the promise, and then the product has to fit all of that. So if any one of those don't work, you don't find really radical product market fit to a degree that people are going to go adopt it and be actively raising their hands and saying, hey, this is a unique problem you solve and you alone kind of solve it and it's worth my time and energy to shift from the status quo. So if you rewind in our history, we built a really unique kind of single feature, which was this user play by play. But then we largely fell into the trap of doing what everyone else was doing in the space. We built a really great crash reporter and sure was 30% better than what you could get at Firebase, but it was still just a crash reporter. And there were a lot of other people building crash reporters. It was a striped cow in a road full of cows, or a pasture full of cows. We built a bunch of other detection as well. It was tools in a toolbox, but it was really hard to discern what was materially different. And so, I started a couple of years ago, talking to leaders who were, they kept telling me the same story, which was our way of operating the app feels like it's just fundamentally data and everyone just goes into the toolbox and they have a couple of things they fix. But when we look at our app store reviews, we know people are kind of having a rotten time and we don't really know why. We know that there's probably disengagement for reasons that we have no information around and we can't quantify what that looks like. And everyone just kind of goes, because there's no data other than I guess a gut feeling. And gut feelings are really hard to prioritise as product and engineering leaders, when we have an endless list of stuff that there's pressure to build. Unless that gut feeling is tremendously strong in your position of being able to leverage that political capital, especially in big organisations, retail organisations, where maybe their leadership isn't grounded traditionally in tech. And so, as we continued those conversations, I think what I started to realise is we needed to look different. We needed to look like a purple cow. We had a series of hypotheses and we needed to go codify them in a way that made us clearly different than the rest of our competitors. And there were a couple of things happening in the larger world. One was that open telemetry, which had been really this evolution from a bunch of open standards to now the most contributed CNCF project, was gaining gravity in the larger world. We were seeing very large corporations attempt to adopt it as their standard observability language, which was really important for sharing context between application teams, infrastructure teams, everyone doing observability. And I think we realised that there was a huge opportunity in the market to really take a leadership position by firmly saying, okay, open telemetry can be a really great way that people adopt across every part of your technology organisation, including front end and mobile teams. And by kind of applying that leadership, maybe we can then also go solve this other problem we were seeing, which is more and more technology leaders asking, can you help me understand how all these problems we kind of believe exist, correlate to whether or not a user is staying engaged with our app? And so, I think I wouldn't call it a pivot at all. I'd call it an evolution, which was we went from this disparate tool kits to how can we start putting the pieces together? And we have some really awesome stuff launching here soon, and I'm really, really, really excited for V2 of it, where we're really starting to apply a machine learning and AI toward how do we help users really understand the things users were doing and the correlation of performance signals, to whether or not they stay engaged, which is I think as you talk about the real service level objectives for your end users, would be pretty awesome if in your reliability dashboards you could show the key things your users do in an app and whether or not you likely could correlate it to a recent change or reliability signal in your app. Because then it's like we didn't just release something users don't like. We released something that actually keeps them from liking it. And I think from a technology organisation for a reliability culture, that's huge. **David DeRemer:** Yeah, that's totally, and what you guys are doing it sounds like is uncovering this information that otherwise was sort of mysterious. And so, you can make a bunch of conclusions. Like if you ship an update and you're not getting the business metrics you expected and you could say like, oh, the feature, no one wants the feature. And it could just be like, no, no, the feature takes a second and a half too long to load and people are frustrated. **Andrew Tunall:** Yeah, I mean I think if you were to look at app load time I think is a good thing. Everyone has experienced that across every app they use. But if today every app has a unique value proposition and there's probably a gradient with which users are willing to be patient during that app load time, to experience that value proposition. For something like X or formerly Twitter, that load time is very low, because they have intentionally created a user interaction where I want instant gratification and I want it many times throughout the day. For other apps, it may be quite long, but it's unique to your context. If you impact that, do you actually know what change that has on your user's willingness to stay engaged? And I would argue almost nobody does. And so yeah, my uncovering that kind of information, I think people can just build better software, which is, I mean that's really our grander mission, right? **David DeRemer:** Yeah. So you're now the president of Embrace as well, and so you came up through product, right? And so, how has that product-driven leadership role evolved over time? Now that you're in an executive position driving that, how has your leadership style maybe morphed over time? And I'm also curious how in an organisation where the product is really in a very senior position like that, and you've been so vital to the trajectory of this, how has it influenced the engineering culture and your ability to ship what people want? **Andrew Tunall:** Yeah, I mean I'd argue especially in technology organisations that are selling to developers, having people who come in through product or engineering backgrounds as the most senior levels of leadership, is critical to them like building an authentic brand, building them a product and go-to market motion that is authentic and resonates with their user persona. I have seen companies that build really great products, hire people with sales and marketing backgrounds who have no experience in the domain as their CEO. And the culture absolutely takes a nosedive. The product authenticity is no longer there and the company result is hosed as a result. That said, running a company at the most senior level where you're doing more than product or technology organisation is a really hard gig. I mean, we're a venture funded company. We burn negative as we endeavour to grow. My day includes everything from reviewing a redlined customer NDA to subbing in for one of our customer success people, because we have IT customer and I need to get an upgrade done to popping onto a sales call at 6:00 in the morning with a European customer, to then sitting in a roadmap planning exercise with my PMs or actually going through product details and mocks. The context switching can be nightmarish and it's totally not for everyone. And I think a couple lessons I've learned is you have to have really great accountable, high ownership leaders around you and say, I'd say ownership is probably under indexed in many organisations, which is are there people that you surround yourself with, where you just know that they're going to get it done? And especially for a small company, those individuals are gold, because if I can hand something to somebody and know they will find a way to find a way, to produce a very high quality outcome, that's something on my plate I don't have to worry about, because I have the trust and relationship with them to know that if they started encountering a problem, would they know it's not going to meet my standard, they're going to come to me proactively and we're going to work on it as a collaborative team, instead of I'm going to look at someone and be like, this just doesn't meet my standard. How did it happen? So yeah, I think it's been an interesting transition. It's been great for me. The reason I went from a very well compensated VP at New Relic, running a lot of product, to a much smaller company was largely because as I looked at my career arc. Being a CPO and a senior leader at a pre IPO or recent IPO company kind of necessitates you've done a lot of the gig before, even at a smaller scale. There's a lot of hard lessons you learn about managing people, about managing culture, about getting people excited, about communicating tough messages that you don't learn when you're kind of a functional middle manager even in a large organisation, you're insulated from a lot of it. And in three and a half years I've learned a lot of those lessons and they'll pay dividends from my career. So there's lots of reasons to go to a startup. You get to learn brand new skills, you get to often do things that are well above your pay grade. You get to define your role in many ways, getting rich quick is not one of them, I will say, not one of them. People think, oh, you go to startup, you're going to be a multimillionaire. It's like most of the time, and by most of the time, I mean 99% of the time, the answer is you will not. Everyone likes to index on the outliers, because otherwise nobody would be saying enough to do. **David DeRemer:** Yeah, you're right. There's a lot of management lessons and sort of entrepreneurial lessons you just rattled off in the last two minutes or so. Totally agree with every single one of them, by the way, because I've gone through that same stuff as well, and just the lessons you learn, it's hard to explain. You don't really learn them unless you really can experience them, and it's just a truth, it's just not for everyone. And I think the startup world does kind of really put the successful startup founders on a pedestal. And you see, even you look at the people who are really successful now, the true, like the Jeff Bezos \[inaudible 00:33:57\], these guys who are billionaires, hundreds of billions, but they've also been doing it for a long time at this point. And the amount of stress and things. And for every one of those, there's thousands of people that we had a recording of this with our VC from Celesta, and I asked him about what surprised him about being a VC, and he is like, "Actually, what most people don't know is actually what I do is spend most of my time dealing with all the companies that aren't going well. The ones that are going well, we don't talk about." So yeah, so many of them don't go well, and so it is really tough. But maybe that's a good segue to a question around, given everything you've learned in your product journey and in your startup journey and everything, if you were to go back in time like five years ago, what would you tell yourself then that you think in retrospect, you would've maybe kind of saved yourself a lot of stress? **Andrew Tunall:** So five years ago, when I was still in New Relic, I mean, first of all, I didn't know New Relic would be acquired by a private equity firm at the share price it was, so I probably should have stayed for my own pocketbook, but you can always make more money. What you can't do is learn some of the lessons. I think overall from a career perspective, the move was really good for me and my partner in crime, Eric, who primarily deals with our strategic investors, he's one of the co-founders, strategic customers, et cetera. He's been an incredible partner and taught me a tonne. I think thinking about, and these are probably most relevant to VC funded startups, we had raised a fair amount of money. I think the 2020, 2021, 2022 world is not indicative of the rest of history in that the spending mood of potential customers and the pressures from the BC community for companies to grow at all costs, where unlike anything that really could ever exist again in the future or really should. I think taking a, I think if you go back in time, sometimes it's like rather than hire 50 people, maybe I hire 10, who just have outstanding high ownership, who have outstanding contacts in the industry, who are just absolute top performers and we build smaller and better and think about how we can just be, we can over index on just batting above our weight before we grow. I think a lot of companies make the mistake, and this happens at every level. I saw this at Amazon, you see it elsewhere, where they have something going good and then they grow to three or four or five times their size and suddenly the culture that they had and all of the things that were really relying upon them having a very high bar internally and talent level and people who were executing well above their weight, it goes out the window and suddenly the direction starts to tilt more toward mediocrity than excellence. So that's probably the number one, which is just now knowing the larger economic uncertainty, the decline of the crazy \[inaudible 00:37:01\] multiples and people buying \[inaudible 00:37:02\] software, I probably would've said before we grow crazy or double down on what we think is working, how could we go from I convince myself to the world is telling me, because I can't say no anymore. If people are literally, I remember and I wasn't there, but I've heard the stories, right after New Relic went public in 2012 or early 2013, they literally could not say no to business. There were so many people showing up, asking them just for order forms, that reps were blowing their numbers and not working 45 days out of a quarter, because they were closing business so fast. I'm not saying you should wait that long to grow your sales team or expand your product, but certainly they had what I would call radical product market fit at that point. There were no questions that you could go add people to that organisation and make more money. I don't think most companies that try to endeavour through growing to add counter at that point. So yeah, I mean a lot of it's figure out how you absolutely have product market fit and it's not you convincing yourself, it's the rest of the world telling you, because they're banging at your door, they're in bounding at such a level where now you have to rise to meet the occasion rather than find the occasion to meet your growing team. ** David DeRemer:** Yeah, love that. Find that product market fit. And it sounds like you're also, there's elements of make sure you are paying attention and optimising the team and the people you got around you and maintaining that pace. And I totally agree. It's like when you're moving really fast, you hire great people, but sometimes great people are not still the right people. And I think that's tough when you're moving fast and those are lessons learned as you go forward. So wrap us up maybe by just doing a quick, given all that, you mentioned AI a couple of times and I would imagine AI's got to be something you guys are thinking about. You have a lot of data and stuff go the other way. What's the future hold do you think, for Embrace? **Andrew Tunall:** Yeah, I kind of alluded to we're really starting to do some stuff around how we build AI, that can be assistive in you defining the boundaries of the things you want to look like and helping you understand the data. I definitely, I'm excited about how we can employ AI to do things that were time costly, but not necessarily super high leverage from a human perspective, increasingly. And that's things like that we know things are clustered around a common time period, we can kind of place them on an affinity map. Is there a way we can train a model to say, hey, these things all kind of make sense together. Tell me what this step looks like. And that helps us define data shapes. I have quite a bit of scepticism that some of the things that are, we are broadly salaried employees, and so there's a relatively fixed cost to us for an organisation. I have some scepticism that right now the cost model of AI is a perfect fit for replicating all the things that our human brains are pretty good at doing around pattern recognition at scale. And that's simply because I've seen, I mean I've seen it, it's pretty easy for enterprises to run up massive bills in a hurry, doing things that if you employ the human to do, they probably could do. It just might take marginally longer. And I think historically, like our world, the observability world, and I had a friend who was an engineer say this, he's like, "I wanted to set up New Relic and then just ignore it as much as possible. It was kind of an insurance policy that I hope never had to pay out." I have a different take, which is that I think great observability tech is something that ideally you're interacting with every day, because you're using it as the control panel around what's going on in your software and you're using it to constantly experiment and tweak. And if you shift to that paradigm, then the level of emergency should be far less insane. The house isn't burning down, it's just a spark that maybe not could turn into a fire, but I want to address, because I care about my overall quality. And in that, we can't have AI costing us $15,000 whenever there's something, little thing is if the house isn't burning right? Now, I could be totally wrong. They could figure out a way to decrease by tenfold or hundredfold the cost of AI. But right now, we did some early experimentation with taking the play-by-plays that we have users going through, placing them in a tokenized LLM, having to do comparisons, and it's like, well, something that if I go click, click, click, click, click, I instantly saw that just cost me $15. And if I have a 1,000 engineers in an organisation doing that 10 times a day, the numbers start to add up pretty quick. And so, I think very much TVD on it. I am receptive to the idea. I would love to see it. I think it's a pretty cool idea, but as a very pragmatic person, you obviously can't just be technology can do this. It's like is it the right thing for technology to do, given the problems that your customers that actually prioritise and their willingness to pay for it. **David DeRemer:** Yeah, that's really insightful and you kind of unpacked something that's interesting about this AI boom, which is most of the consumer tools that people are used to engaging with, like ChatGPT, Gemini, they're either free or very inexpensive in terms of the consumer version. But if you're a business building with it, you can very easily rack up a very large build very quickly. And I think the general public doesn't really see that. So these expectations of like, oh, whatever, just hook it up to your AI and it's just going to do this stuff. Right? Well, hold up. It's one of those things, even fixing bugs in observability, it's like sometimes you get a thing and you're like, well, that only happened to one out of a 100,000 users. So it's kind of not even worth fixing that, kind of a- **Andrew Tunall:** Especially in mobile by the way, because if it's based on an outdated supposition, by the time you fix it, given its lack of prevalence, you may have a new operating system, a new app version that totally wipes off the feature, et cetera, right? So yeah, I mean, why would you employ something that's going to cost you a couple hundred dollars to do that at scale when it may not even be relevant anymore? **David DeRemer:** Yeah. Well, and that's where the AI thing could get really out of control, if you enable all these agents to just do all this stuff. So awesome conversation. Embrace, if people wanted to learn more about your product, if they're hiring, how would they find you and Embrace? **Andrew Tunall:** Yeah, so we're at www.embrace.io. Don't just Google Embrace, because you won't find us, because it's another lesson learned. But that was before my time. Name your company something where when people google it, they can always find it, so it goes, yeah, those times are fast. They can also find me on, my name's really unique, so if you search me, Andrew Tunall, on LinkedIn, you will definitely find me. I'm the only one. That's good and bad. I guess I'm on Instagram, I am technically on Twitter, but every day I'm threatening to leave. Yeah, so LinkedIn is probably a great way to connect with me. **David DeRemer:** Awesome. Cool. Well, there you go. If people are interested in this, should definitely check you out, because you guys have an amazing tool and very thought leaders in this space and moving the needle, so. **Andrew Tunall:** Thanks a lot. \[inaudible 00:44:08\]. It's been great working with the VJB team too. I'm excited to see what you guys have next. **David DeRemer:** Awesome. **Andrew Tunall:** Thanks. All right, cheers. **David DeRemer:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review and don't forget to subscribe to get our latest episodes. Thanks again and see you next time. ### Small Team Shipping Fast With Autonomy and AI Source: https://verygood.ventures/podcasts/kevin-smith-and-mikel-corcuera-snipd-small-team-big-vision-shipping-fast-with-autonomy-and-ai/ From biking with podcasts to building a product that captures insights in real time, this episode unpacks how Snipd blends AI, Flutter, and startup grit to transform how we engage with audio—a candid conversation on vision, velocity, and building with purpose. Plus: Kevin and Mikel are offering Build to Succeed podcast listeners 1 month free of Snipd Premium—which unlocks advanced AI features and usage!  👉 Grab your free month here: [https://link.snipd.com/Cx7S/vgv](https://link.snipd.com/Cx7S/vgv) {% audio_embed url="https://player.cohostpodcasting.com/118c2597-b9d2-42ec-be9a-4c01c3f6bc65/6fd6860c-3067-47ea-a9fb-647cb2c38c2d" /%} ## Transcript **David DeRemer:** Hi, I'm David, and this is Build to Succeed from Very Good Ventures. Today we speak with Kevin Smith and Mikel Corcuera, the co-founders of the AI-powered podcast app, Snipd. In this episode, we learn about their experience building with AI to create an innovative product that uses AI, so let's build some knowledge. All right. Hi, Kevin and Mikel, welcome to the Build to Succeed podcast. Thanks for joining us today. **Kevin Smith:** Hi, David. Thanks for having us. **Mikel Corcuera:** Yeah. Hi, thank you. Thank you so much. **David DeRemer:** Absolutely. So given what you guys do, I wanted to maybe get us started with just a little quick question of so what is your favourite podcast at the moment, given that I'm sure you guys are seeing quite a lot of podcasts these days? **Kevin Smith:** Mikel, you want to start? **Mikel Corcuera:** Yeah, I can start. I'm currently listening to Legacy. It's two reporters who tell the story about famous and influential people. I'm currently listening to the episode about Freud. Well, the episodes, four episodes per person, more or less. Yeah. It's the Freud episode and it was recommended by my girlfriend who's a psychologist, so that's why I started with that one. **David DeRemer:** I'll have to check that one out. **Mikel Corcuera:** Yeah. **Kevin Smith:** I'm a big fan of Lenny's Podcast. As an example, Lenny is a former product manager of Airbnb, and he has startup people on product, people, founders. And basically, they talk about building products, growing products, and it's always super interesting. He always has super interesting guests on, so I can very much recommend it. **David DeRemer:** Nice, yeah. We'll have to get links from you guys and we'll throw them in the show notes for people who want to check those out. So given the question about podcasts, maybe we should give a little backstory around why we would start there. So maybe for those people out there who haven't used Snipd yet or have come across it, can you guys explain a little bit about what it is? And maybe try to like what's the elevator pitch, the quick summary of what you guys do? **Kevin Smith:** Yeah, yeah, definitely. So Snipd is an AI-powered podcast app. For everyone who listens to podcasts, I would say just as your audience, who listen to podcasts to learn, follow their curiosity. So we have a lot of AI-powered features like you can chat with the episode, you get AI-generated chapters. We also generate little mini-bios for each guest. But the feature that we're most known for and also where our name Snipd comes from, is the ability to save any insight that you hear in a podcast, simply by triple tapping your headphones. So how that works is whenever you hear an amazing insight, you triple tap your headphones, and now AI summarises the insight that you just heard, and saves that together with the original clip. And all of this together we call a snip and we call it snipping. **David DeRemer:** I love it. So what's the story that led to this? How did you guys come across this idea? What led you to each other, I guess, to start Snipd? And where'd you get this kind of concept that you wanted to commit all your time and energy to it? **Kevin Smith:** Yeah. In a way, it's interesting because I think the answer to both of those questions, it's almost the same answer. So my personal story, it starts with, before starting Snipd, I joined an early-stage tech startup here in Zurich, and it was my very first experience in working at a startup. And even though one of the things I had studied before that, so I had studied mathematics and economics. You would think that if you study economics that they would teach you anything about how to build a business, and in particular a startup, but of course, they don't. So I had no idea how any of this worked. So what the founder did, he recommended this podcast to me called How to Start a Startup by Sam Altman back when he was CEO of Y Combinator. And I listened to this podcast, and for anyone who doesn't know it, basically it was a lecture at Stanford. Every episode was one lecture, and they would have one of the biggest, best entrepreneurs that we've seen in the last 20 years be the guest and they would just talk about one topic. For example, there was Dustin Moskowitz from Facebook, or Paul Graham, Peter Thiel. Peter Thiel is the founder of PayPal. So here I was in Zurich, in Switzerland where we're based, riding my bike to work every morning, and learning from these incredible people that are living on the other side of the planet. And best of all, I'm doing all of this while not even losing time, because I would ride my bike to work anyway in the morning. So this was how I got into podcasts and really learned or realised for myself how podcasts are one of the largest knowledge libraries in the world that humans have ever created. And it's this very enjoyable way of consuming information, but of course, I ran into a lot of issues. You'd be on your bike, the typical thing is you hear this amazing insight. Peter Thiel drops some contrarian knowledge, and you want to remember that. And you're like, "Oh my God, I need to tell my team. It's such an amazing insight." So what do you do? You have to get off your bike, take out your phone, open up the Notes app, write down what you just heard. Maybe save the timestamp, maybe take a screenshot to remember the timestamps to be able to come back to it. So you do that a couple of times, but then after the third or fourth or fifth time you're like, "Okay, come on, this is too cumbersome. I'll remember it." Yeah. And then you get to the office, and of course, you've forgotten about it. A couple of days later you might remember again, then you try to find it again in the podcast, but of course, you can't because it's all in audio. So you use that skip 30-second button like 500 times, and once you do find it again, you want to share it with your colleagues. So you send them the episode, and of course, they don't listen to it because at the end of the day, it's a two-hour conversation. So these were all of the issues that I experienced. And long story short, another friend of mine, Ferdinand, who's not with us here today, he and I, we were talking about this. Both our background is in AI, and he was saying like, "Hey, I have the same issues." And given that our background was in AI, so this is before ChatGPT came along, we thought, "Hey, there's a better way of solving that." With all of the advancements that are happening, we should be able to create a more usable and delightful experience there. So that's how I started and maybe, Mikel, you can tell the story how you got into this. **Mikel Corcuera:** Yeah. I met Kevin in that startup, in the startup we worked before Snipd. I came to Snipd in a different way. I was there working as a front-end engineer. I was there for four years, and at some point, I wanted to do something different. We were working with web technologies with Angular. Yeah. I heard about Flutter from our CTO back then, he was a back-end developer. He learned about Flutter, he was excited about it, and I said, "Okay, let's give it a try just to learn something new." And I really started liking it, I really liked the developer experience. I really liked where this is going. I also liked it because it was new for me. I wanted to build an app. After some point with that, I got a little bit bored of the product we were building and I decided to leave.And I said, "Okay, I leave and I figure out what I do." One thing that was clear for me was I wanted to learn Flutter in between jobs just to see whether I would land a job with Flutter or not. And I wanted to find a job where I will build a product for consumers. In the previous startup, it was B2B2C, and even though we were building something for the end user, it was very hard to see that connection. So we were building something, the bankers would see it, and I have no idea how people were using it, whether people were using it. So that felt for me, it was like not very connected to the product. So I decided to learn Flutter and I knew that Kevin and Ferdi were building something. I had no idea what it was. I said, "Yeah. I'd rather learn Flutter while doing something that makes sense, some cool idea, than just do another to-do app, because it's like the usual learning exercise." So I talked to them, I said, "Okay, I have three months in between jobs. Let's get together. I will help you build the UI," because they really needed help in that area. They had some prototype that never sees the light of day, so then I started working with them. But basically for me, it was a learning exercise. I said, "Okay, while I find another job." And time passed, I was doing interviews with other companies, and at some point I had two offers, plus I was still excited to work with them.And the three of us sat together in a meeting because I was in Spain back then, and I decided to say no to those offers, and they offered me to join Snipd as a co-founder. It was still not founded, but during \[inaudible 00:08:40\] the three of us would find the company. And I said, "okay, why not? If I don't do it now, I don't have kids, I don't have family." So it was why not? I really enjoy what we are doing here. I think the idea was amazing, and the experience of building something from scratch and the idea that that would be used by someone. We still didn't have a prototype that we released, but it was something that I was so passionate about being able to build the app, have it in my hands. And knowing that that would be the thing that the user will use. So I decided, "Okay, let's go with it." That's how I started, yeah, that's how I joined them. **David DeRemer:** That's so cool. **Mikel Corcuera:** That was already four years ago, more or less, right? **Kevin Smith:** Yeah. **David DeRemer:** Four years ago? **Mikel Corcuera:** Yeah, so then we decided to start. And that's when we started really building it, integrating the idea, and finally releasing the first version of the app. **David DeRemer:** That's great. I love the origin stories here because Kevin, you identified a need based on your own personal experience trying to listen to those. I know exactly what you're talking about. There's podcasts I listen to. There's one I really love called 2Bobs. And it's like they have so many insights that it's just like if I go for a walk and listen to that, I'm just basically my head's down in the Notes app the whole time writing notes. And so something like Snipd comes along and you're like, "Oh, this is doing that same thing, and giving me the opportunity to collect those things and mark those insights." So you building it from that insight and being like, "Hey, I have this need." And from that position of personal desire, and I think so many products have come from that. And then Mikel, on your side, it's like, "Well, okay, I want to build something that actually I want to use, that is something useful for me," even if it wasn't the thing that you originally dreamt up for yourself. So there's just a really interesting insight there of how these things and teams materialise, which I think is a lot of fun. And I do want to get into the technical details of how you guys have built it because there's some AI stuff. But before going that far, given it was a personal idea and podcasts are pretty popular, one of the things I would imagine would be difficult is to figure out the initial feature set for this. You have all this AI change, people consume these things in different ways. You have people riding a bike, walking down the street, in a car, sitting on a couch, for different reasons. Some people do it for entertainment, some people do it for learning and personal development. So when you were getting started, how did you really decide what the user really wanted? And maybe over time, has the feature set changed from the idea phase to what it ended up being? How did the actual concept develop over time? **Kevin Smith:** Yeah. This is actually quite interesting, because now looking back when we had these first conversations and we're prototyping first versions, we did not think that the aspect of saving the insight would be the biggest part of it. We thought the big thing is discovery. Because what we saw was if we have users who are saving the best insights in podcasts, there would be 10, 100 times more people who would want to just discover them. So it's almost, in a way, the idea was almost a little bit like a TikTok for podcasts that we then came to be like when talking with one another. Where a certain section of users would create snips while listening, and another section would just consume these snips or use them as a discovery tool. So when we actually then started to create the first versions that we got into people's hands, we thought the difficult thing will be to get people to create snips. I didn't think that there was so many other people like me, and then everyone would consume them. But once we gave people the prototypes, what we saw, found out was actually exactly the opposite. Is that all of them were creating snips, and they weren't just creating one snip an episode, but 10 snips an episode, and they really loved that. So this was how it's almost like the feature set didn't really change, but the focus of what was the main thing that we put front and centre? That then changed through building something and putting into people's hands and learning from the feedback. **David DeRemer:** How did you collect that feedback? Did you just talk to your users? Did you have a feedback capture tool? I'm just curious how you uncovered that insight. **Kevin Smith:** Yeah. That has developed quite a bit over the years. So in the very beginning, we didn't have an app live, so we were just on TestFlight. So this was just talking to friends, giving it to friends, asking them to use it. And then once we had a version live, then we always had a big feedback button at the top of the home screen where they could immediately give us feedback. And that was, I think, one of the most important things that we did in the beginning. To really have that front and centre, it actually took up a lot of space, so any designer would've hated it. But that's the way how we collected the feedback and it was just a Google Form behind it. There was no fancy whatever, and that worked quite great. **David DeRemer:** That's awesome, nice. Well, so let's get into the technical side of things because you have an AI story here, and of course, AI is super hot right now. And I think you guys got started with it, I think, before all this became super hyped. Because you've been working on this for four years, and had these ideas from the beginning and the AI was part of the genesis of what you could do here. So maybe walk us through a little bit about some of the technical choices you've made along the way, both in terms of how you decided to build it. How did you prototype this? You mentioned getting some TestFlights out and things. And then maybe how has the technology really driven your approach to the user experience itself in terms of what the product actually does? **Mikel Corcuera:** Yeah, maybe can answer that. I think at the beginning, the most important thing for us was not that much about the technology we were using, but that we wanted to ship something as fast as we could to get feedback. In the beginning, the reason we decided for Flutter, was because I was learning Flutter and it was, "Okay, I will learn Flutter with this." Kevin and Ferdi, they knew Python, so they will do all the debugging in Python. So for us, the main focus was, "Okay. Let's get something out as fast as possible for our friends and family first, then to TestFlight, and then to the app store." And I think in total, it took us less than six months to go all through that process. I still think that the decision of using Flutter was the right one. At the beginning, it was only me working on the app side, now we are only two, so it's a pretty small team. And the reason we can have both an iOS and an Android app is because we use Flutter. Or we could have used another, I don't know, React Native or something like that, but I do think Flutter has allowed us to build fast and to build an app that looks very nice. I think one thing that people talk a lot about when talking about Flutter, is this developer experience and hot reload. And I think it's not only important for the developer that that's there, so of course, I like working with Flutter because the developer experience is much nicer than maybe Angular. But I think that is reflected also on the product at the end, because I have joy creating the UI. I will go the extra mile and try to make it much nicer. It will also be faster because of the iterations of the UI. We know how it works. We also don't have a designer. We work all as the designers or me, I work as a designer, but I don't usually do it on Figma. Because Flutter is so fast to iterate on, we can really just start building, do maybe a few prototypes in Figma, then jump directly to code. And because it's so fast to iterate and move the button here, put this colour here wherever you want, I think it allows us to go to the end result much faster. And one thing that is very important for us also is the interactions, like how the user uses the app and how it feels. It's not just how it looks, and I think that's very hard to replicate on Figma. And with Flutter, you can really have the app in your hands, change a value somewhere, try it again, see how this scrolling feels, see how this animation feels. And I think with other frameworks, that will take you maybe you have to restart the app, go back to the screen you were before, replicate the state, and the whole process takes you much longer. So I think the willingness from the developer to get there is sometimes not there or sometimes the time is not there, so I really think that Flutter allows you to create these polished experiences that is so much harder in other apps. And I think in the way the developer is also, I think, they have more joy using Flutter, at least for me, than using let's say Angular to create these polished experiences. So I'm still quite happy that we use it. We, of course, have found that some things are not as easy in Flutter. Everything that has to do with native code or native interactions, it's always a step, you have to take another step. You have to integrate it with a plugin, you have to create a plugin yourself. So sometimes it's not as easy to interact with the native side, but all in all, I'm quite happy that we really decided to use it. **David DeRemer:** That's a really interesting use case for startups there too, where if you have engineers who have a good visual eye, there's the traditional designer developer handoff. Or you do everything in Figma, and then you document that and you hand it off to the engineers who implement it. But I think that's an interesting insight in terms of especially if you're trying to do something fast that's new to the world where you're trying to test it. Do that quick prototype in Figma that gives you the direction, and then just get in there and just start doing the last 20% in code itself. **Mikel Corcuera:** Yeah. And I think one thing that it's important of doing it in code, is you have so many different cases. I don't know anyone who has built a new one knows that, "Okay, the designer will test these few fields with a nice case, a small title with a small description." But then you use the app and there is people, I don't know, there are some podcasts that have five lines or title, and it doesn't look nice on Figma. If you put it there and you just use their design, it doesn't look nice. So I think by using real data also while you are designing the app, it also allows you to catch these cases much earlier and try to figure out a solution for those. And I think just feeling it in your hand, it's also a big thing. It can look nice on Figma, but then you use it and it's, "Okay, this doesn't work." **David DeRemer:** Yeah, no, totally. No, that makes total sense. And that's where you're right, the hot reload and even just the ability to iterate quickly and get it on different devices and understand. Even on iOS or Android, does this interaction need to be a little different on Android, because of something about the OS? Or something that you really sometimes can't tell until you put it on a real device and give it a try, and you're right about all those states. One of the hard things is I think really great, creative designers, they want to do the big-picture stuff, they want to build really cool interfaces. They don't necessarily want to tell you all the 37 different states that can happen when you're trying to enter a password on the login form. But those things are really easy to figure out once you're in there trying to like writing a code and figuring out all the different situations you're trying to handle. **Mikel Corcuera:** Yeah, exactly. **David DeRemer:** That's awesome. So it sounds like that front-end choice, which was out of a position of just curiosity and interest on your part, ends up becoming a bit of a strategic advantage for you guys as you get going. **Mikel Corcuera:** Yeah, I would say so. And I think that allows us also to maintain the small team, which is also some advantages. Also, maintain only one code base for both Android and iOS, you don't have to think about when do you release one or the other? So I think it has a lot of advantages. **David DeRemer:** It's funny because you mentioned about that factor, how it allowed you to get on iOS and Android in prototyping early stages so quickly. And I've been doing Flutter so long now, it's been since 2017 that we've pretty much exclusively been writing apps with Flutter. That it's almost I forget that the old way was you had to have two different sets of developers, or you did one and you only did iOS. And then your friends and family who were on Android, you're like, "Well, if we're successful, I'll get around to building something for you." **Mikel Corcuera:** Yeah. That's a good decision if you start, I would say. You can also see as soon as you start monetizing, most of the money comes from iOS, so that would be one strategy. But as soon as you want to then release on Android, what you will have to do to port everything to Android is huge, right? **David DeRemer:** Yeah, massive. **Mikel Corcuera:** Depends on what your app is, you may have to be in both platforms anyway. **David DeRemer:** Yeah. And I think the key is you still get that monetization boost from iOS when you focus on one of these multi-platform or cross-platform tools. And I think the truth is, I think, React Native has gotten a lot better too. Early iterations and approaches of this stuff were just not quite there, they were substandard. And I think a lot of companies that tried them or teams or individual developers got burned by it and felt, "This isn't really quite what I'm looking for." But I think Flutter and React Native have both gotten so good that, I don't know, it's crazy why people would be building fully native at this point. For me, from my perspective, but I'm a little biassed. **Mikel Corcuera:** Yeah, me too. I'm a little biased as well. **David DeRemer:** Let's talk a little bit about the back end here. It sounds like on the front end, you had a technology choice that helped you iterate really quickly and get out there, but let's talk about back end. You mentioned Python, that you guys had experience in Python. How did you go about figuring out what to do? Especially with the AI side of it and things, how did you make some of these tech choices early on? **Mikel Corcuera:** I think for the AI, I will let Kevin explain how he started them. **Kevin Smith:** Yeah. I think in general on the back-end side, the two interesting things in the beginning were what to do with the AI, but also what to do with the data. So maybe that's something if you only care to go a bit more into detail because we started with Firestore and then at some point, migrated everything over to Postgres. But with the AI, so back then, just to give a bit more context, so this was four years ago or something like that. So this was long before ChatGPT or ChatGPT APIs were like a common thing or even available. So the way that you got AI into your app was to have either you built, you trained the model yourself, or you used an open-source model from Hugging Face. And then you fine-tuned it for your own use case, and then you deployed it on your own servers. And all of basically the entire AI research community, the AI world was running on Python. And that was also what we had experience in before because we were doing that in our jobs before, so that was for us, an obvious choice. And in the meantime, I think Python has just gotten bigger and bigger because the AI community has grown so much, and it's still Python first in a lot of these cases. But what we did there was had basically, as I just mentioned, we had these open-source models from Hugging Face that we used. We fine-tuned them, we deployed them on GPU servers that were running on Google Cloud. I guess most of the challenges there that we had, they were mainly, I would say, DevOps challenges. Once you had the model working, actually keeping it running, we didn't just have one model. That's also one big difference to these days. Back then you had, for every single task, you had a separate model and you fine-tuned it to do just that. You couldn't just ask some generic question. It wasn't just like, "Hey, give me a summary. Hey, give me a quick overview, give me a title." So we had, for example, an AI model or back then you would call it an NLP model, natural language processing model, that would just create a title for a snip. So the input was the transcript of what the user saved, and the output was a title. And then we had a 100% completely separate weights model that would create a summary for it. And so for this, we had various of these models and basically on our GPU servers, we were loading them into the GPU memory, then unloading them and loading the other one in. And there were just a lot of issues of some stuff's staying in memory, and at some point you can't load the new models anymore. And we had a lot of issues that we actually never solved. So we had jobs every two days, we would restart the GPU server. The typical things of just like, "Yeah, just get it to run somehow." So these were the challenges in the early days, and now in the meantime, that of course, has changed a lot. We still have some open-source models that we're running on our servers mainly for where we see that we have cost advantages. But most of our AI processing is now happening via the APIs from ChatGPT, Google, also Perplexity we're also using, but that comes with new challenges. **David DeRemer:** Well, let's dig into this, because I'm sure a lot of people out there, AI is such a hot thing right now. And I think that there's a lot of confusion and a lot of anxiety, and just a lot of uncertainty around how do you even do these tools? Do I need to be a PhD in math and an AI engineer? What is that even? Do I need to be a data scientist, or is this just hooking up an API like anything else? So I'd love to dig into this with you guys and maybe start by being, it says, "Powered by AI. Snipd is powered by AI. "So maybe when you hear a phrase like that, or in your context when you say it's powered by AI, what does that actually mean to you when you're communicating that? **Kevin Smith:** So this is really a big topic that we have internally, is it AI-powered? Is it AI-native? Is it even something that we should mention at all? And we've gone back and forth. Now, the reason why we call it an AI-powered podcast app or sometimes AI-native podcast app, is because these days, we are in this transition phase where it's still something special that your product has AI, and at least AI baked into the core functionality. And that helps gets people interested in like, "Oh, what are they doing different? This is something that wasn't possible before." So it creates some curiosity. But to be honest, the way that we are looking at it is we see AI as it's basically like the new electricity. So in the future it would just be in everything. And it will be so common that you will not say, the same way today you don't say, "Hey, I have this electricity powered watch." No, you just have a watch. In the same way in the future, we will not say, "I have an AI-powered podcast app." I'll be like, "No, this is just a podcast app and obviously it has AI baked into it." But if we use these terms, if we do use them internally, we do actually love the way the term AI-native over AI-powered. Because AI-native really means to me that you thought through this from first principles of what is now possible. Thanks to that, we have this technology. Rather than just looking at, "Ah, this is how we used to do it before. Okay, now we just add a little feature of sprinkle on top." And I think you can see some very famous examples. So there's Cursor, which I assume a lot of your audience is familiar with. That was a case where before you had other, you had a GitHub Copilot thing, which really much more felt like, "Okay, it's just now a bit better of an autocomplete." Because it really rethought the whole process of, "Okay, if we have these powerful tools, how can we integrate them much more natively?" So that's what we try to do with Snipd, like really things from first principles. **David DeRemer:** I like that. So AI-native, AI-powered is that first principles of what can you do with this technology and start there, and then build the product idea. So was AI always a part of the product vision from the beginning then? Because you said you started four years ago, I don't think that this was a super common thing that everybody starting out was like, "We're going to use these tools." Because I think it was daunting building the models and getting into that stuff. When did AI become a core part of the mission where you really made the product this AI-native perspective? **Kevin Smith:** So it was from the very beginning. So this was, it basically all comes back to myself and our other co-founder, Ferdinand, our backgrounds being in AI. And for me, basically Snipd was built on two big, let's say, realisations or beliefs that we have. One is that we see podcasts as one of the largest knowledge libraries in the world. And two, we believed early on that AI would fundamentally change how we can interact with spoken audio. And really, the point that made it clear to me this realisation long before ChatGPT came along, was a paper that was published by Facebook's AI team. Where back in the day, what they did was they used the two main technical innovations that led to huge breakthroughs in text processing with AI. They applied that to speech processing, and it just worked really, really well. And that for me was the moment where I realised, "Wait, everything that's happening in NLP, like this text processing at the time, all of that will happen in speech as well." And if you have both of these worlds, now you can do so much more with spoken audio.So we had that vision from the very beginning, but it wasn't clear from the very beginning what we'd be able to do with it. **David DeRemer:** So that's interesting. So then you're not clear what you're able to do, but then you get in there and you start experimenting with these tools. And at the time you didn't have, when you first got started especially, you didn't have these large language models that are so popular now. How do you go about answering that question, what actually is possible? How do you go about experimenting with different open-source models and approaches and tools, and then figure out A, what's possible reliably? And then B, which approach or which tool gives you the best outcome? Because one of the challenges here with AI is they're not deterministic, so you get different outputs each time. So how did you go about making these decisions, especially in the early days? Now it's a little bit more obvious like, "Okay, I'm going to use Claude or I'm going to use OpenAI or Gemini." There's some more specific directions, I think, people are more likely to go. What was your insights in terms of how can we even today make better choices with these tools? **Kevin Smith:** Yeah. I think, again, it has changed quite a lot if you look at back then and to today. So back then, in a way now looking back, maybe it was even easier because you had less choice in a way, and it was clear what you had to do with it. So there were a couple of open-source models, but then you had to take that and you had to fine-tune it for your task. And it was just for a single task usually that you did that for, and then you just saw how well that worked. The big benefit that we had, and that I also recommend to anyone working on something today is we didn't aim for perfection. When I now tell you, "Hey, there was a summary for this Snipd, this insight that you just heard." Today, everyone thinks of this perfect summary because everyone knows how ChatGPT is. Back then our summary, let's be honest, it was actually crap, but it was good enough for the really hardcore person, who really, really, really wanted this, to use it. And the summary, every third word or something would make sense, and you'd be like, "Yeah, yeah, yeah." A couple of keywords triggers, then they talked about this and about that, and then it sort of worked, and we just bet on all of this getting better over time. And the way that we actually looked at it was that we thought we would have to do all of the work, so we did not envision these APIs to just come along, especially not this early. So we envisioned us having to do the work to make it better and better and better, of course, with again, standing on the giants of the open-source models that at the time, even OpenAI was still open sourcing some models and Facebook was open sourcing a lot of models. But we thought over time we would get bigger, we would get more successful, we would have more data, we would have more people on the team, and with that, we would improve these ones over time. **David DeRemer:** Nice. So that's how you can embrace the hallucinations, just with an innate assumption that it's going to get better? **Kevin Smith:** Yeah, that's how we saw it back then. Now today, I think even the term hallucination back then, it didn't really exist. I think this is actually more a bigger problem today, especially also for us, because we've outgrown the early adopter that forgives everything, the weirdest hallucination. But when it comes to hallucination, I think the underlying thing that you're referring to is it's not deterministic, that you have this randomness in there. And the way that we handle that is, I think that is really something where if you're a programmer, you do have to adjust a bit how to think about coding. And you have to free yourself away from this deterministic flow, and much more the analogy that I like using is having a bunch of interns work for you. So it's like you have a task here and you hand it off to an intern. The great thing is you don't just have to have one intern, you can have 10 interns, and you can even have interns that judge the output of the other interns. And this is more the analogy that I use to try to handle all of these cases. If you can have as many interns as you like and you want to improve the quality, one way is to exactly split it up, have two people, two interns work on the same thing. They come together, they judge what's good, what's not good. You have a third one who's maybe the final decision maker and all of this, and these are all ways of improving the quality in handling of these cases. And maybe the final thing that's just important is you have to live with the fact that it will fail sometimes. This is another thing where you have to adapt the way that you think about code, where usually if you've written everything nicely, it shouldn't fail. Or if it fails and there's something, there's a bug and you need to fix it. No, it's just you have to live with a certain percentage depending on how difficult the task is, that that will fail and then fail gracefully. Also, the end-user experience has to reflect that. And also the user has to, yeah, that's just another thought that just came to mind. One thing that has helped us as well is that we've always been very clear that we are an AI-powered podcast app and that we have AI everywhere. And so the user then also learns, "Okay. If this is written here and no, it's generated by AI, can I trust it or not?" Or if it's missing something, "Okay, probably the AI did a mistake here," and that then also helps. **David DeRemer:** Yeah. Well, I think we're in an era too where people are learning to appreciate that because the code is not deterministic. It's just no different than if a friend of mine listens to our podcast that we're recording, and I was like, "Hey, tell me what you think," and they;d give me a summary. They're going to not get it totally right. Even our memories are not super good at exactly being factual of this stuff. So I think people are getting used to that, but AI is everywhere. So you guys are using AI to actually develop the app too. Is that part of your engineering workflow? Is it full stack for you guys everywhere AI? **Kevin Smith:** Yeah. Mikel, do you want to? **Mikel Corcuera:** Yeah. I think each one of us on four different tasks, we use it differently, but it's certainly something that is in our workflow. I think for me personally, I use it differently depending on the technology I'm using, how familiar I am with it, what kind of tasks have I tried? With Flutter, I know they go from A to Z, and the context is much bigger, so I am there less reliant on AI. Now, for example, if I'm trying to build something new, one good example was how we build a new landing page. We use v0, I don't know if you're familiar with it, it's from Vertex. It's like coding, let's say, tool to build web apps. And the cool thing about that is that, so we are four in the team, so it's two front-end people. We also have Ferdi, who is maybe working on the AI or in the back end, and he started building the website. So the cool thing about v0 and this AI tool was that he was able to grade the main content of the app without having any knowledge on web or design. Even though then I took over and I had to change a few things here and there, I think it was very helpful for us as team to have him be dependent on, "I want to focus on the content," but of course, you cannot just write the content on a Notion page. You would still have to visualise it to see whether it works or not for a landing page. I think it was very good for us that he was able to work on that independently, create the basic look and feel, create the content, and then handed that work to me because I have the experience on the front-end side to polish it and making it production ready. **David DeRemer:** That's awesome. **Mikel Corcuera:** So that's one of the extreme of how we use these AI tools, and there is something in between. For me, with Flutter, as I said, I usually jump to the chat and say, "Hey, how can I do this? How can I do this other thing?" I think for Flutter it's not as powerful. There is less code around than, for example, let's say React, so you can feel it also in the answers it gives. But we also have a web app, not web app, we have the share page and we have a few web tools, and there I'm less familiar with it. So there I'm much more leaning to Cursor or something that is integrated on the IDE to help me, "Hey, how did you create a component in Angular?" I forgot about it because I didn't work on that for three years, so that's super, super helpful for me. And there I have, let's say, less expertise on what the process is, so I find them much more helpful there. So for me, it's depending on my knowledge of the kind of task that I have, but definitely we are all using it on developing. I think some of us use- **David DeRemer:** That's a cool framework to think through it actually, because I think a lot of engineers are struggling right now with figuring out where and when and how to apply these tools. Because you get pressure from socially just reading LinkedIn or reading tech blogs and stuff of everyone's using all these tools to be so productive, what should I be doing, me? But it's interesting, because if you know the app super well and you already have that context, and you can go in and be really productive and really quickly produce something, then maybe an AI Copilot is not as useful because you can just yourself get in there and get it done. If it's something new you're trying to add or something you're not an expert at, it's helping you to get things quickly. And rather than hiring a web engineer or a freelancer or somebody coming in to do that, you can get somebody to move it far enough, where then you, as a competent engineer, can get in and get it the rest of the way. And so I think that's a good framework because I think sometimes people are like, "Oh my gosh, do I need to replace my core expertise with this?" And it's like, "No, it's an augmentation that helps you get better superpowers," right? **Mikel Corcuera:** And I think at some point, you need this core expertise also to judge what it's giving you. We give you a lot of things and some things are good, some things are not so good. And some things will work, but some things will work in a way that if you have the expertise on Flutter, I know that this works, but I know the issues it may have. So I think it's still important to have that so you are the judge. You're reviewing what the AI is outputting. Of course, you will test it, but there is much more to code then. I think as Kevin said, it's like an intern. You can give it to an intern, he will give you something that works probably. We create a PR, it works right, but that's why we have PRs. It's not just that he passed the test, like, "Okay, how did you code it? What are the issues that this may introduce or not?" So you can then guide the AI to build what you want, how you want it. And if you have the expertise, you can get it much better and maybe you need it in less places. I'm still personally learning how to use it. It's not that I have the answers to these things. It's also a new tool for me, and in some places I'm more comfortable using it, and in some others I'm still like, "Yeah, I don't know." I have used it and it didn't work out that well, but I think I'm still also experimenting on different tools and different ways of how to use it. As you said, I'm also going to dip into LinkedIn, into, I don't know, Hacker News. You get these stories and you are like, "Am I missing out? Is it good? Is it bad?" And it's also a little bit, it gives you a little anxiety, but I think as developers, we're also afraid that, "Hey, are we losing our jobs?" It's a little bit terrifying, but as you said, it's also an augmentation. I think it's much more positive to look at it that way like, "Hey, I'm a good developer already, or maybe not, but I think I am. But then I can be even better and can be even faster, and I can maybe delegate the things I don't like, or I'm not that good at the AI." And I still use my knowledge to be a better developer than someone else that uses the same tool, right? **David DeRemer:** Totally. **Mikel Corcuera:** Yeah. I think we all still have to figure out what is our workflow? I think also before AI, everyone had a different workflow. Not everyone was programming the same way, and I think it's the same. I think it's important to be open to try it and see how it fits in your workflow. **David DeRemer:** Yeah, I love that. There's so much fear and hype, and anxiety and just chaos out there around this whole change. And it's really as you think about it for us, those of us that have some engineering skill or capability, it's no different than it's ever been in terms of something new comes along and you got to learn it. And the true expertise is being the engineer of understanding how to use your tools effectively to get your outcome. And the tools change and the things that you need to figure out and learn change, and I think everyone's still just figuring this out. There's like when you read LinkedIn, it's full of people who claim themselves to be experts and wizards and masters of this stuff. And the truth is no one's really been doing this that long. And I always say, "You guys are one of the apps that actually is on my phone that has core AI features." Whenever somebody's like, "Oh my God, are we behind? Do I need to shift my career to AI or something like this?" It's like, "Okay. Think about how many apps you have on your phone where there's an AI feature that is a core element of what you're using right now." And there's just very few still that actually have a lot of like you guys are definitely one of them, and then you have the core ones like ChatGPT or things like that. But we're still so early in this, and that's what I think is really interesting about your team. So one of the things I wanted to ask you about was, there's a couple of nuances here. You guys have AI background. You approach this from an AI-native approach. You are using actually AI features in your product. So it's not just that you're using AI tools to be more effective in productivity and delivery, but the tool itself is very AI driven. And you're a small team, you're a startup, you're a small, nimble team. And I was curious, what kind of recommendations or insights would you guys have for larger, more established teams based on your experience over the last several years getting this product going? At VGV, we work with a lot of very large enterprise companies that have big teams, complex internal structures, complex existing code bases. And one of the things we often want to bring to them or that they want for themselves too, is how do I get that more fast, innovative, quick, small, lean team dynamic that you guys are enjoying right now into my company? And I'm curious what kind of advice you would give to those leaders to how to infuse some of the things that have worked for you into their cultures? **Kevin Smith:** Yeah, shall I take this, Mikel? **Mikel Corcuera:** Yeah, take that one. **Kevin Smith:** Look, at the end of the day, I don't think we have the perfect silver bullet here, but I can tell you what I think makes us fast. It's not just because we're a few people. A few people shouldn't make you fast, but what are then actually the characteristics here? And one of the things is that all four of us understand the product deeply. We understand the user. All of us have, just as an example, all of us actually are doing customer support. So each week, it's someone else who's handling that so we have really the direct connection to the user. We are all using the app ourselves, and basically we all have the authority to write and push code. And once you have that, then every single person can act autonomously. Of course, it's important to have a direction. We need to align and everyone needs to know what is the important thing that we're working on right now? And this is the big project that we're working on, et cetera, but it's often then the small things. Even if we all say, "Okay. We have Project X, this is what we're doing now," but there are so many small, little decisions that everyone has to take. And if all of that has to go through some kind of approval process, then you just get slowed down so quickly. And I believe just looking at a lot of what I hear from colleagues who work at big corporations, this is something where I realise it can never be as fast as if you're just four people. But it seems that there's so many processes in place, especially at older corporations, older companies, that are just actively trying to go in the other direction basically. Like, "Oh my God, no, you can't push code without, I don't know, having five, 10 reviews and it always has to be like this." Yeah, this is a bit from my side what my experience and thoughts there are. Maybe one additional one is definitely if you work in a small company, especially in a startup, it is the success of each single employee is basically equal to the success of the company. It's just very difficult to decouple that, and in large corporations, that's actually often quite different. The success of a single individual is more about getting the next promotion or being part of this next "cool, big project," or getting a salary raise. And all of these things can very easily be completely decorrelated to the actual success of the company, so I think that helps in a small company to then run in the same direction. **David DeRemer:** Those are great. I think you're totally spot on. It's that empowerment thing and the complexity and the trust, and these big companies build all these systems and it's hard. It gets really complicated. And I think that is ultimately, there's so many management books and all the guests we've had on our podcast here, it always comes back to team dynamic and leadership trust.Does the leadership really trust the team to act and to not be super nitpicky about everything they do, and let them do their things that they want? And communication is the hardest thing, keeping everybody aligned, so I love that. Thank you, guys. This has been really great. I want to wrap us up here. And one of the things I just wanted to ask as a parting shot is, given that you guys have been in AI-native land for several years, predating its popularity and the broader zeitgeist, what do you think our AI-native future really looks like? Where does this go for Snipd? And where does it go in general for engineers, and for knowledge workers and everything else out there? What do you think? **Kevin Smith:** So I think the most important takeaway for me, is that I believe all of us still underestimate the impact that AI will have and how much it will change things, and that includes myself. Because I think for us, in general as humans, it's so difficult to understand anything that's exponential or goes in the direction of an exponential progress. And that is what we're going through right now with these models. So I'm actually quite careful always to predict or try to frame myself as knowing what will come. And more try to go back to just remind yourself that you're underestimating it and then that's already a good approximation, but I can give you a couple of things. One thing that very few people are only talking about, I believe, are AI in the real world. Because right now, most of the very public process, I would say, has been happening in the digital realm. Basically, all of these LLMs and then with the coding, and generates a generation of images and audio, video, but you can see how these humanoid robots are making more and more progress. My wife, she works for a company that does autonomous robots, and it's just clear how this will happen. And it might be a bit like autonomous driving where we're like always three years away, always three years away, but you can see what's happening with autonomous driving now. I think in the last five months, Waymo has done twice as many rides than in all of the years before, something like that. They have, I don't know, 20% market share in SF for ride hailing. It's happening slowly but then suddenly all at once. So if I can just say one thing, maybe real-world AI is something that a lot of people are not talking about yet. **David DeRemer:** Very interesting. Mikel, do you have any thoughts? **Mikel Corcuera:** Yeah. To be honest, I also have no idea how it will change. I only know that it will change a lot, and I think it will change a lot for us developers. That's maybe where I'm more worried or excited both at the same time, but I think there it will change a lot. And I agree with Kevin that we really don't know how much it will change. I think I would say much more than what we think it will, but I'm trying to have a positive attitude in the sense trying to get away from, "If I lose my job, I won't be useful." I want to think that, "Yeah. We will be able to maybe, as developers, build much more, much faster, and still enjoy using these tools." So that's where I have my hopes and views. **David DeRemer:** Anytime we have these major changes, but there's so many examples of technology over the history of mankind just having these radical shifts that change workforce. And honestly, it's a little bit disappointing and disturbing to me that people feel like, "Oh, because of AI, suddenly there's just going to be all this unemployment." I'm like, "I feel like it's not really giving all of those people a fair shake." It's like you think that something's going to change here, and they're just going to say, "Well, shucks, I lost my job, I'm not going to." People are crafty, we have ingenuity, we figure new things out to do. And realistically, what I've always felt about AI and some of these tools, these code assistants and things like that. Really, it's the same thing that Flutter did for us. It's like it's giving our developers more productivity, more superpower, more better developer experience. And I think all the tools that people are worried about AI, "Oh, AI is going to come and replace my job." No, it's not. It's just going to make your job better. And you just have to take it upon yourself to graduate to more interesting things that you can do with your time, and I think that's the exciting part actually. **Kevin Smith:** From our side, if there's any developer listening and is afraid that they will lose their job, I can only say that we try to use AI as much as we can in our coding. And I think we have enough things to code for at least 10 more developers easily. There's always something, we have such a long list of ideas. I think, what's it called, general in paradox or something like that? I forgot the name. I think this is so true. Basically, if a good basically becomes cheaper, then the demand for it actually goes up and it goes up by much more than the "price decrease." And I think software is one of these things where that will happen. **David DeRemer:** That's a great thing to end on. Positive note, I think we have to be optimists, and we have to look at this as the opportunity and the excitement for what's ahead. There's so much doom and gloom, it's unfortunate. We've got to stay focused on this is going to make everything so much better in the long run, and so I thank you guys for coming on here. Thank you for making Snipd. It's a really awesome tool. Really cool to see that connected from your personal observation and need and your own passions, and then leveraging the latest and greatest tools to make something people enjoy using and makes a difference. So thanks so much for coming on, telling your story. **Kevin Smith:** Yeah, thanks for having us. **Mikel Corcuera:** Yeah, thanks for having us, David. **David DeRemer:** Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review, and don't forget to subscribe to get our latest episodes. Thanks again and see you next time. ### From Platform Silos to Unified App Development Source: https://verygood.ventures/podcasts/landon-robinson-etsy-from-platform-silos-to-product-unity-a-new-era-of-app-development/ Discover how Etsy Staff Engineer Landon Robinson and his team adopted Flutter to scale the Etsy Seller app. Learn about their “away team” model, cross-platform architecture, and how focusing on business impact drives engineering success. {% audio_embed url="https://player.cohostpodcasting.com/7843684d-4094-4c88-8ccc-40da304809c9/8a3fa4fe-6ba5-4fb7-9fad-a1bf3e04469c" /%} ## Transcript David DeRemer: Hi, I am David, and this is Build to Succeed from Very Good Ventures. Today we talk with Landon Robinson, a staff engineer at Etsy. We're going to talk about how to build success in mobile in the two-sided marketplace, as well as the importance of understanding the needs of the business as software engineers. So, let's build some new ideas. All right. Hi, Landon. Thank you so much for joining us. Welcome to the Build to Succeed podcast. Landon Robinson: Hey, thanks for having me. David DeRemer: Great. Why don't you get us started? I know you work at Etsy. Maybe you could give us a bit of your background, your story, your career arc of how you got here today and kind of catch everybody up to what's going on for you these days? Landon Robinson: Yeah, absolutely. I've been in IT probably about twenty-some years. I won't bore you with a lot of the early career. But I started my career in software development doing web. Traditionally it was the Microsoft.net. Everybody wanted a web application at the time. I did that for a few years, learned a lot about SQL and things of that nature. And then mobile came out somewhere around 2007, I think. iOS and Android, and I did a lot of the Android work early on in my career, probably for the first seven or eight years, mainly because it was a low barrier to entry, you only had to have a PC to get started. So really sort of dove into the Android development and then Swift came out. I thought that was pretty cool, because I did not like Objective-C whatsoever. I did iOS for a little bit. And then big Flutter came out. Big Flutter came out somewhere, I don't remember, 2002 or something, '17 or something like that. And I was a traditional native guy, of course, and you couldn't get me to try a cross-platform framework and all. And so Flutter came out, which completely changed my mind. And I worked with a few companies that worked basically migrating native to Flutter and then that's how I ended up at Etsy, because they were in a similar position where they were in a position where they were looking to move away from native development and more toward cross-platform using Flutter. David DeRemer: So you have a awesome story there like Web into native, both iOS and Android, and then discovered Flutter. What was it about Flutter when you saw it that suddenly got over your concerns about cross-platform? Landon Robinson: I think for me, the fact that Flutter builds pretty much every pixel on the screen from a displaced perspective, they don't reuse these wrappers of native components, was a really big draw to me. Because it was very difficult for me as a native guy to tell that this was done in cross-platform. And then I think the tooling that Flutter initially had, it was very akin to native, where I've had experience other cross-platform technologies where you didn't have the debugging tooling or things like that. Which just wasn't there. The developer experience was there. So coupled with the developer experience along with the ability to paint every pixel is grand, just sort of fell in love with Flutter. David DeRemer: That's perfect. I think there's a lot of people that follow that pathway and there was something magical about Flutter, especially early on where people just kind of felt like it made things easier again. Made it kind of fun to do. So now you're at Etsy and I believe you're working on the Etsy seller app initiative. Can you give us an overview of your current role and what's going on with Etsy these days in terms of technology and mobile? Landon Robinson: So for those that don't know, Etsy is two-sided marketplace that sort of brings buyers and sellers to together. And then unique crafted items is sort of what brings everybody together. So, Etsy traditionally has a buyer app and a seller's app. And I'm typically work on the seller app, so it's the go-to place to go for on-the-go management of seller stores. When I joined the team it probably was about four engineers at the time. I didn't hit start a new project for the seller app, but I was pretty close from some people that did. And I think we progressed over the years. Currently, the team is about eight engineers. And my current role is more skewed toward platform work these days, work that involves upgrades, tech deck, performance, modernization, things of that nature. David DeRemer: And that's within the Flutter base for the Etsy seller app? Landon Robinson: Correct, correct. David DeRemer: So what's the history of the overall Etsy mobile app experience? I mean, obviously it's 2025 and mobile or iOS and Android came out a long time ago. Was the seller app always ... Did it start as a Flutter app or was there a native app before that? What's the history that got you to having a Flutter seller app? Landon Robinson: Yeah, so the story I've been told is that you can see that the seller app used to have a legacy app that had the traditional iOS and Android team. And I think they were merged in sort of with the buyer side the way I understand it. So, there were times where, I guess, resources or time constraints that they started looking at more toward going toward this cross-platform work. But they traditionally had your regular native iOS and Android building on the seller app. David DeRemer: Gotcha. So before the seller app and the consumer app were one code base then this was just kind of a variant of it? Landon Robinson: Yeah, exactly. For what I can tell is everybody shared the same code base with just different functionality, different bills for the seller side and the buyer side. David DeRemer: Cool. So got an opportunity to split it out and leverage some new technology with Flutter. That's awesome. Landon Robinson: Exactly. David DeRemer: Yeah. Well, and I'm curious with your own personal experience, the flow that you went through, but also maybe from other teammates, and even to some extent the perception of the current native team still working on the consumer app relative to the seller team working with Flutter. What are some of the comparisons that you think you've identified or been able to dig into relative to the other solutions, right? You got React Native, you got the whole Xamarin Maui set of things, you even just have web apps, progressive apps, things like that. What are some of the differences though when you talk about it internally that you think has made this successful as a Flutter app? Landon Robinson: I think some of the key differences is I think the infamous one code base for two platforms I think speaks a lot. I think that is very evident as we move more toward Flutter. Some of the things that stay the same are probably the architectural things of it, the modernization of it, the things of ... The abstraction layers, all those things are the same, it's just implemented different. But I would definitely think that the biggest difference is being able to have engineers write the code once and have it tested on two different platforms are, it's a really big deal. It's really big timeframe. David DeRemer: Yeah. How do you guys interface with the other mobile developers? Are you guys close to them? Does each team, sort of the Native people who are working on the consumer app and the Flutter team working on the seller app, do they collaborate a lot together? Landon Robinson: I would say early on we worked a little bit more closely with the Native guys, because we were trying to understand feature sets and how things were done. Do we want to reinvent the wheel or not? I think early on we worked pretty hand in hand with a lot of the Native people, or we sought out a lot of their advice or how to do things. But I think as things moved away from the early on, we started sort of being really separate, apart from like I mentioned earlier, the architectural pieces of it, the observability pieces of it. We still have those common threads, but early on we worked pretty heavily close to the Native team, but as we got further away from building out, getting our old cadence, we forced our way in. David DeRemer: And organizationally, is the work you guys are doing Flutter seen as a real advantage and benefit for the company overall, given your pace and velocity that you're able to move more quickly and with maybe even fewer or more efficient resources? Landon Robinson: Honestly, I think we see that a lot in our org, in the shallow org. From a company-wide perspective, honestly, I don't see a lot of that being talked about, but I definitely think it's being noticed, because like I mentioned earlier, our team is eight engineers that we're able to handle this core app. So I'm pretty sure they're being noticed- David DeRemer: Right on. Landon Robinson: ... how small but mighty team, but we don't make a whole lot of rah-rah about it, but we could. David DeRemer: Yeah. How big is the overall engineering team at Etsy? I mean, I know that's a lot. You got web, backend, mobile, what are we talking? Landon Robinson: Honestly, I do not know the numbers, I know it's a lot. I know it'd be Native teams, Android and iOS is probably five times as large as our team, and that's probably being generous, but there's a lot of engineers that support the Native apps. David DeRemer: Yeah, I mean, a lot going on with consumer apps, of course you're doing a tonne of stuff there to market to them and promote things to them and maybe the seller experience is a little bit in terms of the overall, there's some of that's probably just because of additional complexity, but obviously I think one of the key advantages of cross-platform is being able to do more with the resources you have. And I think that's just been really Flutter's unique niche and opportunities to create a great experience. And to do it by giving your engineers superpowers to do more. So, it seems like you guys are enjoying those benefits there. Landon Robinson: Totally, totally. David DeRemer: How did you start the team? I mean, I know that started before you got there, but when you think about the evolution and growth, how did Etsy go about establishing a team and some expertise in something brand new? And as the team has grown and you've been there and developed that team, and maybe you've had some people leave, some people join. What's generally been the strategy to bring people from Etsy or new hires into this seller app and the Flutter experience? Landon Robinson: Yeah, I think it starts with just really good engineers, people that know how to problem solve, people that can look at a bigger picture, if you will. And I think that's been really crucial in us being able to keep a really good team around. Most of the team that started this project, is still around, so we're very fortunate to have that on our side. But even when we have different people come in and build inside the app, it just starts with really good engineers, really good documentation to keep the momentum going that we started early on. David DeRemer: Do you mean documentation that was written about the old app, or you guys have been writing good documentation as you've been going? Landon Robinson: Yeah, I think we try to do a good job at trying to document how the app is set up, how to get going really easily locally, here's some best practises that we have that you should be considering. I think that goes a long way when we try to bring internal people into the team and build out in Flutter. David DeRemer: And I know Etsy obviously started as a website and probably I would imagine there's a strong web engineering culture. And you started at web too. People on your team, did they traditionally come from a web engineering background or a mobile background? Are there any patterns that tend to get good results with Flutter engineering? Landon Robinson: I think our team, most of our team come from native. Either Android or iOS or both. So that makes it really easy to build different building software in mobile versus web because especially some of the release cycles and how fast you can get updates out and things like that. So the majority of the team, they do come from Native. The other part of the team are probably pretty much full-stack engineers that's kind of know backend and web. But I think what we've seen generally speaking is that if you have some type of experience doing mobile development, you won't have a problem fitting in with Flutter. David DeRemer: And how do you guys work with the broader, so you obviously have the seller app, your Flutter engineering team. You got the consumer app, you also have the whole web and full stack and all the data and all that sort of thing. But you also have the product teams, the design teams, I would imagine all of these other teams that you guys are working with. How have you made some safe on-ramps for people to get involved with your code base and to work well with you guys? Given the fact that you guys can move pretty quickly and you can build something that works on all the platforms for all the sellers, how do you keep in sync with everything going on on the Native consumer apps and the web app? I assume there's a web-based seller experience too. How do you work with all of these different teams and people to stay in sync and make sure you're executing and shipping in parity? Landon Robinson: Yeah, I guess the big thing that I think we do really well is we do a lot of collaborative development where you have certain teams that build certain features while other teams build other features. But we all come together to build a great experience for our sellers. From a technical standpoint, the way we sort of do that for web engineers, is we create these co-labs that sort of walk people through how to build in Flutter, like some of the best practises that we've uncovered. We also do rotations from other team members where you maybe have a single engineer that's embedded in our team that builds their feature. They get to learn how we work and how the code base works and things like that. And a little sort early on. And that evolved into more of a programme we have now where we can have more than one engineer. It's sort of like the away team that comes in and is able to build out their feature from beginning to all the way to production. And I think that's sort of how we imagined it from early on where we could always have people outside of the core Etsy seller app team to come in and build different features. So you couple that with everybody has a shared mission of building great tools. Everybody's on the same page about what features we need to build. It makes it a lot easier to build the app. David DeRemer: Amazing. So this concept of the away team and those embedded engineers, was that a broader Etsy initiative or was that a seller app thing that you guys stood up first? Landon Robinson: It was something that the seller app stood up. Again, when I got at Etsy, I didn't hear a lot about things like that. Because traditionally speaking on the consumer side, if the away team wanted to build a feature in the Native app, then you had to either get a special team member to do it or you had to get on their roadmap. So, this paradigm is a little bit different from what they see on the buyer consumer side where we actually allow people to come in and build inside the code base. It's not as daunting because of Native code. I mentioned Flutter Dart are very similar to Reactive JavaScript, so it's a natural progression to kind of come in and build, and that's worked out really well for us. It allows us to maybe really feature rich for our sellers where it's just not one team that's providing all the functionality or Flutter team that's providing all the functionality. David DeRemer: Wow, I love that idea. The concept of the away team and bringing them into your team. And so they have the opportunity to learn from you guys and your expertise and be safe. Because you can guide them around things, but if you have good documentation onboarding, you can bring someone in and help them ... I mean, I assume you need some leadership commitment and buy in on that to make sure that people, the away team can come and be left alone for the time they need to ship feature. How did you navigate that internally? Landon Robinson: Yeah, I think it's a credit too, like I was saying, the collaborativeness of Etsy and us building, everybody's on the same mission of building great tools. So, no matter if it's us or the away team, the leadership from Prada to their engineering leadership, everybody's on the same page building. So, it wasn't a hard stretch to say, "Your team has come up with this great feature, you're going to build it on web and you're going to build it on mobile. So here's what it's going to take." A few weeks, a few sprints, and then with the Etsy seller app a few times with the web. So I think the fact that we think in that nature makes it a little easier for everybody to sort of be on board with how things are set up. David DeRemer: Do you ever get away teams from the native team, from the consumer side, or is it mostly web people coming in? Landon Robinson: It's mostly web people on the seller side of the marketplace, whether it's shipping features or message features, whatever the case may be. David DeRemer: That's cool. You've uncovered something here that I think is pretty interesting. In my experience, a lot of times Flutter teams that are successful get started from mobile engineering backgrounds. Whereas React Native, I think because of into React Native, there's a lot of React web teams that get into Flutter, or just web teams in general. And I think that there's a cognitive thing, there's an experience thing that you kind of have to struggle with next, which is you're actually building for mobile. And mobile has a whole different set of needs and parameters and expectations than web does. And so not only do you have to learn a new language or a new framework, but you also have to learn a new platform you're building for. But what I've seen is people who come from native backgrounds and they get into Flutter, they understand all the nuance of what it takes to build a good mobile app. But you guys have kind of done both. You said before that your team has largely formed out of native engineers who adapted to Flutter, and now you're bringing in web people, but because they have you as sort of experts to help coach and lead them, they're able to come in and pretty quickly be productive. I think that's just a really cool pattern to follow. Landon Robinson: Yeah, it been pretty awesome and rewarding as well. Because a lot of the things that we set up early on, the stretch of the layers, making things easy. I think early on we thought a lot about the developer experience a couple of years into. Because our end goal was to not only speed up development, but also to allow these other teams to come in and build. So that was always sort of top of mind went through and sort of set up the code base and shut up everything else. But yeah, it's pretty awesome to see this a feature rich application that can really help our sellers manage their shops on the go or whatever the case may be. David DeRemer: So when a web person from the website of the seller experience comes into your team, that away team comes in, what does their day-to-day look like for the first, let's say it's a month project or two months, what does that day-to-day look like? How do you get them ramped up to speed to do Flutter quickly? Do you have some training you give them? Do they get in there and read some docs or how does that work? Landon Robinson: Yeah, I think I mentioned earlier about Codelabs, where we have Codelabs to walk people through how to set up, I think the Codelab builds this feature in the app. And we have all these pretty good documentation to show you how to set up the API request, how do you set up state management. And sort of like the top level, high level things that have to be done pretty much for every feature. So that's sort of part of the onboarding. So there's this ramp up onboarding time, maybe one to two weeks in which they go through. And then somebody from our team is aware of what features being built. There's engineering discussions on best ways to do it. And then I think we've adjusted along the way as far as how involved we are with the project. So, as we understand more knowledge about what's being built, we can provide way more context, much more support for the away team, because we understand what they're building. So day to day, probably week by week we're being available for questions they may ask from a technical standpoint of why things aren't working the way they think it is? We're there reviewing PRs to make sure they're following best practises. And I think it's just an inner approach of building PRs, cushioning, testing and things like that. So, it's support that we do have to provide. So, we do provide daily support for the away teams, but I think mainly they're just cranking out their features, learning Flutter, having fun. David DeRemer: I love it. That's awesome. What is the general structure of your team? I know it's a small team, eight people, but you get these away teams to come in and I assume when they're on the team, they're welcomed and part of the seller team for a bit. Are you guys structured by feature? How do you structure, you're on the platform team kind of side, you said What's the general topography of the Etsy seller team? Landon Robinson: Yeah, it is pretty much sort of by feature. So there's a subset of feature that certain teams own, whether it's either web or mobile. And I think it really aligns well with our strategy, allowing just any team that's sort of built in. Because you might have a team that, I don't know, that's specific to inventory, they're going to build it on web and then they're going to build it in mobile. So this allows those teams to do. So we are sort of structured or organised by feature set. And then of course, you mentioned the platform team as well. David DeRemer: Nice. And as people come in, again, I'm really interested in this away team concept. I think it's really innovative and cool way to do it, especially in the context of Flutter where I think the onboarding, when you have expert support to help you, the onboarding, if you're, you started this with, if you're a competent strong engineer, you can get up to speed pretty quickly. But how do you as a team ensure quality and consistency across engineers to different people from different parts of the business, different feature verticals, different engineering backgrounds? How do you get them on board and make sure that the quality and consistency of the overall product it remains to your standards? Landon Robinson: Yeah, that's something that we have to be very intentional about. And I think we do that, one by a good architecture of the app, that's one, best practises laid out. And I think we have a really good cadence of branching and QA before releases. Like I mentioned earlier, we do support the teams pretty much on a day-to-day basis. So, nothing comes to a surprise when it's time to release these apps, because we've been pretty much involved with every step of the process. And I think that really helps us with the quality and consistency. Because we know exactly what's going into the code base, we know exactly what they should be doing, and then we have a really good strong support for QA. They do really good regression testing before our release cadence happen. Someone on the core seller app team is always understanding the code that goes in. So, there's a lot of checks and balances before that code actually gets into production. That makes us have a lot of confidence in the quality and consistency of what's being done in the app. David DeRemer: That's awesome. In long run, now that you've been at this for a while, I mean, when did the first Flutter release get released? Do you know off the top of your head when it was first one to production? Landon Robinson: I don't know. I do know that the strategy we wound up doing was we had the old app in the app store and we sort of released the new app, the Flutter app at the same time. So, it probably was a couple of years after we started that we felt like we had enough features to actually put an app out. But we basically ran both apps- David DeRemer: Oh, interesting. Gotcha. Landon Robinson: ... side by side at the same time. David DeRemer: Yeah, well, I love those strategies too. There's so many different ways. There's people who stop what they're doing and they kind of switch to all Flutter. There's the kind where you just do add to app and you start adding new features in Flutter and kind of Sam Moore from Betterman describes as the Strangler Fig pattern, which is like a vine that wraps around a tree, until eventually you just eat away the old app and you're just left with the Flutter app. It sounds like you guys did the strategy of maintaining two fully independent apps until you got them to parody and launched. That's a strong strategy too. I think that's really interesting. When you look back, now that you're on this, and when you operate in a regular basis and cadence, are there some tangible real benefits you guys point to in terms of that as a result of your technology solutions. And frankly, your technology team architecture and your engineering philosophies and approaches, what are the tangible benefits within Etsy in terms of how your team is operating? Landon Robinson: I think it goes back to what you were saying about we do more with less. I think that's something that we can really look at even from a standpoint of, "Let's look back and see what we've accomplished." And we've able to have a lot of features in a short amount of time. And I also think that because we do use Flutter, there's no compromise in design, because we paint every pixel on the screen. Traditionally speaking, in my experience there's always telling the developer, "Oh, I don't know. We need to do this. It's going to take too long." Or there's always some compromise in design. And I think with Flutter that technology \[inaudible 00:26:02\] there's not a lot of compromise on \[inaudible 00:26:04\]. I think that's really a nice benefit to look back on because we chose the technology. And then I think I mentioned this before, but I think the fact that we allow these away teams to come in and build, we can get ... It's just a feature read application for our sellers, which we can look back and say, we could probably do the same thing without Flutter, but the speed in which we did it, the ease of which we did it a Flutter allowed us to do it. David DeRemer: Well, I also think you were pointing out before that maybe this away team concept wouldn't work as well in Native code bases. Because first of all, you still have two, the away team would have to go to two places, iOS an Android. Also, as you mentioned, those languages are a little bit harder to understand from, Dart is similar to some web programming. And that has to impact culture too in terms of how the company culturally works together and sees each other. Because there's this interesting nuance that I think Flutter in particular has seemed to unlock for people. Which is sort of breaking down these associations with a particular language or framework or platform. Like you identify in your career as I'm an iOS engineer, or I'm a Swift engineer, or I'm a React engineer, all these things. And I think people still say, "Flutter, I'm a Flutter engineer." But when you really get into it, I think people feel like they're just product engineers. They can just work on a lot of different things. And have you started to see some of that cultural shift as you've got these away teams going and the team gets this sort of broader vision of what's possible? Landon Robinson: Yeah, absolutely. Because I've noticed that as we talk about features, like I said, very collaborative. But I think people start focusing on the product itself as opposed to being confined by what can we do on Android or what can we do on iOS? It's more of a really product driven mindset that I think I have seen happen over the years. I think teams sort of almost reorganise around product domains as opposed to platform domain. So, it's very specific to just the product that we're making. And I think it just helps us streamline interactions, just making it easier for people to collaborate and discuss new ideas. Because everybody pretty much has the same goal, building this really great tool for our sellers. And I think that culture has this sort of amplified just because we have this \[inaudible 00:28:32\] platforming initiative. David DeRemer: I like that. That's a really ... People tend to think about the buy one get one free thing. Well, I can just write the code once and it runs on iOS and Android. But you've kind hit on this idea of it affects your product decision making. Because when you do have to think about your different platforms and your different end device types that you're hitting, I do think you're right that there's this sort of, almost from the beginning, you're taking this filtering process of like, well, how's that going to work on Android? How's that going to work on iOS? It needs to behave differently. Or there's nuance between those things that are different when you're at that level. But you're right that when you get to this point where you're just kind of building it the same way, you're really just like, "Well, what's the best way to build it?" Not, How do I fit this into whatever kind of restrictions I'm faced to deal with?" And I think that's maybe a good way to think about the product power that these solutions really help to create by people. Thinking about design, app design in particular. One of the things that I think people get worried about sometimes is how do we kind of keep it unique to iOS and Android? Or how do we ensure that it doesn't just become some lowest common denominator ... Lowest common denominator app design? Turns out that's a hard word to say. And make sure that it feels flashy and special and cool and unique and kind of conforms to the unique nuances of the platforms that you're actually deploying. Have you guys developed any good strategies to make sure that you are still meeting the needs of your users on different platforms, even though you're just building at one time? Landon Robinson: I think we have, and I guess I can see that manifests itself. And I think I know from my past experience and something that I feel a lot done and answered I didn't see previously. It was like you prioritise the capability of the feature or the ease of use over simplicity. Because I think I've seen a lot of experiences where we will just dumb down solutions or product solutions because it's easier, it's more, it fits into the time constraints, things like that. But I think we've got a really good job at being able to look at the capabilities of the app, and easy to use rather than just trying to make things simple. David DeRemer: Yeah, no, that makes a lot of sense. Yeah. All right, so given we're here in today's world where everyone's talking about AI, one question I wanted to ask you about too is, AI and usage within Etsy and within your workflow. And in particular how you guys are using it or not using it? Are you using some AI in your development process so far, or where are you guys on that position? Landon Robinson: I don't think the team has fully embraced AI like we should. I think some of us are using some of the coding assistant tools like Copilot to help us do some of the mundane tasks. But I definitely think it's something that we can start considering. Because if AI can help us be forced multipliers of ourselves, why wouldn't we? Some things that come to mind for me are like translations or maybe unit tests or things like that could possibly be easy done with AI. But we haven't yet to sort of dive into it. But from Etsy as a whole, I think they're really embracing AI and LLMs some of the problem sets that the buyers' consumer size for LLMs and things like that, but Etsy definitely fully on board. They want us to use it, they want us to see how we can maximise it. So definitely excited going for how we can layer that into the already pretty nice and smooth process we have going. Right. David DeRemer: Yeah, no, I think it's tough. Because you got to figure out how to use these things in the context of still doing the job that you're asked. You still have deadlines and you don't want to waste precious time experimenting or playing with things. I think even that transition, that challenge, I don't know about you guys, but there's a lot of engineers by nature I think are ... Well, there's that famous, I forget who it was. Was it Bill Gates or somebody who's like, the best engineers are lazy engineers because they're the ones who want to solve a problem so they don't have to solve it again. Which I think is an interesting insight, and AI can help with that. But also it means that you don't really want to rock the boat if a process is working. But I think actually the switch to AI for engineers is basically the same exact thing. Going through that transition to using a tool like Flutter. If you're a native iOS or Android engineer, you have this thing, "Well, this is the best way to do it." And that other things feels like it might compromise it somehow. There's this promise of insane developer productivity, but there's this fear of, "Well, it's not going to be as good or something." And so on of the things I say to the team at VGB is, "Actually, the value proposition of AI is the same as Flutter, which is just making our engineers have even more superpowers than they had before." And really accelerating and accentuating the unique skills of the person who's architecting the system, which is really cool. So, it'd be interesting to see as it progresses. Landon Robinson: Yeah, totally. Because I think that's the way I sort of envisioned it myself going. Where we're going to get so good at developing to ... We'll have more time to focus on architecture, we'll have more time to focus on better experience. Because we can just crank them out so easily. So I'm excited to see what the next two to five years brings. David DeRemer: And in the context of that, adopting new technologies. I think Etsy was an early adopter of Flutter, and you guys and your team there have been pushing along in production, adding a lot of value. What's something you think, when you think back on your experience, what's something you think teams might get wrong when they try to adopt a new technology, when they're really trying to embrace something new within the organisation? Landon Robinson: Good question. I think for me, probably, and I'm a technologist, I go after the shining things a lot. But I think you have to be very cognizant of the business solution you're trying to solve. If it doesn't make sense for the business, even though it's the coolest thing out there, as hard as it may be, you can't go that, right? You have to find a businesses, and I've seen some companies that just go after shining things without any business and it never ends well. David DeRemer: That's insightful. And actually it tracks with things I've heard from other people who we've talked to, which is, it seems like when these things go well, it is really tightly rooted to a specific business outcome, that what you're doing isn't just for fun. It's not even to make your own life easier or something. It's to really impact the business in a strong way. And when you have strong leadership buy-in, the adoption of that new technology goes well, because aligned on what the outcome could possibly be. Now you're growing your career, you're a more senior person. And so along those lines, enablement and having vision. When you think about leadership and the leadership things maybe you observed on your team that has helped you to be successful, what do you think the most important thing a leader can do for their team is? Landon Robinson: I think from my perspective, a really strong leader is to sort of empower and support the team by just fostering this environment of clear communication, trust, recognition. I think that goes a long way with trying to sortify a team. Keeping the morale good is that if you empower team to do what's right or what they think is right and then you support them along the way, I think that's very important for a leader. David DeRemer: Awesome. And one final question then for you. If you could go back maybe to when you were just starting out at Etsy or maybe further back earlier in your career and give yourself one piece of advice, what do you think it would be? Landon Robinson: I think at this stage of my career, I touched on it a little bit, I would invest more time in understanding the business side of it. I think early on in my career I just wanted to build cool things and try new things. But I think if you couple that energy with understanding how it affects business as you go into engineering leadership, as you move up in your career, I think those things go really well, where you can be that distinguished engineer because you know so much about the business. So, if I had to go back and tell my future self, "Take time to understand business you're in." Find an industry that you really love and really passionate about and go for it that way, but the business with great software is really magical. David DeRemer: That is a brilliant insight, I love that. I think, yeah, that's amazing, because you're right. So many people get hung up on, "How does this stuff work? Or how do I learn all the nuances of this API or get super excited about a technology, but what is technology?" Ultimately, we're trying to get outcomes. We're trying to make something, whether it's something that people love or that makes an impact on the business we work for. And connecting the dots of those two things with the right focus, it's more likely to lead to something that you get to keep doing is the things. Landon Robinson: Right, exactly. David DeRemer: Yeah. Well, thank you so much for sharing all these insights today, I really appreciate your time. Does Etsy have a place where if people are ever interested in careers or jobs or obviously Etsy.com is where to go to find out more about Etsy? Anything you want to plug? Landon Robinson: No, I think go to Etsy.com. Go find some gifts for your loved ones, your friends, your family, and yeah. David DeRemer: Well, and if you're ever hiring for Flutter engineers, let us know. We'll help shout it out to the community and get some more people heading your way. So, appreciate what you guys are doing, both for the world at large with Etsy and also for the Flutter community, and I appreciate you coming on. Landon Robinson: Yeah, awesome. It was a great time, David. Thanks for having us, and yeah. David DeRemer: Thank you for joining us on Build to Succeed, a Very Good Ventures podcast. We hope you enjoy exploring the experiences and insights of leaders that have built successful digital products. Please take a moment to leave us a review. And don't forget to subscribe to get our latest episodes. Thanks again, and see you next time. ### Merging Culture and Code at Headspace Source: https://verygood.ventures/podcasts/pierre-benz-headspace-merging-culture-codebases-and-care-with-purpose/ In this episode, Pierre shares how his team at Headspace transitioned from fragmented native codebases to a unified Flutter architecture. He details the cultural, technical, and organizational hurdles of managing a high-stakes migration, balancing performance, feature parity, and developer sentiment. Pierre also reflects on parenting, leadership, and what it means to ship value fast when people’s mental well-being is on the line. {% audio_embed url="https://player.cohostpodcasting.com/5a4cd2ea-6920-4713-a737-4aade92057f7/74ab8a53-df77-4b67-abb2-0e01c00b3590" /%} ## Transcript **David:** Welcome, Pierre. Thank you so much for joining us on Build to Succeed. How you doing today?  **Pierre:** I'm doing well. I'm doing well. Thank you.  **David:** Awesome. Let's get it started. I'm doing? great. Thank you for  asking. can you, uh, let's start by maybe just giving an introduction. Introduce yourself and give us a little bit of your background story.  **Pierre:** Yeah. Um, so my name is, uh, Pierre. Benz, I live in cloudy Port Oregon in the Pacific Northwest of America. I. and I am an engineering manager at, uh, Headspace. Headspace is kind of like known for a meditation app, mental health app. and I've, I've been here as part of the engineering organization for the last, just over three years. I started my journey at Headspace as a iOS engineer. and, slowly but surely and \[00:01:00\] steadily kind of. Grew there and I'm now leading, I'm the manager in the team that is busy converting the app, exciting me to Flutter.  **David:** So tell us, how did you get into tech in the first place?  **Pierre:** you know, that's a good question. I think my love, in tech or technology probably came from an early age like I was. You know, one of those kids who grew up with video games,  and just loved playing them. Unfortunately to my, dad's dismay, you know, he always kind of saw them as like a absolutely waste of time. but got into just video games and wanting to make video games. And I remember like the first, one of the first books that I bought was How to Make a 3D Game Engine with C Plus out of all Languages. and I think like out of that came my love for tinkering with technology. I think like At a young age, maybe like, 13, 14 decided, hey, I didn't want to be, in my dad's windows computer. 'cause I kept on breaking and formatting things. got a Linux machine going up. I think I started with Linux from scratch and gen two. and I think my love for just tinkering and breaking things and messing around and \[00:02:00\] understanding how things work, uh, really started there. ended up going to university studying computer science. and even there, my, my path was about, you know, making games. I think like undergraduate over there was about game development . And in that process I learned, you know, the very early on, you know, when I'm surrounded by so many clever people that could like out program me, in their sleep, I learned that I had a, I guess like my focus at that point was about understanding why do people. Use technology, I think more so than the technical aspects of it. and that kind of took me in my career, over there as, doing postgraduate studies, really trying to like mesh, how does technology and psychology kind of intersect. and then I had a kid, and you know, at that point doing computer science at the University of Cape Town in South Africa. And then I had a kid and that kind of like. and with a whole bunch of other stuff with supervisors and stuff like that, ended up moving to, the US and jumped on the \[00:03:00\] thing that I could do at that point, which was make apps. at that point, like my, my, dissertation was about, I think we were doing Android apps and iOS apps, and the easiest thing for me was like, okay, I, this is pretty easy for me to do. I'll just make some iOS apps. Then slowly progressed in that career. went into startups, went into IOT devices, went to a design agency, A KQA, for a bit, and then more,agency life or startup life. and then I think like halfway through the pandemic, you know, I was at a startup that unfortunately had a close down, and. I found myself knocking on the door again. I, surprisingly enough, interviewed at Headspace before and didn't get in and then interviewed again. and I was, luckily, unfortunately,got in, and in that scenario I was like, you know, also one of those, it was one of those moments where I was, just I guess through my own, how do you say it? Like through my own, I think we all kind of went probably crazy in Covid, so probably in own, um,ability to kinda like find what do I want to do with my \[00:04:00\] skills? Headspace was a no-brainer for me. I'd used the app, I think I was a member since 2016. a Headspace user. it definitely helped me in patch in my life.  and I think the pull to try to use technology for more than just. Solving an itch. that was kind of the drive there. and then I joined Headspace and, have been on this kind of crazy wild ride, for the last three and a bit years. And as I mentioned, I've been there from just an individual contributor to now, needing a team that's busy transforming how we're viewing our apps and our technologies over there. Yeah.  **David:** gave us a nice roadmap of things to talk  **Pierre:** Yeah.  **David:** Um, before we get into the Headspace stuff though, and what you're doing and some of that journey, I wanna go back to one of those things about the video game stuff.  **Pierre:** Yeah,  **David:** me, asking about that, 'cause you have kids, how old are your kids?  **Pierre:** I have, yeah, I've got a 9-year-old and a 6-year-old.  Yeah,  **David:** given your exposure to video games and the influence that had, what's your strategy for technology and video games for your kids?  **Pierre:** it's a good \[00:05:00\] question. if my kids have or are gonna listen to this, like unfortunately they have a bounded, they have a very constrained window of. Access to, games and technologies. I definitely, like with my son in particular, and probably with my daughter's, the, the 6-year-old of my, my son is a 9-year-old. you know, I think over there I've learned, from myself that, as with most things, like it depends on the attitude in which we approach them, right? Like, so, so for example, with my son, I'm now in a place where I'm like, Hey, I have this. Library exposure that I've had to, here's a taste of this, like, do you like this? here's a different one. he right now is really enjoying, legend of Zelda. think he's just finishing Breath of the Wild and he's gone down his own rabbit hole of reading the game guides and reading the books. And I think for me, the thing that I'm trying to cultivate in him in particular is persistence . Like, you're gonna, you're gonna confront things in your life bosses scenarios where I. you're not gonna succeed the first time. And instead of just giving up, like, \[00:06:00\] can we cultivate persistence and grit, like in particular, you know, for him. so like he's on his own journey and you know, he's constantly asking me for, more games. I haven't quite gotten, like, there's a whole genre I'm discovering like, as a parent, you know, you kind of put your kid in, in the wild at school and they come up with their own social, game exposures and what's really big for them are these kind of like horror games where you're being chased by something, which you know, clearly is speaking of. Probably some other unconscious things that are going on in their lives as they're grappling with things that are scary and being chased and how to navigate that. So we haven't quite gotten there yet, with him. I think like maybe as a 9-year-old, I'm not quite gonna expose him to that. but I think some of the other games, like, like I mentioned, legend of Zel suffered. We, we is being heroic and trying to overcome the, insane odds with our character is brought in. I'm like, Hey, this is. Is really great. And on, on the flip side, you know, he has like dived into \[00:07:00\] getting those game guides and it's like, like giant books, this thick reading them cover to cover without me having to prompt him. And, you know, inadvertently he's, an amazing reader at the moment and, uh, really had little to do with me saying, Hey, you have to read these type of books. It was more like, find your interests, let's encourage that. Let's explore that. And he has himself discovered, the things that he enjoys. So  that's worked well with my daughter on the other end, like she is, you know, she has, she's growing up with an older brother who's playing all sorts of games and she's starting to read now, and so she's starting to navigate games and what it means and what are things and learning instructions. So she's in our own journey. She's not quite enjoying the fighting games as much. So she enjoys like the more, boating games. The, there's a whole spectrum of cooking games and stuff like that. and you know, but they actually both enjoy the untitled goose game, which, it's a lot of fun just being a goose and, you know, stealing keys from a farmer. \[00:08:00\] Uh, but  yeah.  **David:** fun.  **Pierre:** Yeah, Yeah, it's a lot of fun. **David:** I think the games, why I wanted to ask about that is, you know, a lot of us grew up playing games. I, not to date myself, but I grew up playing tethered to a TV with a  **Pierre:** Yeah.  **David:** system, you know, super Mario and all the things that came with that. and then going through that journey and, you know, wouldn't, when you get a, as a parent, there's this thing of like, okay, how much are we gonna introduce? More screens there to have tv, all these other things.  **Pierre:** Yeah.  **David:** I have just found it to be like, just such a massive improvement over tv. Like if they watch a TV show, they're just like zombied, But when they play games, they're solving puzzles. They're thinking about, Yeah. details. My daughter's the same way. She's got this book that's like two inches thick about this tears of the kingdom,  **Pierre:** Yeah.  **David:** thing like memorized  **Pierre:** Yeah.  **David:** Where I've been seeing it being really creative from the perspective. 'cause you, for you as a pathway into tinkering and getting to become software engineering, like how did these things work? they like to watch YouTube videos and they found this glitch. Turns out it does work. Like there's duplication glitch, that if you follow the sequence of  **Pierre:** Yeah.  **David:** you can somehow, like, you know, make five \[00:09:00\] more of the thing that you're holding just magically. It's literally a glitch. And so I think like, but they've discovered these things. They find these things and then they start thinking about like, why that might be happening, right? Like, oh, well  **Pierre:** Yeah. Yeah.  **David:** like. When whoever wrote the game, they didn't properly handle this scenario. And the counter that's keeping track of how many items you're holding is out of sync with like the actual items out of your inventory. **Pierre:** Yeah.  **David:** there's a, it's a legitimate bug, you know? So it's been really fun to find those things. And I just think collaboration wise, it's such an interesting thing like that these kids are, capable of exploring these worlds and all the creativity that comes with it and the hero's narratives and all those sorts of things. and I think still today they get scoffed at a little bit, but I think that. They're really important, can be really important parts of someone's journey to even what they do professionally one day. **Pierre:** Yeah, I mean, I think of like, just the act of self discovery that they're going on. You know, like, I think like before legend, I was out. My, my son was into like just Minecraft and. I don't think he really enjoyed the survival aspect of it. you know, of like, hey, like I've gotta, you know, at nighttime these things come out and \[00:10:00\] that's scary. Like, but he really just enjoyed the playground aspect of, Hey, I'm gonna put these things together and, see what happens. and you know, for us at least, we have some constraints. I think, like, you know, I'm probably on the one spectrum of like 20 to 30 minutes a day where he can play whatever he wants to play, you know? But the. Offscreen that's still continuing. Like we have a giant, he in particular has a giant Lego thing and he is consistently and constantly boating worlds. you know, we, we've got a, his room and his floor is just about like boating these platforms and stuff and role playing and doing all this type of stuff. And, I'm slowly but surely getting him into d and d 'cause that's one of the things that I grew up with, as well, which wasn't. I think he enjoys the physical aspects where d and d for us was more mental. but it is, it absolutely is a, I think anything that, that we can do to, I guess as parents and as caretakers, is to participate with them \[00:11:00\] and growing that imagination. And I think that's the fun thing, right? Like that's what games do is, Hey, what if I do this? What if I do that? and the games are a little bit more sophisticated to. Respond to your input. I think back in the day, ours were, quite, maybe not as sophisticated, but it was enough for us to want to be in that world. **Pierre:** you know, but sometimes there's a danger, right? 'cause like you have that aspect and I'm sure we all are aware of there is the act of escaping that is there in present, or that is there in these really highly livable and breathing worlds. You know, where there's, I guess every parent has that tension of like. I don't want you to miss out on life while you're enjoying this other life that you're creating. And they're, they both are lives to some degree. alive in our kids' lives, and so they're part of life. but it's that balance. I think that's the biggest thing I'm trying to kind of bring to my son is like, enjoy your games, balance it out with the other things in your  life. **David:** we're, uh, this is about technology and **Pierre:** yeah.  **David:** and \[00:12:00\] all those sorts of things. And so people listening to this point be like, all right guys, come on. You're talking about kids. Like what do we, but here's the thing. This is, you're not the first person that we've talked to that, Um, we get into something about kids, right? And what it's like to be a parent. And in a way it's a very specific type of managing an engineering process in a way, right? All of the things we just discussed, you could go back and re-edit and talk about what it's like to be, to develop yourself in a professional  **Pierre:** Yeah.  **David:** your passions, finding things that you wanna like, consume voraciously, right? Like you find that thing and you wanna read the book, you wanna get onto it. The balance between the two types of life. You know, there's your work life, there's the thing you're building, there's all these opportunities. And so I find that some, one thing interesting about. Parents is like not trying to compare managing people to managing a child. It's very different, obviously, or managing a product or doing the work we do professionally. But one thing about being a parent is like you observe these behaviors that are all inherent to all of us, but you observe it in a context of somebody who, for them it is totally new.  **Pierre:** Yeah.  **David:** new and it's eyeopening. We \[00:13:00\] learn new stuff all the time as adults, but it doesn't feel as like shockingly amazing as it does to a 9-year-old or a 6-year-old, right? So  **Pierre:** Yeah.  **David:** of being able to observe that learning happening in real time. And I think that kind of helps us to, mature a little bit and think about like, how do we bring this, some of these things like patience or how do we, um, figure out how to guide behaviors or certain things or. how to like, like really harness your passion areas and stuff. And I've always just found that really interesting. what sort of being a parent allows you to do that you can bring into your life and other spheres. so Yeah. **Pierre:** Yeah, I mean, I, you know, I think blessing and the curse of being a parent and a manager is like, oh, you just never switch off. you're constantly place of, maybe being overstimulated, maybe being overwhelmed, maybe having to kind of put a lot of things out. But I know for a fact that being a manager has made me a better parent, and being a parent has made me a better manager. in, you know, and definitely know in, earlier in my career when my kids were a lot \[00:14:00\] smaller, I particularly think of my son, you know, when he was a baby, I probably did not have the empathy or the, I don't know. Like I didn't have all the tools that I have now raising a 9-year-old, 6-year-old back then. so I'm very fortunate enough that for me. I know there's some other people, like I work with some people, like my manager right now, she doesn't have kids and she blows me away. Right? And so, but I know for me at least that in my journey, both of those have helped me become better at both aspects of my life. you know, and so that's, I'm fortunate for that. **David:** Yeah. And **Pierre:** and  **David:** a leader, as a manager, like that. empathy and building that, whether you get it from, being a parent or some other thing that you learn or mentorship or other **Pierre:** yeah. **David:** and I think you're right. It's like we're not saying to be an effective manager, you have to be a parent.  **Pierre:** No.  **David:** one's saying that,  **Pierre:** No,  **David:** but I  **Pierre:** no.  **David:** um, you know, paying attention to those things. And I like just looking for those signals of like, what are the things we can learn as leaders as we grow in our career? Where can we draw pockets of inspiration? It doesn't have to be a business book that you buy in the airport, you \[00:15:00\] know, it doesn't have to be, something you would do as a parent. It could just be like a random observation walking down the street of sea, somebody does an act of kindness and you think about how do I connect that back into a. Making a difference in the world and what I do on a regular basis. so it's using that. let's take some of these ideas from what it's, the things we've talked about,  **Pierre:** Yeah.  **David:** empathy and, finding passion and exploration and all those things. Let's fast forward it to what you're doing at Headspace. I know you guys have been through a bit of a product journey. I know there was an acquisition mixed in there and, maybe it takes us back to like, whatever you think the appropriate beginning of the story is that kind of gets you to where you are today and give us a narrative.  **Pierre:** Yeah. You know, so interestingly enough, I joined Headspace exactly when the merger of Headspace, the, iOS and Android app and Ginger. The, back then it was the Flutter app, which was mainly more about coaching and therapy. I joined right when they came together, and. I witnessed, you know, two companies try \[00:16:00\] to figure each other out. two companies having, you know, working through their silos and integrating over there. and I would say that it was definitely eye-opening it, you know, having those scenarios and also having the directive of like, Hey, we want to get under one house, so we need to be in a place where. We are not fragmented, or we don't have, we don't have to split our teams in multiple ways. But, you know, again, I was coming, when I joined, I was coming in as a iOS engineer. So my focus was mainly on iOS. somewhere in the journey that, you know, the product vision was, Hey, we don't wanna have separate apps. We don't have a, just a legacy Ginger app or Headspace Care. I think we rebranded it there and Headspace, we wanted to bring it into one place because. I don't think people wanna have different apps when they switch to things, they want to have a one place where they go and do things. and our journey began with, hey, bring in the core functionality of the Ginger app into the Headspace app.\[00:17:00\]  And historically at that point, that was a Flutter app, you know, through and through. And now we needed to, uh, to some degree like add. Add that functionality in there? I think internally we called it our Frankenstein moment or our Frankenstein things because we absolutely pulled it off. like my hat still goes off to all of the engineers and the people that were involved in that one because it was an insane feat, for us to do and for Headspace and for us, like we, we do have, sometimes when you're in the healthcare space, tight deadlines and they're oftentimes. January 1st, right? Because that's when healthcare plans kick off. And so we have these kind of like, at some scenarios, and particularly for this one was one, one, that's your deadline. make it happen. and so, you know, heroically, we did that and we launched it and it was a success. But now we had more problems, right? We had our legacy iOS and Android apps, and now you had this flutter. Shared library and this fluter component. \[00:18:00\] We had it in a scenario where it's like, this is a shared SDK that we are using in three different places. We're using it, the Headspace app, we're using it the Headspace Care app or the Legacy Ginger app. And we're using some of that code in like a web scheduler that's more on the front end side of our  clinical team and our coaching team. And making changes in one place took a long time. We had lots and lots of, just integration issues. And again, this was, our first, Hey, get it in there, you know? And I think for many of us it was, like see what happens. and then, I think come coming at the end of 2023, or the last quarter of 2023, it was myself and a group of other. amazing engineers. We were tasked with the act of like, okay, well investigate what it would mean for us to go full flutter. and we gave our recommendations. I think for many of us on the native side, egotistically, we were no, let's not do that. because we wanted to, you know, we had our own roadmaps, \[00:19:00\] right? Like we had our own things that we wanted to improve. We were just starting to work on, at least on the iOS side, we had maybe like 40% of stuff in Swift ui. There was a whole bunch of, a whole bunch of stuff that we wanted to do. And all of a sudden you get this side conversation, which, for us it's like, you know, I've been around long enough to know that hey, like don't wanna do React native. I've done that before. I don't want to, all of these other things come with other costs. we are just trying to kind of. We're just trying to land this plane and move in a way and scale this. and so I think, in retrospect, and that's not, I think our CCTO Vic, at that point, he made the right call, which was, you know, looking at the big picture of technology of what we're trying to do as a company, which is make mental healthcare affordable, scale. get onto multiple devices, et cetera. Like with all of those other things in place, the preferences for the engineering team as much as what they need to be considered, they need to \[00:20:00\] be considered within the largest scope of things. you know, and, trust me from the native side, we tried everything. We tried getting it on the, performance side, which, you know, there were some considerations that we had to do, but none of them were. Breaking, on the UI side, like, Hey, can it really match the native standpoint? Like, we try to kind of go from that route also really didn't move the needle as much.  and then, you know, then came the hard goal of, okay, we are making this decision. Here we go. whoever is on board,I remember. From our perspective is like, Hey, this is the decision that we're making. It makes business sense. we can't be in a place where, at that point Headspace was still like, Hey, we're we have a limited amount of engineers. I think everyone has limited amount of engineers, right? we're not just overflowing with engineers, we have limited capacity, uh, limited resources. but we wanna extract, we want to get value to people, right? And one of the biggest hurdles that we had was the ability for us to be feature complete across all \[00:21:00\] platforms. So say, you know, you play a meditation or you sign up and you buy a subscription on the web, you're buying a subscription for features that you want to be able to have on iOS and an Android. And I don't think we had that parody feature parody. And that is one of the things that obviously, like when she's centralized to a different platform where you can build and your features are over there, it's a lot easier for us to kind of expand and provide those features to all users. and yeah, that's kinda what started that journey for us was like, okay, we are gonna make that decision. And what has been part of my life now for the last year and. Year and four months, five months has been migrating the existing surfaces that we have, be it the players, explore profile, all those type of thing, onboarding, migrating that to flutter. and it has been a journey. I'll tell you that one, it hasn't always been straightforward, but I definitely can see like, you know, one of the benefits of this one is that for us at least, \[00:22:00\] it has solved that silo. You know, before we had, you know, Flutter engineers and native engineers and native Android engineers. And part of that getting together and building something really has, from my perspective, really helps solidify a cohesive team. the trick now is to make sure that team expands and that the knowledge that we're sharing and the comradery and community that we are growing over there and the culture and ethos that expands outside of the walls of our team that's busy. Migrating it because we haven't quite said, Hey, pause on everything, while we are migrating this. Like, it's one of the, one of the design decisions that we chose, was like, Hey, we are not just gonna go into a cave and convert your flutter app or your native apps to flutter and then come back. We, we decided not to do that. Maybe it would've been easier if we did do that. But you know, like we are also, I think everyone is in a scenario where like no one can just go away for six months and come back to predefined sets of designs or specs \[00:23:00\] because life's gonna change. And as we are now understanding and knowing like the last two years, Things have absolutely changed for I think most engineers. and even for us, like we started this journey and all of a sudden halfway through it was, Hey, by the way, don't just replace everything using the old APIs that you're doing. Let's make things server driven. Let's start to scale  right,   let's ramp up experimentation. It's start to scale things a bit more. And that was another little bit of a, beautiful curve ball that life kind of throws you. and. the nice thing is at least we had one platform where we can make those changes, because if we were staying under the old Android and iOS one, it would be, oh, we have more resources than iOS. Now they can pump it out, and then Android stays behind and then you can't really release at the same time.  so been a journey. it's really been a fun one. I think, you know, I constantly laugh at my team. Like if we finish and we are still working in sprints. the big thing is like, are we learning? Are we learning? are we \[00:24:00\] failing and are we learning? 'cause that's great. and then we can make improvements on it. If it was just like classical music playing in the background and everyone's kind of like zen out and there's no chaos, I would be kind of questioning what we're doing. but, you know, so a little bit of that chaos and order kind of interplay and, we are succeeding. I, I think is the. The  long tail answer. **David:** You know, you go to the gym and it does, it's not hard. You're not gonna build muscle,  **Pierre:**  **David:** this, I mean, there's so much in there and I like, that sort of phrase you said, like, the goal is to get value to people,  **Pierre:** Yeah.  **David:** in what you guys do, where like your value can like really help people. and so if you think about the, whether we can ship this in one month or three months. The number of people that you can really legitimately help two months might mean a very serious difference in terms of the impact you can have on people. And so I think that's what this is all about. I think, um, in engineering or product development, we can all get sort of very focused on, I. The camps we're in, the tools we use, the expertise we have \[00:25:00\] a lot of identity tied up in this stuff  **Pierre:** Yep.  **David:** and I think we lose fight sight of the fact that our objective here is to get value to people, like you said, and what are the things or choices we can make to streamline that as much as possible so that we don't spend our time figuring out how to define this in terms of how we did it in iOS or how we did it in Android. And are we getting parody between those two things that are on different build trains and different release cycles.  **Pierre:** Yeah.  **David:** behind on that side. Not over here, you know? Yeah. Part of this is just, and I think this is the natural progression of what engineers are trying to do. There's that whole thing like gimme a lazy engineer any day. Like, I forget who said it, like some, you know, bill Gates or somebody,  **Pierre:** Yeah. ' **David:** cause the lazy, like, you know, you wanna make it easy. that's sort of the point of engineering. So there's a lot in that. I wanted to ask you a question about that decision. So it sounds like your CTO like made the call  **Pierre:** Yeah.  **David:** are going to do this. That's a really tough thing to do, right? Because at some point someone has to decide and these types of situations, any transformation, you're never gonna get a hundred percent \[00:26:00\] agreement. Uh, and **Pierre:** Yeah.  **David:** in these types of cases. And it sounds like you guys had some people that were like trying to actively find ways to find ways to like, well, maybe we don't want to do this, or maybe there's another method or something like that. how did your company and your team, or your leadership like handle making that decision? And then I'm kind of curious like what happened immediately after that? Because I'm guessing there were some people that were like, Ugh, you know, throw the hand up. Like, how do you get people over the hump and get them on board?  **Pierre:** Yeah, I was probably in those conversations myself. like, like if I'm, absolutely honest, right? which is like, hey, I know some of us probably, you know, to your point earlier, I think like we as human splits, you know, I remember back in my Linux days it was like, who uses, what is it cute or GTK or it's V or emax? you know, it's, we split, we make this, these differences. And, I think the biggest call was. Hey, if as a company we're trying to drive costs down and we're trying to make mental \[00:27:00\] health accessible, right? And I think especially today where we're like, hey,the banner of like, let's get, let's people, let's get people to love their minds. we need to kind of. Put that up there with regards to how we kind of value or judge technologies and the tools that we're using. I think the, one of the biggest conversations that I had, I remember with my CTO in this scenario, was again, just reframing that on like, whatever we do, we're using tools to build stuff. and to your point, like the end goal is for people. And for Headspace, the culture at Headspace oftentimes is we often get readouts for how much the app has helped people. we often also get, uh, readouts for how much people are struggling with what we're building, and, hey, we wish we could kind of make these improvements, et cetera. and trust me, we have our own long list of things that we want to do to elevate the plan and fighting against the, you know, the. Constant tech debt, which is inevitable whenever you're voting something, you're gonna have \[00:28:00\] tech debt based on what you're voting. but it wasn't. I would be honest and say like, Hey, there definitely were people that weren't happy with that decision. you know, these were in, in many cases. These are, you know, I don't want to slight anyone, these were professionals, right? Like they've been doing and working with the technology and honing their skill for 10 plus years. And as soon as you come and throw in a, Hey, let's learn something new there, there always was the conversation like, Hey, what? I'm really good at this one thing I can operate at speeds that I can't do at this one. What happens with my leveling? What happens with my performance stuff? Like what happens with all of those conversations? am I still gonna be valued? Am I still gonna be, you know, in, in our economic climate with layoffs and stuff like that are there? What happens to me in that scenario? that definitely was. Conversations that we had to have with people, and honest conversations. And I think big picture, we were like, Hey, we're going on a \[00:29:00\] journey. I, I we want everyone to give this six months, try it out for six months, learn the thing, go ahead and learn it. And we try to, as best as what we can, we could carve out capacity for people to, not after hours, but during their working hours. Go and learn dart and flutter. Just go take the time. and that's difficult to do itself, right? Like some people come to a technology with like, like myself, like I grew up on, you know, turbo Pascal, cc plus. Like, those were my languages that I grew up with. So like jumping into, Dart for me was quick and easy. for other people, they're self taught, or iOS or Android could have been the only language that they were exposed to. So coming up with any kind of training program or transitional program is going to be hard to do, and hard to kind of see value immediately. you know, unfortunately we did lose some people. We did lose people that said, Hey, there were. They were rock stars at Headspace, and they decided, hey, They wanted \[00:30:00\] to stick with their career. They wanted to keep on pursuing this thing because they really enjoy it and they wanted to, keep being an expert over there. and that's unfortunate. Like I've lost really good coworkers for that. but I think, you know, this also kind of bumps up with that kind of growth mindset, fixed mindset kind of mentality of, Like languages and tools and stuff like that are going to change what's our North Star. And as a company, I think that's the thing that we kind of have to hold to is, what is the North Star? Is the North Star goal for users or is it technology? and hopefully people aren't at Headspace just because of the technology that they're using. Hopefully it is on the, I mean, what, the other day we had like. A number is like close to 50 million people that are active users, for the things that we build, that is impactful.  from my side like that. I still can't kind of  imagine that number. it's a lot of people that we have an impact on.\[00:31:00\]  What can we do ruthlessly to help improve that? It shouldn't be about tool preferences. It should be aggressively, how do we solve those problems?  **David:** Yeah,  **Pierre:**  **David:** fair, like for a person, individual choice in their career, they get  **Pierre:** Yeah.  **David:** choices and that's part of it. but it's like, are you pushing the vision? Are you pushing the mission and the objective of what you're trying to do? Or are you committed to the tool or the expertise, you know? And I  **Pierre:** Yeah.  **David:** We need to always be changing anyway, you know? there it is not, there's all of these things we're talking about today, whether it's our kids growing up and the transformation they're going through, or your transformation of getting these apps to become a hundred percent flutter. These elements, the human side of it, the cultural change, the complexity of managing all the conversations and everything. This to any transformation it's applying right now. Like what are we doing about AI tooling,  **Pierre:** Yeah.  **David:** it's the  **Pierre:** Yeah.  **David:** of thing. Like if somebody's a, well, I'm really an expert in Swift. So is chat, GPT and Gemini. You know, like, so it starts to change some of these things, uh, a \[00:32:00\] little bit. I also, I had a mentor one time that I liked, it was a tough thing to hear, right? But he was like, okay, look, the thing is, if you're gonna grow, if you're gonna grow fast, especially. Some people who are dear to you are not gonna make it. sometimes it's gonna be because they can't keep up or they, you know, they're not like, sometimes it's gonna be because their mission or their vision becomes misaligned with what you're trying to do. And I thought that was like really helpful guidance and I think it's helpful from a manager perspective is like, just because someone leaves it doesn't mean that it was a bad thing necessarily, right? It doesn't mean that, somebody like that person who left your team who was an amazing good friend, maybe I don't, you know, or an incredible engineer. They're gonna go off and do the thing that's right for them. But  **Pierre:** Yep.  **David:** as managers, we have to like be very clear, crystal clear about why we're making our choices and be comfortable with the fact that like not everyone's gonna be along for the ride. We can't convince  **Pierre:** Yeah.  **David:** to be as enthusiastic about the thing that we're trying to do as leaders and especially in these moments of like hard changes where we're  **Pierre:** Yeah.  **David:** really disrupting what's going on. So, one quick question about it. So like, we talked a lot about sort of the, \[00:33:00\] some of the cultural things and some of the difficulties, right? And you said like there was a lot of things you had to work through. Was there anything like through this process that when you started you were like, oh my gosh, this is gonna be so hard. That ended up not being that big of a deal. Like, were there any surprise, both either technology or culturally or process or product-wise? Like, was there something that was surprisingly like not a big deal that you thought was gonna be a big deal?  **Pierre:** Whew. That's a really good question. you know, I think as. There were many things that I think that we thought were easier to do, that, you know, came out and completely took us by surprise and completely decimated any timelines that I initially had, which is always great. I would say the thing that I'm really happy about, you know, like Headspace is known as being a very design. LED company. we are loved by the things that we make and the, the unique branding that we have. I think the thing that really surprised me is how \[00:34:00\] easily what we built translated into beautiful ui, that works. I think there was always this, whenever you're adopting multi-platform. technology. I mean, I remember back in the days of Swing and Java, right? where hey, build this thing and it, you have it on the screen and you can kind of tell that this is something weird and different. I think that was , the most surprising thing for me was, hey, build something that is beautiful and we can build something that is fast and beautiful and that works. that for me was the biggest surprise. And, we could then. Get our design team on board with that. I think that for me was the, I thought I would have to fight a bit more for that, and that was surprising. and that was nice. That was a nice, that was a nice thing on the technical side.  you know, the door swung a bit, the other way. And I think primarily just how we chose to do our migration, which was way more, it, you know, instead of, again, going into a closet and doing everything from file new, we decided to \[00:35:00\] approach things from an add to app model. And those are hard,  especially when you are migrating existing functionality. I think potentially if you're adding new functionality. You might get things a bit more for free. you know, when you're migrating existing technology, there are often things that you have overlooked or you didn't see, or, you know, some analytic event that you potentially forgot that brings everything down internally. those are things that are hard. Um,they're hard to see, they're hard to plan for. the complexity is oftentimes hidden. When you're boarding those things out. but for me, I'm, I've been pleasantly surprised by how quickly, two things, right? Like, number one, I remember myself, like I was pleasantly surprised how quickly I could contribute. I think it was something like, I had like two weeks of just flutter onboarding myself whilst doing day-to-day work. And all of a sudden I was giving a pr, a complex pr, Into our DART stuff. And for me it was like, oh, this is actually like, DART is programming on easy, \[00:36:00\] to some degree. Um,you know, so that for me was a real like, oh yeah, we are gonna be fine.  you know, with,what we're building, like what we're boarding is not quite that complex yet. so for me it, there definitely was the view of, okay. Bring the cortisone, bring all of that kind of like unsure, like unsureness down and like, we're gonna be fine. We just need to kind of think about this a bit more. **David:** Yeah, I mean I think that's critical to find those frameworks or models that help you like manage this complexity **Pierre:** Yeah. ' **David:** cause you can really quickly fall into chaos if you don't have like strategies for that. so it sounds like you guys found some space to be able to kind of, I. Do this the right way. I mean, definitely that a doc model is very difficult, right? But this is very, probably the most common way because you, like you said before, you can't stop, you can't stop shipping features. Like you don't really get the luxury. Sometimes you can, I think it depends on how complex your product is or like what your strategy is and some of those types of things. **Pierre:**  you know, I remember one of the reasons for that was when we started this journey, I remember \[00:37:00\] we, we reached out to a bunch of people and they all kind of mentioned, Hey, a migration takes two years. Like migrating your app takes from start to finish. Like, Hey, we've done this before. This is gonna take two, three years. and I don't think anyone right on the exec team or anyone is gonna be like, okay, let's go do this for two years. that becomes a really hard sell, I think particularly when the dynamics of marketplace and user demands are gonna change. so. we were kind of forced into that, that position of like, you have to do things incrementally, step by step. I think at that point, and this probably gives a bit more of like the underlying structure at that point, the way in which we were structured internally was we had our. Core team, which was a few of us, uh, which I started off in and then started leading, which was like the foundations team, which was creating the bones and the structures for, \[00:38:00\] you know, how do you launch features on the iOS side and on the Android side, and making it played nicely on the flutter side. And and then I think initially we had separate squads that would kind of do their domains of migrating things. That becomes hard to do because I'm sure every single squad has their own, or every single group has their own roadmap stuff to do, and no one really accounts for, oh, you gotta migrate this thing and that's gonna take you way more time than what you anticipated. so we quickly learned, I think it took us a while, but we quickly learned that, hey, that model doesn't quite scale work, or that's going to be the thing that pushes your. Migrating to flutter over X amount of years because there's the larger piece of getting people on the same,how do you say? Like, getting people up to speed with knowing flutter and being experts over there. and that can be slow depending on what that particular group is busy working on. Maybe they have a way more higher \[00:39:00\] priority item than converting something to flutter, right? Like they probably have someone like, Hey, this is a giant unlock for. This group of people, Flutter migration is probably gonna be lower on that, on list of priorities, because it's a one-to-one migration in that  sense. so we quickly learned, you know, coming out of this year, in fact in 2025 Jan, we made the switcheroo, which is like, we're not doing that model anymore. It takes too long. we are gonna kind of have a giant group, that is busy migrating. The app over, and obviously you have it split up in, into groups because you can't have everyone doing the same screen. but that has seemed to work a bit easier. So like that would be definitely be, in, in the vein of how do you centralize knowledge? How do you get people to communicate with each other? you know, especially in a remote environment that we are in, which is, we have people all, all around the world and all around the country, getting everyone into kind of like a, almost a quasi. war room, oh gosh, I hate that term, but like one, \[00:40:00\] one, just one room where like, Hey, you're solving this problem and there's a little bit of osmosis that happens by just being there, but you have this constant like, Communication and problem solving, just generative ideas that worked better for us. maybe that works for other people. I don't know. Maybe other groups are smaller that they can kind of do that. But for us, we found that kind of centralized group, and it was a,way larger group. It was about 27 people that came in. Hey, we're responsible for all the surfaces, while other groups are busy migrating things. or while other groups are busy building their own things. We've definitely seen a improvement in how do you get stuff done in that environment. The hard part is going to be, you know, the group that, that we have that's busy building it, they have to disperse at the end of this kind of quote unquote, migration. the hope is that they then become the leaders within their respective and within their respective squads that can help mentor, help guide some of the other engineers that might not \[00:41:00\] be that. expert in Flutter or any of the other technologies. So that's kind of where we're going with it right now. but so far, so good.  **David:** Awesome. I  **Pierre:** Yeah.  **David:** And so now as you think about where you are, you've been through this experience, you've had a, you know, a career even before this particular project of this  **Pierre:** Yeah.  **David:** and things that you've learned, curious about,takeaways, like how you've grown and maybe even the company, how it's improved, has going through this experience making That call. Working through it, learning these things like changing the team structure, figuring out how to be successful of this. What is your company or you, what have you learned that helps you to, that you think will be helpful in the future, the next time? whether you're building a product from scratch or you're going through another transformation, what have you distilled out of this process that you think is an indicator or, something managers can do to maximize the performance and the outcomes? overall, the success of the team. As you go through these complex technology changes. **Pierre:** \[00:42:00\] That is a hard question. That's a really good question. I'm probably gonna butcher the answer because again, I'm still in it, right. I'm not able to have the kind of like the big picture view of this one. So I'm gonna completely kind of answer this from, you know, someone who is. Deep in the migration and battling with, all the fun stuff that happens in that one. I think what this does do, or this kind of switch does do, at least for us at Headspace and, you know, potentially for any other, company that potentially is wanting to kind of move to photo or looking at like, Hey, what are,what are we trying to solve over here? I think it's that focus and clarity on what is the problem that you're trying to solve, and then looking at the tools that you have at your disposal for how you solve them. and again, this kind of goes back to, hey, like I think people enjoy being in camps. People have their preferences and you kind of have to acknowledge that. You really have to be focused. I think companies nowadays, specifically companies that need to \[00:43:00\] potentially, everyone's being a little bit leaner than what they were. Everyone's being a bit more focused compared to what they were. you have to make trade offs. and you know, I think choosing a technology like flutter. It's not a get out of jail card. Like there, there are pros and cons to every single technology that you use. for us, flutter is at this point a solve for a problem that we had, right? Like in our problem was, fragmented code bases. Things were not, in parity, keeping resources. At the same, you know, rolling out value to users at the same time was a hard thing for us to solve. Flutter solves that problem for us. Flutter also introduces a whole bunch of other, you know, issues as,we are expanding, we are also realizing that hey, like there are gonna be scenarios, for example, widgets and Apple watches and  the ecology of devices.  I enjoy using that term because like the ecology of devices is one of the things of like why I \[00:44:00\] believe. Why we chose Fluter in the first place was people have multiple devices, they use them interchangeably. let's choose something that can accomplish the goal of getting things out that can cover all of those devices, and you're gonna be, you're gonna have little stragglers on the sides there that aren't going to be covered with that. so the goal is about ruthless optimization. What can we do? what can we have that is. A source of truth, like, so we know, hey, when there's a bug, we know where to look. You know, that's partially also why we started looking at, you know, we did flutter on that side, which was, Hey, if we have a bug, we know where to look. It's not just, oh, is it? there, it's one place. And our, evolution with adopting SDUI, it's also trying to solve that problem, which is like, hey, like when someone sees something on the ui, like is it an F statement? And the. In the client code that's doing it. Is it an API thing? It's making these trade-offs about like, how do we identify where the issue is \[00:45:00\] fast? Because again, we wanna solve, for us it's a, you know, at Headspace it's a, hey, someone is potentially trying to regulate themselves, trying to help themselves. And for us it's like, oh, oh, we don't have another week to wait. We have to get a fix out to someone because. It could potentially help someone's lives or, save someone's life. so for us, it's really about like, you know, from the engineering team. And I can promise you, I always get this joke from my friends that, you know, as they know that I'm a Headspace, they always think like, Hey, you guys are just a bunch of hippies that meditate all day.  I think we probably should meditate more. you know, but it's about how do we, from the engineering team's, perspective. How can we solve problems quickly? and we found that the other route, you know, even in the, when we had the mix of, we have native iOS, we have native Android. We have flutter. A big push was trying to get out of that place was when there were bugs. It was, it took us \[00:46:00\] days to kind of figure out where does that bug live? Does it live on a platform service that's just out of communication? is it something on the native side? Is it something on the flutter side? Is it something that exists in the one app and not on the other app? Like those became so, just hard to solve that. I,we made the right choice. I'm like, okay, let's just like. Solve the problem over here, let's move to one place so that we can make life easier for us. And at Headspace, we are doing that not just on the apps, we're doing that on the infrastructure place where we're like trying to kind of solve those, just unifying a lot of our infrastructure. I, I would say, and again, the goal is to, so that we can do more with less, I think is the big, the big push. **David:** Makes sense. Get that value to, to people faster and you know, and Yeah. commit to that. I mean, Sam Moore from Betterment, he had a line of like, everybody wants to come to the party. Nobody wants to stay to clean up, you know? And I  think there's a lot of work, there's a lot of cleanup, there's a lot of things that are not the glamorous part of doing these things that you have to do to get  to that \[00:47:00\] sort of final state. To get value to people faster. So this was awesome. Thank you so much for doing this. I guess just to bring us home,and land us here, maybe, you know, do you have any final sort of recommendations like to your past self or maybe even to your future self in a time capsule based on this experience? maybe give us a little parting thought and then also just if you're hiring or where we can learn more about Headspace.  **Pierre:** Yeah, I think. So this is something for me personally that I'm kind of dealing with. It's funny, I actually, I had a therapist appointment before this one. surprisingly enough, embrace failure. that capital F letter, that f letter is the way that we learn. and that's sometimes a hard thing to do in technology when we're, you know, when we are trying to get unit tests out and we have a very binary thinking. Things are passing, things are failing. Embrace failure. From me on a manager perspective, it was to trust the engineers. to trust them, to trust the decisions that they make, to give them the space to make those decisions and to make the mistakes that they need to \[00:48:00\] make. otherwise, um, you know, it's, it's gonna come out anyways, Like make them, embrace. non-pointing fingers, like, don't blame people, but just embrace the fact that like, hey, we're all learning. every technology is imperfect. and so to know that technology is imperfect and we're imperfect, it means that there are, that it's going to be, uh, how do you say it, like failure on the other side. But then the goal is to like, not just keep it there, not just to avoid it, but to integrate that and to learn. Uh, I think that that for me is a, it's a hard lesson to learn. It's a hard lesson to, to accept. but the goal would be, you know, how do we get better? we don't get better from getting things right all the time. we don't get things right from making the right decisions. you know, and even in this scenario, as we move to Flutter and embrace all of this change, things are going to not always work out. And that's okay. Uh, because, we can always fix it. \[00:49:00\] we can always improve. there is always tomorrow in that sense, as, as unfortunate as some, uh, probably as some of my product, uh, managers don't like me saying that there's always tomorrow.  **David:** Yeah. Yeah. Tomorrow's expensive. I don't  **Pierre:** tomorrow is very expensive. you know, that's one. There's, there's so much, right? there's so much in the engineering, there's so many final takeaways. I would say like those are the ones that I'm learning in this journey at this point. **David:** That's great. Great. So, and if you're hiring or where people, where can people find Headspace and download the app and give it a try?  **Pierre:** we are aggressively hiring, photo engineers. There are still many open recs. reach out to me on LinkedIn if you need to reach out to, headspace.com. Uh, really simple, um, careers we have. Multiple openings over there. we are growing, we are aggressively trying to hire, we are, we're trying to make a really big impact. so, and that will probably continue as we grow and as we, we have more success.  Yeah.  **David:** Awesome. Well, thanks again. Thank you so much. You've learned a lot today and great story, \[00:50:00\] and,you're very insightful and have a lot of like, great lessons for leadership and management, so  **Pierre:** Thank you David. Thank you. Thank you so much. so, so, so happy to be here and just thank you for this opportunity. Thank you. ### Investing in Deep Tech and Innovation Source: https://verygood.ventures/podcasts/david-goldman-celesta-capital-investing-in-deep-tech-that-shapes-the-future/ In this episode, Celesta Capital Partner David Goldman shares his perspective on deep tech investing, the long game of venture capital, and how AI can drive meaningful impact in sectors like healthcare, housing, and education, when used with purpose and conviction. {% audio_embed url="https://player.cohostpodcasting.com/66809a83-23bb-4fbd-81c9-24bf97ea6410/13d34b33-7390-4af3-b6a7-4f493a898229" /%} ## Transcript **David DeRemer:** Hi David. Welcome to Build to Succeed.  **David Goldman:** Thanks for having me.  **David DeRemer:** Yeah, thanks so much for coming on. I'd like to start with a little icebreaker. So, uh,what's something wild you've seen in tech in the last seven days? Names like constrained to seven days. **David Goldman:** I'm gonna go with something in robotics. There's this company called mec. Who has sort of changed my conception of what I think robots are going to look like, I'd recommend going to check out. Just their whole concept is, there's no reason a robot should look like a human, just 'cause humans look like humans in the same way that planes don't look like birds. and their robot is designed to look \[00:01:00\] like a thing that is for the space it goes into. And it's very different from how a human looks. And I have been thinking about that a lot lately and just like, yeah, okay. A lot of money's going into humanoid robotics right now. Is that all silly? Should we even not be using the human form factor at all?  **David DeRemer:** Yeah, I love it. I was just talking to my wife. Um,we have a little pinhole leak that started and I was like, oh, at some point we'll just have like a little AI bot that will just climb through the walls and just like some sort of little spider that can fit through your power outlet and just climb into your wall and get there. And it doesn't have to look like a human at all. So it makes sense, but we, it's a good point right now because I feel like we're creating a lot of things in our own image and maybe there's a lot of innovation when we start thinking about, well, what if we eliminate the constraint of like, human form and human ways of thinking and approach things differently. So, well, a lot of deep stuff to get into on that one. Um, but, let's take a step back and say, uh, like, actually introduce yourself to us. Maybe tell us a little bit about yourself, um, what you do and your journey to today. **David Goldman:** Yeah, absolutely. So \[00:02:00\] I'm a partner at Celesta Capital. We're a early stage deep tech venture capital firm. We invested in VGV at Series A, which is, I think the only reason I'm invited on this podcast.  **David DeRemer:** Well, and you're really smart and have a lot of good ideas. **David Goldman:** so I started my career in investment banking, working on mostly semiconductor deals. At that time, the company that I was working for was AMD that was our client, and I was a young analyst at the bank and the head of strategy at AMD figured out he could just call me and ask me to do stuff for him. And I couldn't say no because they were a big client. And so I ended up doing a bunch of strategy work for him, helping them map out the startup landscape within the areas of semiconductors they were looking at. And that was really my first introduction into the industry, was working within the banking side on the sell side and became really fascinated by all of these startups. And then went to go work for a diversified investing group where we were an LP in \[00:03:00\] Celesta. And so I started looking at the deals that they were doing and some of the co-investment opportunities, which had a lot of crossover with where I had been looking when I was in investment banking. Went to work for a year at a startup in the building construction space. And then about four and a half years ago became full-time at Celesta. And so at Celesta we look for early stage. So, that's kind of like seed to series B. The letters aren't as important as it is the stage of the company and the capability for us to make an impact. And then we invest in deep tech companies, so companies that are built around a technology innovation. So that can be a systems level innovation, it can be an invention. In the case of VGV, it's really that differentiated knowledge of flutter. And then it can be monetized in a variety of ways. Sometimes that invention is monetized via hardware, via IP software or in the case of VGV in services. **David DeRemer:** So, yeah, deep tech, it's the sort of foundations of technology innovation, and you're trying to find those seeds that will like blossom into the next wave. is that the general idea? **David Goldman:** Yeah, it is. I mean, it \[00:04:00\] means different things to different people. for us it's generally, when there's a core technology innovation that the company has built around. So within the startup world, you can have lots of different types of innovation, right? You can have a business model innovation, you can have some sort of regulatory arbitrage that you've figured out where you're able to operate, but maybe someone else can't because of some way that you've designed your company. For us, that core kernel is the technology. If there isn't a differentiated piece of technology there, we're not interested, even if, you know, perhaps in other ways, that it could be very successful. 'cause that's just not how we invest and not how we look for companies.  **David DeRemer:** Hmm. Well, and VC is like a, such an important part of the technology ecosystem and I think a lot of people, there's a lot of mythology around vc. There's a lot sort of. Looking at it with,you know, rose colored glasses and excitement, if you're building a startup, you wanna find your VC partner, uh, someone who's been in VC and also come up through other ways, uh, other types of career like companies, right. And then landed in vc. What's something surprising about \[00:05:00\] working in technology VC that you think most people expect? **David Goldman:** I mean, I think technology VC is like a very general term that describes a bunch of different jobs, and it can be everything from people who sort of like meet with the founder one time and are like, yeah, it seems smart, has good chemistry, you know, here's 250 K and I've made 300 such investments, and I'm just gonna see like if one of them works out and then be like, I'm a genius because I, you know, wrote the first check into X, Y, Z thing. the way that we do it tends to be a lot more involved and a little bit more, driven around conviction in individual companies and individual markets. And so it seems like a very, Glamorous, like, you know, you're talking about big sums of money and these companies are all rocket ships and everyone's posting all their successes on LinkedIn, but I think you end up spending as much or more time with companies that are struggling than companies that are doing well. Probably much more time with companies that are struggling than doing well . And \[00:06:00\] so at times it can feel like in your day to day like. Wow, everything's kind of struggling because that's where my time is needed the most. Those are the people who have the most questions. They're going through some situation that's really thorny and they're trying to figure out, do I trust my lawyer on this one? Like, what are the other considerations, oh, this investor is saying this thing. Can I trust them? Do you know them? Do they have a good reputation? but the reality is that there's lots of good things that are happening, and that's mostly when they don't need to pick up the phone and call us, except to tell us. By the way, it's going really well.  **David DeRemer:** Hmm. Makes a lot of sense. I mean, VC or investments in general, right. There's definitely that portfolio theory where, there's huge numbers around startups, like how many don't make it. And so, you know, in a portfolio theory that does mean that there's a lot that are gonna be struggling. And you're right, I've actually never really thought about it that way. Like the other side of that coin is like. For every one company who's gonna IPO and make the huge return? There's a ton that don't have a great outcome, and that's really difficult and I would imagine that's a hard part of the job. so kind of tracks with sort of leadership in general too, is \[00:07:00\] right, like the hard things, the problems tend to boil up that you need to,address and deal with, not necessarily the celebrations and great things to high five about. So. **David Goldman:** Yeah, well, the really challenging thing is you build personal relationships with the leaders of these companies, and you've, in many cases, been with them for years and you've seen the ups and downs, and so if things don't work out a lot of the time, it's not anyone's fault. Like the market shifted. Something happened that was outside of their control. They had a big customer who walked away because of a thing that was outside of their control, whatever it is. And now you have this emotional moment of this thing that they've been working on for a long time that they've poured their heart and soul into is ending. And you're a part of that ending and you've built this personal relationship and Yeah. it's just tough. **David DeRemer:** Yeah. Well that's a, that's a good transition to something I wanna talk to you about today, which is, obviously right now the market's in a bizarre spot. Right? and,uh, you know, we've got tariffs, trade wars, uh, economy's been going down. and I thought maybe like, rather than talking about like what's going on today, but may, and maybe to try to make our conversation a \[00:08:00\] little bit more evergreen. What are some strategies,and sort of recommendations,we can follow as entrepreneurs, as leaders of companies about how to handle these types of crazy chaotic markets and moments. What do you recommend to some of the firms you're working with right now around how to endure these tough times? **David Goldman:** Yeah, I think the important thing is understanding the trade-offs, so. Immediately when a big event happens, you're gonna get questions from investors, from customers, partners, whoever. Like how does tariffs affect your company? Because that's, the thing that's happening right now. and people tend to have this like knee jerk of, I need to immediately have an answer. And that answer needs to immediately result in something like, oh, don't worry, we've moved manufacturing away from China to Vietnam, which may be the right answer. But a lot of the time people aren't thinking about it in a structured way about the various trade-offs and what you're giving up if you do that knee \[00:09:00\] jerk reaction. And so having the time to just take a beat, talk to the right people, think through it in a structured way, consider the trade-offs, and then have your answer, and you know, in the first 24 hours, it's appropriate to say, I think the impact is this. But I'm doing this, this, and this to find out more and I may adjust my view. So gimme 72 hours.  **David DeRemer:** It does often seem that the pace at which we need to react as companies or as a sort of global tech environment or whatever it might be, is, is really fast, but. I think you're right. Like these things do take time to unravel and even things, you know, even things take time to actually sort out what the impact even really is. But regardless of what the news reports or anything like that, how do you as an investor or as an investment team, navigate these environments? 'cause clearly, I would imagine it has an impact on how you're thinking about the types of things you'd wanna pursue or go after, or, how to, deploy your capital differently, across, through your portfolio.\[00:10:00\]  **David Goldman:** So we have like kind of an interesting style of investment. We venture capitalists relative to other types of investors. So if you work at a public market hedge fund or something, you can go in and outta positions really quickly. You can change your mind on things. Everything needs to be like really liquid and fast moving. We're making an investment at the early stage. We're signing up for potentially a decade plus investment in a company, and we want it to become massive over that timeframe and usually over that timeframe. Underlying things are going to change. In a decade, you're gonna have macro changes, you're gonna have different governments, you're gonna have different policies in place. And so unless the underlying technology creates some sort of favorable situation or differentiation for the company that's really persistent across those things, it's unlikely to be able to be successful over a time period like that. Or even over five years, you know, five year timeframe you're guaranteeing there's gonna be a presidential change. So. You can't just make a decision that's based \[00:11:00\] around something that changes, at the drop of a hat based on, one comment from someone who may not be in power in five years.  **David DeRemer:** Yeah. And actually when things are chaotic or being disrupted, I think sometimes that's the right time to be making bold investments or,big choices. a lot of the times we work with big companies and one thing that tends to happen in the marketplace is people become risk averse 'cause they're uncertain. So like making, maybe making tech investments or things start to slow down because people aren't sure there's risk. but it feels like maybe we should be doing more in these moments, right? Because these are opportunities where there's disruption where you could kind of do something interesting. And I guess if you're investing in early stage companies that have a long time horizon, those ideas, those things could be materializing any day regardless of what's happening in the macro situation. **David Goldman:** Yeah, I think you touched on a good point. So when things are going well, you tend to have a higher degree of perceived success from startups, and so more people who do not have strong conviction in an idea are likely to \[00:12:00\] pursue a startup because there is either a perceived higher likelihood of success or some social value attached to that that they think is good. And in some cases that's fine. There's more people pursuing ideas is good. There's a marketplace of ideas the best one should win out. But it can be challenging as a venture investor to differentiate between those who have real conviction and those who are perhaps doing it for other reasons. And in times of uncertainty or in downturns. The only people who are really trying to, you know, leave a cushy job in big tech and go do their own startup are because they really, truly are passionate about it, and they're going to have the grit and stick to it.  **David DeRemer:** Is that conviction kind of the key, one of the key elements of success when you evaluate opportunities? One thing just in general, I was curious about is when you look at. All the various companies out there that are, interested in raising money, they've got a cool idea. They're chasing whatever the gold rush of the day might be. How, like how do you evaluate, companies for \[00:13:00\] success, um, and identify what has the highest potential for good outcomes? **David Goldman:** Yeah, I mean. If there's a lot that goes into it, maybe not enough for a pithy answer right now. In general, I think on the founder's side, we're looking for people who have, like I said, that grit to stick by an idea because any startup is going to go through harmonics. It's never just up into the right, and so those who don't have the stomach for the downturns are unlikely to survive it. And if you want your startup to be successful over a five or 10 year period, it's going to have to survive downturns . On the technology side, it's kind of different. I mean, we invest in a lot of different categories, so the way that you look at a software company is gonna be different from a semiconductor company is gonna be different from a sensor , bio convergence company, et cetera. So I don't necessarily wanna go into the, each one of those.  **David DeRemer:** Yeah. Yeah. Well, I do wanna, um, tap into AI maybe, but, um,it's, it's funny, you're that grit and conviction, it's obvious when things are like a downturn or you gotta navigate a difficult thing. It's also not easy when things are going \[00:14:00\] great. Like, I think one thing that people underestimate is the complexity and difficulty of scaling too, right? Like how you, how teams change. How, more money, more problems, right? It's like the bigger you get, the more things you, expose yourself to and the more just challenges there are to face. And so I think having that grit and that conviction, like you have to be able to see it through the good times too, kind of in a way. **David Goldman:** Yeah, it, it's also, I mean, to use the term broadly, like a question of integrity. and I don't necessarily mean integrity in the sense of, are they gonna return someone's wallet they found on the ground, although that is also important. but. more do they have like a, an internalized sense of self. Where in the downturns, they're not going to become so despondent that they can't survive through it. But also in the upturns, when things are going well, they're able to see when people are maybe giving them too much shine and they need to be focusing on actually running the business instead of themselves. Or, you know, they're don't need to, not overhire because. Lots of interested, \[00:15:00\] lots of people are suddenly interested in their company and they're like, oh my God, that's so amazing. I wanna hire all of these people. They're like, no. Okay. I understand why I am hiring what I'm doing it for and just 'cause I have money right now, it doesn't mean I have to spend it because I have these specific goals and I want to go achieve them. And so it, it's a little bit about that, just like internalized sense of self  **David DeRemer:** Hmm.I like that. And you know, obviously, uh, one of the technologies, so yeah, I know you guys invest in a lot of different types of things and,you know, you don't have a strategy around a particular type of technology, but deep tech in general. The big one of course, uh, macro right now is AI machine learning in general gen ai. so just with respect to that, given that it's the big theme, what is your current view and thinking, about what's going on in AI these days? **David Goldman:** I mean. can I ask in what sense? Like with, from the investing lens, from my personal life, GDP impact.  **David DeRemer:** Let's do the personal life first. Like what's, when you look at all this stuff, there's a, um, you know, we, we live in this really interesting moment right now where you have sort of some economic turmoil, new administration, tariffs, all these things. You've got \[00:16:00\] AI coming and it's got this like insane promise and like massive productivity. And you can do things like that. You can do a week ago. The pace is really fast, but it's also terrifying for a lot of people. I think for me, what's been interesting about AI is. We've in sci-fi, we've been talking about AI for a long time, and so we have this like existential crisis kind of baked into AI that other types of technologies haven't really quite had, because we kind of have this like terminator like vision of the future that's been given to us by sci-fi. So you have this like insane technology progress. Coupled with kind of a tricky economy and then fears of like, oh my God, the AI's gonna come take all of our jobs. and I think that's the current state of fear, maybe not less the terminator fear and more the what does this do for the future of work and for the future of what we all do every day. and so I'm just curious, like as you kind of have, have been filtering in all the information and seeing things from an investor side, just being in tech, um, being in business in general, what's your current kind of. Vibe \[00:17:00\] on it all. **David Goldman:** I am broadly very optimistic about it. I think in general. There will be subsections. I mean, it's not gonna be evenly distributed, both in terms of the impact and the sort of distribution of the actual technology usage. Right? We have lots of technologies that exist today that are still not widely distributed for a variety of reasons, right ? We have the capability to have clean water everywhere in the world. We don't have clean water everywhere in the world true of a lot of other things as well, but where I'm very excited about it is in making. More productivity in industries that are important to us. So if you look at where a lot of the sort of like economic malaise in America comes from, it's things like healthcare, housing, education, and the unequal distribution of those within the populace. The inability or high cost, you know, inability to access or high cost of them relative to what \[00:18:00\] many people think is what they can afford. And so anything that we can do to improve productivity and make those areas more accessible to people is going to make people's lives a lot better. And so the extent to which you can make homes easier to build quicker, to build cheaper to build, you can make healthcare more broadly available at lower costs. Particularly like expert level healthcare available to people who can't go to, you know, Stanford or Harvard's, hospital system and, access the best doctor in the world. But you know, if anyone in America can get access to that, quality of that doctor through ai, that's a huge unlock in terms of outcomes. and then in education, like just the incredible things that one can learn using these in a self-directed way. It's not too difficult to see how they can be imbued into the educational system in a way that allows children to learn much more. Lots of challenges to that stuff. There's reasons that Ed Tech hasn't permeated more in the United States, um, but the promise is there, at least.\[00:19:00\]  **David DeRemer:** I love it. I love you. How you're kind of spinning it into like, or turning it in, in the focus onto the things where we can actually like real, really create value for a lot of people. Like one thing that's driving me nuts about how we talk about AI right now is, you know, I was just at Google Cloud next, right? And there's all these booths and they're all saying things like. build this thing in like an hour, or replace your engineering team with this like AI product. And I'm kind of like it, it kind of blows my mind 'cause I'm like, here you have a tech conference, or you're selling stuff to technical people and you're basically trying to sell them something that's gonna replace their job. Is the promise. And I'm like, this doesn't seem like good marketing to me. You know, it seems like you're missing something about how to really sell this stuff. But then, um, I dunno if you saw the most re this, uh, Harvard Business Review thing where they were talking about the changes in usage of gen ai and the top three were therapy, companionship, organizing my life and finding purpose. Which is really interesting. And of course it's been making the rounds 'cause people are like, oh, like we're already offloading our like deep philosophical needs to ai. But in the article they actually talk about how well \[00:20:00\] hold up, you know, people in Africa don't have access to therapists or psychiatrists. And so if you could kind of emulate if. That sort of experience with a gen AI product, maybe it's not as good as the real thing, but if it's better and actually now accessible to a lot more people. That's really interesting. Right, and so kind of focusing on how these things, like you said, build houses faster, like get the cost of that down so more people can have affordable housing or like do things with our food supply or whatever. That's the stuff that's really exciting and I feel like we're in this like marketing bubble right now where all you have all these tech companies racing to figure out how to do something interesting. And so they're finding like really like quick, like dopamine hit things that like really hit the, the balance sheet or the, or the p and l statement. and they're focusing on those things. I think we need better vision for ai, more aspirational use cases for humanity. **David Goldman:** Yeah, I mean. There's some stuff that's just nice and like it, I don't think it really adds anything to GDP, but I like it. Like there either there's an app that's really good at meeting notes, summarization, like I. used to have to take notes in meetings. Now I don't, my life is \[00:21:00\] better. I, I'm not paying for it. I don't know what that adds to the GDP, probably nothing. but then on the subject of things like therapy, like, it's not just A question of access in terms of can people in Africa get access to therapists? Can everyone in America afford to pay one to two $50 per session to talk to a therapist? But there's also this subsection of people, and I don't know what size of the population it is who don't feel comfortable divulging certain things to another person. and like the example I would give is, I don't think there's anyone in America who would be willing to have all of their Google searches. I. Publicly posted for everyone to see, right? But you're very comfortable putting weird stuff into the search bar on Google because you know it's just between you. And so to the extent you can have that interface that is private with an AI therapist where you know there's no human, and as long as you can trust the AI therapist company, which let's just assume you can. I don't know if you, obvi obviously always could, but lots of people will. then you've created a whole section of society who is now accessing therapy, \[00:22:00\] who possibly were the people who needed it the most. Like people who couldn't feel comfortable around another human can now get that help. **David DeRemer:** A hundred percent. Yeah. we talked to, uh, a doctor at Mount Sinai Hospital. He is the, like foremost prostate surgeon in the world. It's a thing that men don't really like to talk about, you know? and there's a lot of people who like die and have major health outcomes just because they're socially awkward about talking about it, but maybe you can talk to the bot. And turns out a lot of people like to do that and they're comfortable with that. So. Totally agree. Where's,as we are covering a lot of ground here. this is,changing so fast and even within what's happening here, I mean, there's such a huge spectrum of where dollars could be funded or where you could invest in AI broadly. It's a very big space. I've two maybe kind of two lenses on that. Like where is in, in your view of like observing what's going on in investment in this area right now? Like where are the most dollars going right now? More broadly, not just from Celesta, but like the community and then. Where do you think there's like sort of latent or emerging opportunities that are maybe being \[00:23:00\] overlooked by the broad investment trends? **David Goldman:** I think most of the dollars today are going into two things, and they're interrelated, which is basically creating, for lack of a better term, supply of ai. So you have lots of dollars going into foundation model companies. So this includes the open AI Andros of the world, but also the new ones like, you know. There's been $4 billion raised from two new foundation model companies. I mean, at least I think that's what thinking machines is going to do. I don't totally know their business model, but in, you know, 18 months ago, I would've told you that those were more or less set that I was gonna be open AI and philanthropic and X and mytral and there weren't gonna be too many more who are gonna raise a lot of money. And now we have two new ones. So that's been one big bucket of money is at the foundation model layer. And then the second one is in. Hardware and infrastructure that enables that. This is where we tend to do a lot of our investment and even before ai, where we were doing investment. So this includes on the public market side, stuff like \[00:24:00\] Nvidia and AMD, but then also on the private market side, investments in new AI accelerators. So, semiconductor processors that are specifically tuned for AI workloads. things like networking, which allows you to move data around, better, which is particularly important for training these really large language models. And then software tooling on the infrastructure side. So stuff that helps you use your infrastructure more efficiently. Stuff that helps you train your models better, stuff that helps you access them, et cetera, et cetera. So all that stuff's in a broad sense, like creating this supply of ai. and a lot of the growth so far has been linked to the foundation models and to companies who are building on top of them. so then you have this next layer of. maybe the top layer up, which is like these vertical apps. So that's everything from like Cursor or Windsurf in the coating space, who are building on top of other people's foundational models to some vertical apps that are doing their own models, either because they don't need to have a super general \[00:25:00\] thing. They need something very specific, like their type of text. The type of input for their model is something other than text. It's some different type of data that isn't contained within chat. GPT. or because they think they have a really good knowledge graph and so they're just gonna take an open source model and build on top of that.  **David DeRemer:** Nice. So supply of AI and then sort of the, the interface interaction layer on top of that building on those foundational models.  **David Goldman:** Yeah. and it's really in that last piece where I think a lot of the growth is going to come from in the next two years. and so let's call it basically like supply and big demand drivers, right? Like, There's lots and lots of really interesting things being done in different verticals, and not all of them will be successful, but some of them will. And so in areas, I mean like some of the things we talked about, healthcare, construction, coding is like the most obvious one because it's an easy, easily verifiable domain. so it's easier for the AI to see if its answer is correct or \[00:26:00\] not because it's like, does it compile Yes no. which is very different from construction of. Did I actually speed this project up by two days? Like kind of difficult to prove the counterfactual.  **David DeRemer:** Yeah, it's exciting to hear that there's more investment and opportunity. 'cause walking around, like take Google Cloud, NEX is an example. You know, there's thousands of vendors there, 33,000 people, and all these people kind of pitching their, their own unique story and the thing that they do. But then you look down on the floor and there's a giant Google logo, and you realize like, we are all just guests in Google's house, right? And so is all of this stuff, just really for the Googles and the Microsofts open ai, the, and Nvidia of the world, right? All of. What we're really doing is creating value for them, by being this like service layer on top. But I wonder, you know what, I've talked to a bunch of, uh, machine learning kind of agencies, development companies and stuff, and, uh, one interesting nuance that I found there was like, there's some like degree of like scoffing, around people who've been doing ML for 15 years when it comes to gen ai. 'cause all of a sudden gen AI put AI on the map for so many people and they're \[00:27:00\] like, Ugh. You know, like there's so many things people are asking us to use, LLMs and Gen AI tooling for that would be better just to do it the old traditional ML approach. And so I wonder if, there's like a boomerang that will happen where, LLMs and gen AI will kind of be like the safe onboarding pathway for a lot of companies to get into. Machine learning and AI solutions, and then they realize like, oh, actually we have all this data. Maybe we should train and build our own ML model, build our own things. And whether or not that becomes the flywheel that kind of gets new types of models and foundation models and things like that sort of started. so it's not just everything built on Gemini or, uh, chat GBT. I think there's an interesting flywheel there potentially. **David Goldman:** Yeah, I think that's broadly correct and it's, I think also a good thing. So the extent to which you have people awakened to the idea that value can be created by. Investing in r and d around better using their data, better understanding AI tools. They may start with saying, okay, \[00:28:00\] use chat GPT, because that's the only one they've heard of. But ultimately, people care about outcomes, right? They want a six inch hole, not a drill bit. And so the people who are building these things, if they can deliver those outcomes in a different way, will get the great credit that they need. And this is the. Investment and executive attention needed for those people to be able to have, prioritization within their organizations given to them. And whether that's an outside group getting money or it's an inside group, you know, getting resources and getting people and being able to present to the board meeting, whatever it is, it, it gives the opportunity for this value to be created. And we see this in our portfolio with some of the semiconductor companies, for example, where. If you were doing something that, let's say for example, on the networking side, so you're creating a hardware product to help move data more efficiently within hardware systems. Even before ai, people \[00:29:00\] needed to do that, and there were lots of reasons that you needed to accelerate it, and there was a whole roadmap of them doing it. But now that it's so essential for ai, there's a lot more focus. There's a lot more dollars, there's a lot more buyer interest in these things. And so even though you're maybe making a similar technology as to what you would've made before, and that's not universally true in networking, but just as a hypothetical, because you now can say that it's ai, you can access a bigger, better pool of capital from investors. You can get better employees to work on it because it's more interesting when they go tell people at cocktail parties what they work on. And then when you're selling into end customers, it's a higher priority for that CEO. Because he's talking to his networking team and saying, I need this stuff for AI and make it happen. And now they have the budget and the focus to go out and work with vendors  **David DeRemer:** Yeah, I was drawing a parallel to the AI stuff happening in the market right now to what you were saying about like reactions to tariffs and market conditions, right? Where sometimes it's like you need that urgent reaction. Like, what are we doing about this? I feel like over the \[00:30:00\] last 12 to 24 months, that's kind of been happening in, in large enterprise and companies too. Like, oh, we gotta do something about ai. same, kind of similar, would you recommend sort of that same approach still of like, Hey, take a minute, think about it. you know, figure out where you want to do, like, how, how does someone navigate that when there's so much change and the pace is so fast and you know, you need to react to it? what kind of advice do you have for leaders or product creators to like smartly engage the wave that's happening? **David Goldman:** I think having a really deep understanding of what makes your company valuable to your customers. Then seeing where AI or whatever the thing is, interacts with that. So, it's a little bit different in the two scenarios. 'cause I think in the case of tariffs, it's like, do we need to make changes to, because something is affecting our cost structure And so maybe we need to change pricing. Maybe we need to change supply chain. But in a lot of cases, people don't even fully understand their supply chain, so. You probably need to do some \[00:31:00\] work to understand all of the inputs and trade offs before you just do a knee jerk reaction. Whereas in the case of ai, it's really more about thinking about what is the possible upside? What is the threat if someone else recognizes that upside before us? And then where can we create the most value for our customers using AI or creating our own AI or whatever the case may be in a way that makes sense with our business. So, you know, it's a little bit like, you know, when Snapchat introduced stories and then Facebook introduced stories and then you saw every other app had stories on it, like LinkedIn had stories and you're like, well, why does LinkedIn have stories that has nothing to do with like, what I. wanna see outta my professional network? People were just fast following too fast. and so, you know, think for a second about why people are coming to you, what it is they want from you, and whether or not the decision you're making actually matches that, customer desire.  **David DeRemer:** Makes sense. Makes a lot of sense. Um, one thing I wanted to ask you about, switching gears a little bit around the team, you \[00:32:00\] mentioned, um, conviction, right? Being important to the teams and, in general, it's like you guys probably come across, I would imagine being in nbc, like a lot of companies selling a lot of things right? and sort of like really engaging the, the fleeting trends. And you have to somehow distinguish like what is the real genuine. Like sort of real convict like founders of conviction, defensible technology, et cetera. but one of the things that I, I noticed in your profile, you have this thing about like, advising founders to focus on like 10 x capabilities and avoid getting bogged down. And I feel like in a world where things are changing a lot and you have these new stimulus, you're like, oh my god, these emergencies or these new emerging things. Can you like, expand on that sentiment a little bit more in terms of like what you mean by that? Sort of the,the te like focusing intensely on the 10 X capabilities and what kind of, um,how a leader can operationalize that in their day-to-day when they're just inundated with chaos and change. **David Goldman:** Yeah, I mean, I think it's about recognizing where you have the greatest impact. No, CEO has more than \[00:33:00\] 24 hours in a day. And so thinking about what you bring to the business that no one else can do. So there's some things that a founder just has to do, or a CEO just has to do. The decisions that must be made by the CEO, no one else could possibly make them. But lots of decisions that get made by A CEO don't fit into that category. And so in many cases, those things can and should be delegated either to someone within the organization or someone outside the organization. So like an example I. would give is like, accounting decisions. For the most part, those things can be delegated. you wanna make sure that you're broadly within the rules of what is acceptable, and you're not in violation of anything with the law or with the IRS, but you know how you should be categorizing some expense between different GL accounts. That's not a good use of any CEO's time, but some CEOs do spend time thinking about that sort of thing because they just like to be in the details. and so instead, if you're a highly technical founder who's a incredible inventor, you should be spending that time working with your technical team \[00:34:00\] on the product roadmap or on driving forward innovation, not on, you know, is this expense in category A or category B? **David DeRemer:** and what about like, leading teams? You, you recently wrote an article, about, uh, the, uh, sort of the importance of multidisciplinary teams, right? Getting, engineers and designers together in the same rooms. And sort of that idea of like sleeping on the factory floor, getting your, in the case of like a Tesla or something, getting your car designers and your engineers together to work on it. can you expand on that idea too, and maybe tie it to that theme of like, getting the right people in the right place and getting the right types of collaboration? Because the opposite of the thing, like you're saying with the CEO who likes to get in the, in the details is that that could actually maybe get in the way of collaboration. or if you're not doing it for the purpose of collaboration, maybe it's not the most productive thing to do, but maybe dig into that a little bit. I'm curious, your point of view on, on these multidisciplinary teams and the importance of that for innovation. **David Goldman:** Yeah, I mean, I think one of the biggest advantages startups have is being small. and what I mean by that \[00:35:00\] is if you have a large enough organization, you almost by definition have to break it up into some form of grouping. And oftentimes that's like some form of functional grouping. So you might say, okay, all of the designers work in this office, and our head designer lives in LA and so our design office is in LA and we really like this head designer, and he wasn't willing to move. So all our designers now work in LA and we have a thousand of them. So they have their own office. You know, our head of manufacturing, our factories are in Mexico, so he lives in Texas, and so all our manufacturing people are now gonna be in Texas, and we have a thousand of them . And so inevitably, all of those people may have cohesion with each other, but they're not going to have cohesion between the two groups. And so you're going to have things that are designed, which cannot be AC that had you had feedback from manufacturing into some of those decisions, you would've been able to produce it more easily at lower cost. At greater scale. All of these things, startups, by virtue of being small, have the \[00:36:00\] capability, although they don't always take advantage of it, to be tightly cohesive and to be, if not physically in the room together. 'cause you know, these days, remote work is very often, very often the case to be in close contact with each other, you know, virtually. And so because there's a limited number of people, you can just have that cross pollination that allows those decisions to freely flow from just normal collaboration. So instead of saying, Hey, we're gonna write up a, requirements document and then send it over, you guys are gonna mark it up and then send it over. You're gonna be like, Hey, I'm thinking about putting this screw here. Is that gonna be okay? And you'll say, no, actually, our screw placing machine can only go six inches to the right and you've just placed it seven inches like I need. You need to not do that.  **David DeRemer:** Amazing. And so, it's kind of interesting because a lot of things we hear about ai, like in defense of like humans in the future relative to AI, is like we, we still have this communication, like humans aren't good at telling each other what they need. And so \[00:37:00\] if we can't do that, how are we gonna be able to like, direct the ais? but I feel like there's kind ofa, an interesting maybe opportunity here for technology, right? To bring people together and increase that collaboration and if, the ability to share information and for us to kind of like. Refine it together, gets us closer. I wonder if we can get to that. There's also this book, team Topologies. You ever heard of this? I think I mentioned it before on,this podcast, but it basically is the idea that, the shape of your, uh, product you create, mimics the, um, structure of the team. so like if you have teams that are feature-based, like over time your product will diverge in terms of how those features  **David Goldman:** Mm-hmm. **David DeRemer:** If you do it stack based, like backend, front end, whatever, like you know, over time your systems will diverge and get harder to integrate. so it's just the top apologies of things I think are very interesting as you think about this. but yeah, coming from where I come from, like user-centered design, human-centered design, collaboration, getting design, engineering, everything, product people all together, yeah, I think it, it makes a lot of sense having the good, good, at least triads of people who understand things from different perspectives. So you get aesthetics. Sort of the economics and the ability to make the thing \[00:38:00\] all sorted out at the same time. **David Goldman:** Yeah, I mean one of the areas where I don't think AI is good yet, which I would love to see it improve at, is in being able to make these sort of like cross-functional connections. I. So in a theoretical future, you could maybe have the two big organizations in different places, but if they're doing a good job documenting their thought processes, so like maybe they're having their meetings transcripted and put into some knowledge base, then there is some central actor who is capable of looking at all of that stuff and actually pointing out to them that if they had been in each other's meetings, they would've realized that there was some conflict and they're actually on divergent paths and they don't realize it. And so it can like raise a flag and be like, oh, actually there's an issue here. Now we're not at that point yet. At least not to my knowledge.  Um, but that's like a future point for ai That  **David DeRemer:** Yeah, the pace is so crazy that it feels like, uh, one of the things I've just been saying is like, once you really start exploring these things and using them and you see how \[00:39:00\] fast things are going, it's like something that doesn't work. It's like just a matter of like how long until you'll get there. but, let's look at time for a minute, right, because we have, obviously we're in an interesting moment, but it's, it's always like this every year or, you know, every, whatever finite timeframe you look at, there's always a lot of change happening. The world is never static. Our lives are never static. It's always perpetual change. So if you were to kind of like,think back on, on your career and maybe the investments the firm has made, or like others that you've seen, just like companies be really successful. Given the,the nature of this is called like, build to succeed with the idea of like, let's build stuff for the purposes of like, getting better and better and finding ways to like make the things that we do like more meaningful and more impactful. What are your themes or sort of trends that you feel like you've observed across the most successful companies, startup teams that are, you know, not necessarily causation maybe, but at least correlation or at least, something that you feel like you could bet on as a probability of success. **David Goldman:** So I think the first one is like continued \[00:40:00\] organizational cohesion. And what I mean by that is there is. A clear understanding that is throughout the organization of what you are building towards, and people are able to get behind that and stay behind that. And so where you can see companies diverge is when you have fracturing of organizations, you have politics. You have people who are working towards things that are not necessarily company goal related, but individual goal related. And so that a lot of that falls on the CEO to create the right structure, to hire the right people and to make the right culture so that everyone is working with cohesion and there's not one culture that works. Uh, and that's why I didn't say, you know, hey, everyone has to be like this one company, but there does need to be one culture pretty much. That everyone understands and is working towards, and one broad set of goals and mission for the company that everyone agrees on and is \[00:41:00\] able to line up behind. So that's one. I mean this one's not super helpful because it just is what it is, but like getting the technology wave right. so people who use famous examples, like there was music streaming before Spotify. It just wasn't the right timing. Being early is the same thing as being wrong, and so having the right technology at the right time for the market, and sometimes that means being slightly too early. so. In our case, many technologies that we invest in take one or two years of intensive r and d and development before you're launching. And so if you start working on it, if you started working on a, hardware, AI product, the day that, you know, you heard about chat GBT and it had crossed, you know, a hundred million users or something, well, it's gonna be two years before you're actually in market with it. So you really need to have the version that works in two years, not the version that works today. And similarly, if you had been working on it from two years ago because you understood how big this would be, and then you had it in \[00:42:00\] market the day that chat, GPT crossed a hundred million users, you'd be very well positioned. So having the right timing and the right technology for the time.  **David DeRemer:** Hmm. So a little bit is, uh, the people and the mission, and the culture you create. Some of it's just timing and just somehow things work and sometimes they don't, you know? So,  **David Goldman:** And some of that timing is, uh, vision. You know, it's understanding the technology trends. It's being able to skate where the puck is going, a little bit of a it.  **David DeRemer:** Makes sense. Makes sense. Well wrap us up. So I wanted to say, uh, crystal ball time, right? Um, there's a lot of things going on. so I guess maybe to constrain the question, like let's maybe focus on AI and what it's doing. what kinds of like unexpected or transformative businesses or just broader social changes do you think might emerge? big picture, like long run, you know, let's do a 10 year kind of thing. What's out there that you, you think might be unexpected for people in terms of where this all goes? **David Goldman:** I think maybe, I don't \[00:43:00\] know what the right timeframe to say this is, but like, if you are, if you're having a child now when your child is an adult, they will potentially talk to AI as much as people. So they're gonna have all forms of personal I, whether that's therapist, it's life coach. It's dietician, it's just someone you know you're wanna be able to play video games with. And instead of playing against the computer or playing against someone online, you wanna play against someone who's like perfectly matched to your capability. And then you also wanna talk smack to them at the same time. that like the summation of all of those things and whether they're embodied in one AI or multiple ais, is going to equal more than they talk to, human beings in an average day  day. **David DeRemer:** Hmm. Yeah, I think you could be right. And, um, I, I think we don't really even fully grasp, that's my takeaway is like, I think we're just early stage scratch on the surface. It's like we have, um, the example I keep giving is I feel like we have like the Edge network right now and \[00:44:00\] the version of ai, right? Like when the iPhone first came out, it had, I. Internet connectivity, but it was like that, like really kind of poor edge network on at t and you could kind of barely, you needed wifi essentially. And now we have like 5G ultra wideband where it's just like, you just expect to be able to like stream high definition video like anywhere on the planet, pretty much. And I'm kind of like, what's that? Like, what is that version of ai? Like, we're in the Edge network ai, where we're like very constrained, right? In terms of what it can do. So it's pretty exciting. And I had this, um, I was on  **David Goldman:** By the way, I also don't think that that's necessarily dystopian. Uh, like some people may take it that way, but you know, for example, we use our phones a lot during the day and if we were to be just using our phone less and instead interacting in the world, but we were help, you know, had some, I dunno, wearable or embodied air or whatever it is that's collaborating with us and helping us with stuff. And so now we don't need to be buried in a screen that I think that would be better.  **David DeRemer:** Yeah, I agree. I think, I would love to see the major \[00:45:00\] players in this space like Google and open AI and others like. Start to paint some better pictures like that are aspirational for humanity, right? Like, oh, let, let's just like replace the workforce with ai. Like that's not really exciting to anyone, I don't think, unless, you know, if you're managing the p and l maybe, right? and certainly we'll probably drive like broader like income inequality and other things, right? I think that there's a lot of things that these tools can do for us that are actually truly extremely aspirational for like humanity. I would love to see a little bit more like vision put forward by the tech community around what those things are as opposed to sort of the more near term cost savings and sort of the more dystopian things that I think are like less exciting. but like back in March, I, uh, I heard this talk by Eric Schmidt,you know, former CEO of Google or Alphabet or I dunno, whatever it is, and he had, someone asked him like,what's a prediction about AI or what's your a position on AI that, That you think is like controversial and he said that it's under hyped. and I thought that was really interesting, position. 'cause o obviously it's about as over-hyped \[00:46:00\] as anything in the world right now. but just a idea that it can go so far. So, well thanks very much. Thank you so much for, for coming on sharing all these ideas. We covered quite a, abroad range of topics, today. And so, um, if people wanna find out more about you or Celesta, where can they learn more? **David Goldman:** Uh, you can go to Celeste's website, Celesta vc, and I write a substack called close to the metal.  **David DeRemer:** Close to the metal, like it. Awesome. Well thanks so much. Appreciate your time and uh, thanks for sharing your ideas.  **David Goldman:** Absolutely. Thanks David. ### Redefining QSR Technology With Quality Source: https://verygood.ventures/podcasts/brian-abston-the-wendys-company-redefining-qsr-tech-with-quality-at-the-core/ In this episode, Brian Abston shares how he led the mobile transformation at Wendy’s by unifying fragmented native apps into a single Flutter-based platform. The result? Accelerated development, improved scalability, and faster delivery—doubling output without growing the team. Brian also reveals how tight alignment between engineering, product, and marketing enabled Wendy’s to execute seamless real-time experiences during high-stakes moments like their March Madness campaigns, ultimately driving business success in the fast-paced QSR industry. {% audio_embed url="https://player.cohostpodcasting.com/2556b84c-d308-4f10-9cf5-ee0404a52dc2/144dfe97-8690-4610-8d5b-d2d8f2c4f939" /%} ## Transcript \[00:00:00\] **Brian:** It's really easy to tell somebody what you don't like, but it's hard to tell \[00:00:03\] people what you really need. And so until we can get there, \[00:00:06\] uh, it's, it's, we're we're just not gonna be there with ai, in my opinion \[00:00:09\] for a while. But, but as a, as a, to help with productivity, \[00:00:12\] I, I, I think it's exciting. \[00:00:15\] \[00:00:18\] \[00:00:21\] \[00:00:24\] \[00:00:27\]  \[00:00:30\] **David:** Okay. Hi Brian. Welcome to Build to Succeed.\[00:00:33\]  \[00:00:33\] **Brian:** How you doing, David? \[00:00:34\] **David:** Doing great. Thank you so much \[00:00:36\] for taking some time to chat us. Um, so I wanted to start our \[00:00:39\] conversation. You're the Senior Director of Digital Technology at \[00:00:42\] Wendy's. And, uh, so the question, I want to know that's most \[00:00:45\] important is, uh, is it okay if I dip my fries in my \[00:00:48\] frosty. \[00:00:49\] **Brian:** Yeah, I mean, so \[00:00:51\] absolutely the, that's kind of really what a lot of people \[00:00:54\] do. So, you know, you get the salt and the sweet at the same time. \[00:00:57\] The biggest controversy and the bigger controversy that we have, \[00:01:00\] uh, at Wendy's internally is. Vanilla or \[00:01:03\] chocolate. So \[00:01:06\] what's, what's your answer there? \[00:01:07\] **David:** Yeah, I'm, I've always been to chocolate \[00:01:09\]  \[00:01:09\] **Brian:** Okay. So, yeah.  \[00:01:10\] **David:** it's just a really, really great \[00:01:12\] combo. well, so that's a good intro. Uh, as I mentioned, you're the \[00:01:15\] senior Director of technology at Wendy's. Um, maybe you could \[00:01:18\] kick us off by just introducing yourself and maybe take us \[00:01:21\] through your career journey that got you to this point. \[00:01:23\] **Brian:** Yeah. \[00:01:24\] So, um, like you mentioned, uh, senior, \[00:01:27\] director, digital technology at Wendy's. Uh, been \[00:01:30\] around the industry for several years, across different, \[00:01:33\] uh, different industries. Uh, started \[00:01:36\] my, career as an electrical engineer. So I have, \[00:01:39\] my first degree I got was an electrical engineering \[00:01:42\] degree. started my career working in with \[00:01:45\] hardware and data centers. Uh, you know, late, late nineties \[00:01:48\] things started shifting towards software and less hardware, and \[00:01:51\] so started dabbling around and. \[00:01:54\] Software and programming and, and kind of fell in love with it. And, \[00:01:57\] uh, went back to school, early two \[00:02:00\] thousands and, uh, got my computer science degree. \[00:02:03\] And, uh, ever since then I've been, doing software development for \[00:02:06\] my career. Kind of left the hardware scene \[00:02:09\] and, and went straight into software and, uh, have been \[00:02:12\] primarily a Java developer most of my career. Da, \[00:02:15\] dabbled in some front end stuff. I'm, I'm dangerous with the front end. \[00:02:18\] Uh, Part of the stack, but primarily just been \[00:02:21\] doing Java development, some architecture stuff throughout my \[00:02:24\] career. yeah. And so about eight years \[00:02:27\] ago. got into the quick ER QSR industry, which is what we call it's \[00:02:30\] quick service restaurant. So fast food. had an opportunity to work \[00:02:33\] for an iconic, uh, Oklahoma brand. come on board \[00:02:36\] as the architect and help them, uh, build and launch \[00:02:39\] their mobile experience for, for \[00:02:42\] their brand. And it was a lot of fun. I really enjoyed \[00:02:45\] digital, and really. Cloud, \[00:02:48\] mobile, all that stuff is just a lot of fun. And it was really also my \[00:02:51\] first time I've got to, uh, be involved \[00:02:54\] in something that is used by the public. Most of my \[00:02:57\] career was like in FinTech and law enforcement and \[00:03:00\] power industry, and so we had like internal, I. \[00:03:03\] S software was internal to, to our, our \[00:03:06\] customers internally, you know, that were paying for it, but nothing like where you \[00:03:09\] have millions of people, including your neighbors, your parents \[00:03:12\] and family using, using the product. And that, that \[00:03:15\] made it a lot of fun. And, and so, four years ago, about four \[00:03:18\] years ago, I had an opportunity to join Wendy's, uh, as their \[00:03:21\] director of engineering and came on board and \[00:03:24\] helped them kind of rebuild. Retool their, their \[00:03:27\] engineering shops at Wendy's, and then pivot it over to \[00:03:30\] just focus on digital technology. And, and that's where I'm at today.\[00:03:33\]  \[00:03:33\] **David:** So when you say digital technology, like what does that \[00:03:36\] encompass at a, at a company like Wendy's? Um, what kind \[00:03:39\] of digital platforms and tools generally are within that kind of \[00:03:42\] business? \[00:03:42\] **Brian:** Yeah. So for, for, for Wendy's, uh, \[00:03:45\] our digital platform, digital technology, it's all custom \[00:03:48\] built. So I'm over the, the software engineering \[00:03:51\] side. Um, so you know, you're talking. \[00:03:54\] We have a platform team, and so, uh, you know, we have our, our stuff \[00:03:57\] runs in the cloud, so we have a cloud platform, and then my team, \[00:04:00\] uh, develops all the APIs for that run on the \[00:04:03\] platform and power our e-commerce as well as, \[00:04:06\] uh, my team does the, the mobile, development \[00:04:09\] and, and our website as well for, or mobile, \[00:04:12\] ordering on the website. \[00:04:13\] **David:** Hmm, and that stack was mostly \[00:04:15\] homegrown at Wendy's over time. \[00:04:16\] **Brian:** Yes. Yeah. As well as the \[00:04:18\] last brand I was at. So it was all custom, custom \[00:04:21\] software. 'cause really QSR is a unique industry \[00:04:24\] because, uh, if you think about it, like for Wendy's, we have \[00:04:27\] over 6,000 stores and so there's a lot of \[00:04:30\] integration. So I. No matter where you're \[00:04:33\] ordering, on, on the mobile app, your order eventually has to \[00:04:36\] end up in the store. And so there's, there's a lot of challenges \[00:04:39\] there because you can think about it as, you know, \[00:04:42\] 6,000 little data centers that \[00:04:45\] we have to be able to communicate with. And so, there's some \[00:04:48\] uniqueness to that and, and, and with menu and, and everything \[00:04:51\] like that, it, requires a lot of custom development \[00:04:54\] and.  \[00:04:54\] **David:** did the, no, I was gonna say, when did the, uh, \[00:04:57\] when did mobile really start to take hold at Wendy's as a key part of the \[00:05:00\] strategy? \[00:05:00\] **Brian:** Uh, so when I, when I, uh, when I \[00:05:03\] joined four years ago, uh, it was already, uh, \[00:05:06\] built and in production. So I think it was about \[00:05:09\] probably six years ago when they kind of started with \[00:05:12\] it. Uh, and then, about probably five years ago is when they \[00:05:15\] really kind of started investing heavily into \[00:05:18\] it. And then, uh, when I came on board, it's been, heavily \[00:05:21\] invested in and keeps growing every year. \[00:05:23\] **David:** And so \[00:05:24\] the, the mobile app was a, a big, big project at \[00:05:27\] Wendy's, right? I think you guys have recently, um, did you \[00:05:30\] re-platform or, or was this a new build that you guys have done recently?\[00:05:33\]  \[00:05:33\] **Brian:** So we recently, uh, completely rewrote the \[00:05:36\] mobile app. Um, we originally had a \[00:05:39\] native iOS, a native Android app, and we've \[00:05:42\] rewritten, rewritten it from the ground up and flutter. \[00:05:44\] **David:** \[00:05:45\] What was the challenge, uh, with the previous \[00:05:48\] native apps that kind of led to that sort of change? \[00:05:50\] **Brian:** \[00:05:51\] Yeah, I mean, so mobile app mo \[00:05:54\] mobile native app development's expensive, right? So you have \[00:05:57\] one product, but you have to have two, you have two code bases, right? \[00:06:00\] You have a Android code base and iOS code base. And \[00:06:03\] so that means, you know, two different teams, uh, \[00:06:06\] potentially, and it's hard to kind of operate as one team when you've \[00:06:09\] got. You know, two different sets of developers working on two \[00:06:12\] different code bases. some of the challenges we had \[00:06:15\] was the app over the time prior to me \[00:06:18\] joining is it had kind of changed hands a couple of \[00:06:21\] times with different partners. And so, originally it was, \[00:06:24\] you know, it was, when it was built, there was some, \[00:06:27\] Architectural things that were, were not \[00:06:30\] done, uh, properly and some best practices not followed. \[00:06:33\] And then it just kind of kept building on top of that, uh, \[00:06:36\] as, as things as the app got more complicated. \[00:06:39\] Like, for example, it was not very testable. The, the way the \[00:06:42\] code was written. Uh, it was hard to test. And so when \[00:06:45\] things are hard to test, right, it takes longer to get things into \[00:06:48\] production as well as, you know, you're gonna, \[00:06:51\] uh, you're gonna have more bugs in production. So, uh, it \[00:06:54\] just became to where it was taking too long to. To meet our \[00:06:57\] business, what our business was asking for. So \[00:07:00\] we'd come to really a crossroads at Wendy's and we, the, \[00:07:03\] the decision was either major \[00:07:06\] refactor of, of both code bases or a \[00:07:09\] MA or rewrite it. And so, I. I had \[00:07:12\] dabbled in flutter for a while and had been following Flutter \[00:07:15\] since it was originally announced and really liked what \[00:07:18\] I had seen with it. And so, uh, \[00:07:21\] we, I really started looking into the decision of, Hey, let's just \[00:07:24\] rewrite this thing in flutter and, uh, \[00:07:27\] let's get one code base, uh, and, and not have to worry about, \[00:07:30\] you know, managing two different code bases. \[00:07:32\] **David:** Yeah. What was the \[00:07:33\] process you went through to, \[00:07:36\] um, a identify the need? Right? Because I think \[00:07:39\] that's probably, there's, I'm assuming there was some pushback or concern at the \[00:07:42\] time of like, do, can we do this? Can we, can we \[00:07:45\] rebuild the thing or, uh, or completely \[00:07:48\] re-platform it? What's that gonna do for business continuity, pushing new \[00:07:51\] features? How do you kind of like go through that \[00:07:54\] process of figuring out that A, it's the right thing to do and get the \[00:07:57\] buy-in, and then b, what's the process for \[00:08:00\] evaluating those two solutions, um, to a point where you could \[00:08:03\] make a decision and, and run with it? \[00:08:05\] **Brian:** Yeah. I'll \[00:08:06\] start with the kind of the de the decision on flutter. \[00:08:09\] I'd been following, you know, the industry \[00:08:12\] for a while and, and you know, the, the promises of \[00:08:15\] being able to have one code base and run on both, \[00:08:18\] both platforms and, you know, really there had been some \[00:08:21\] previous, some predecessors that tried, and in my \[00:08:24\] opinion, it just was. Didn't live up to the \[00:08:27\] hype. And it really came down to there was, there's two, in my \[00:08:30\] opinion, two players. You got React native and you have flutter. And so \[00:08:33\] I took a look at both of them. Um, I. \[00:08:36\] React is React native is a great \[00:08:39\] product and it's, and it's popular. Um, but, but \[00:08:42\] for me, uh, when I was evaluating \[00:08:45\] React Native, I found it hard. I found it difficult to get up to \[00:08:48\] speed quickly with React Native, as well as. \[00:08:51\] A lot. It was really hard to find like, \[00:08:54\] Hey, if I'm doing this, what is the best way to do that in React \[00:08:57\] native? Um, it was really hard. You would, you know, you'd do a \[00:09:00\] Google search and you'd get 10 different answers and the, \[00:09:03\] the, the platform was moving, the framework was moving so fast at \[00:09:06\] what was best practice, you know, six months ago is like not the way you do \[00:09:09\] it anymore. And so I, I found it really hard as well as \[00:09:12\] the tooling I found kind of difficult to kind of get up and \[00:09:15\] running and, uh, took a little bit of work. And, and so \[00:09:18\] when you compare that, when you contrast that with flutter. I really \[00:09:21\] enjoyed dart. I thought DART was an easy language to learn. \[00:09:24\] really the tooling and flutter is top-notch in my \[00:09:27\] opinion. You can, you know, you can get a, you can get up and running in, in a \[00:09:30\] matter of minutes with flutter and the developer experience \[00:09:33\] is great. So, uh, for me it became really an \[00:09:36\] easy decision, you know, as your, as a, as a leader and \[00:09:39\] you're, you're thinking about the team, you also have to think about, you know, \[00:09:42\] when I start trying to find developers, \[00:09:45\] you know, if I have a junior developer that's never. \[00:09:48\] That has experience, but you know, not \[00:09:51\] experiencing either flutter or React native, you know, how hard is it gonna be \[00:09:54\] them to get up to speed on, on either one of these frameworks? And, and I \[00:09:57\] just felt flutter that was hands down the winter there. \[00:10:00\] So, was a really easy decision there for that. you know, how \[00:10:03\] do you, how do you do this for, for us, you \[00:10:06\] know. I didn't have a lot of buy-in at the time, uh, \[00:10:09\] from, from the business. because we, you know, our \[00:10:12\] previous code base was not great. We were having, you know, \[00:10:15\] really hard time kind of delivering, uh, \[00:10:18\] and meeting the required, you know, meeting the needs of the business and being able to \[00:10:21\] move as fast as they wanted to move. So not a lot of \[00:10:24\] confidence in the team at that time. So, uh, \[00:10:27\] fortunately I had. I had the confidence of our, of our \[00:10:30\] CTO and, um, and so talking \[00:10:33\] with, you know, with our, with him, you know, we, we \[00:10:36\] both agreed that, you know, something's gonna have to be done. And, and so he had a lot of \[00:10:39\] trust in me. And so the way we went about it \[00:10:42\] was, you know, he, he worked with me and we. We \[00:10:45\] basically funded a six month engagement with, we brought on \[00:10:48\] two developers, two flutter developers, and gave them \[00:10:51\] six months and said, let's, let's see how \[00:10:54\] far we can get with flutter in six months. And you know, \[00:10:57\] within six months we're gonna know, Hey, is this gonna work or is it \[00:11:00\] not? And so for us, you know, we really kind \[00:11:03\] of. K kind of skunk worked it, I guess you would say. and, and \[00:11:06\] really to start showing the business that, hey, this is the \[00:11:09\] right decision. And as we started building it, \[00:11:12\] it, it became really, really obvious that, uh, this was \[00:11:15\] gonna be a go. \[00:11:16\] **David:** Awesome. So you, you built the new \[00:11:18\] app in parallel while you're continuing to support the native apps. \[00:11:20\] **Brian:** \[00:11:21\] Yeah. And so the interesting thing there, is. \[00:11:24\] While we were working on Flutter as kind of a skunk \[00:11:27\] works, you know, we were also still adding things to the, \[00:11:30\] to the Native app and um. \[00:11:33\] We got to a point where we were, we would be adding the \[00:11:36\] same feature in both to try to keep 'em, uh, similar \[00:11:39\] and we were getting like two x speed on flutter. \[00:11:42\] Like we'd f like something that would take, you know, 30 days \[00:11:45\] and native to get done. We would get done and, you know. \[00:11:48\] Less than, a sprint. And so we found really overall, \[00:11:51\] about a two x improvement in, in velocity \[00:11:54\] to get things done. Kind of a funny story, uh, \[00:11:57\] that those two developers started middle of December, \[00:12:00\] uh, a couple years ago. And, uh, I I went on \[00:12:03\] leave, for, for Christmas break for two weeks. \[00:12:06\] And, uh, when I came back we had, we had our first. \[00:12:09\] Call when I got back, and they worked a lot of \[00:12:12\] Christmas break. Uh, they took a few days here and there, but for the most \[00:12:15\] part they, the, the two, the two were working. And when \[00:12:18\] I, when I got back, I expected maybe to have a screen or two ready \[00:12:21\] and, you know, I got back and they pretty much had a, a \[00:12:24\] functioning prototype where, you know, you could browse the menu \[00:12:27\] and log in and off and, and just, \[00:12:30\] I was just totally blown away, uh, by how fast you, you know, we were \[00:12:33\] able to get something up and running. \[00:12:35\] **David:** Did they have, \[00:12:36\] um, so were they like essentially using the native apps as \[00:12:39\] a reference, uh, reference code? Or did they have. \[00:12:41\] **Brian:** Yeah, so, \[00:12:42\] so for us, uh, it was, it was a little different because we \[00:12:45\] already had a, we already had a app, uh, with millions of users \[00:12:48\] using it. Uh, and, and they were both already in the app store and \[00:12:51\] public and, and, you know, and, and so we, \[00:12:54\] when we started, we said, okay, let's just make it \[00:12:57\] very similar to the, to the native app, and we, we'll, \[00:13:00\] we'll kind of go from there. Uh, really it was just to \[00:13:03\] prove that we could make. We could make what we have \[00:13:06\] now and flutter and, uh, and, and show the \[00:13:09\] business that, yeah, we can do this, you know, easily with flutter.\[00:13:12\]  \[00:13:12\] **David:** Was there skepticism? You mentioned, you know, \[00:13:15\] internally the business was, uh, sort of \[00:13:18\] feeling the pain of the, the prior apps and there was a little, a \[00:13:21\] little low trust. When you think about making a bold choice \[00:13:24\] like this, were you gonna do something different as a solution to it? \[00:13:27\] What kind of skepticism did you have to overcome? Or what kind of \[00:13:30\] internal sales did you have to do to, to get everybody comfortable with this \[00:13:33\] choice? \[00:13:34\] **Brian:** really it was just a \[00:13:36\] matter of, of, of me kind of, calling in some \[00:13:39\] favors and say, Hey, just, you know, could you \[00:13:42\] trust this decision? And, and, and the way we went about it, \[00:13:45\] I think really allowed us to. \[00:13:48\] kind of hedge our bet, because, you know, we didn't say, \[00:13:51\] Hey, we're gonna, we're gonna go, you know, spend millions of \[00:13:54\] dollars and, and build a whole team and, and we're just gonna throw away the old app and \[00:13:57\] rewrite it, right? We, we kind of, we said, Hey, we're gonna, we're gonna \[00:14:00\] prove to you in, in less than six months that \[00:14:03\] this is the way to go. And so the, the \[00:14:06\] decision really paid off well because, You know, \[00:14:09\] it's, it's scary to, to go, to tell the, you know, for the \[00:14:12\] business to say, to go tell them we're gonna just throw \[00:14:15\] away everything we've been working on and rewrite it completely. So, \[00:14:18\] um, so that made the deci that made it \[00:14:21\] really easy to, to kind of overcome the skepticism. Uh, \[00:14:24\] you know, we had skepticism on the technology side as well. \[00:14:27\] Um, with Flutter being a, a new frame, newer \[00:14:30\] framework, um, we were worried about, you know, we have a lot of \[00:14:33\] integrations with different partners, uh, and we were worried \[00:14:36\] about, you know. It could flutter. Could we \[00:14:39\] deal with it if they don't have an SDK? You know, \[00:14:42\] does the bridge, you know, the, what the, I think flutter \[00:14:45\] calls it a bridge. Does. Is that gonna work? Is it gonna live up to the hype? Can \[00:14:48\] we, can we do that easily and make things work? But \[00:14:51\] for the most part, uh, we got lucky, several of our \[00:14:54\] partners, uh, while we were building the app, we're \[00:14:57\] also launching flutter support as well. So we \[00:15:00\] actually helped, uh, one of our partners test their flutter \[00:15:03\] SDK as we were building our app. So a lot of that \[00:15:06\] became, Not important as, as we kept going \[00:15:09\] because there was more support for Fluter, you know, every week.\[00:15:12\]  \[00:15:12\] **David:** Hmm. That's awesome. What was the like, sort of help \[00:15:15\] me understand like the shape of your team back, both \[00:15:18\] pren and then maybe kind of where it is today in terms of, I. Like \[00:15:21\] roughly how big was each team and what was the flow of like \[00:15:24\] idea through into like product and design and into \[00:15:27\] engineering and deployment. And I'm just kind of curious to contrast \[00:15:30\] how that may have changed from the old way to the current state.\[00:15:33\]  \[00:15:33\] **Brian:** Yeah, so at, at Wendy's, the \[00:15:36\] product team, uh, the digital product team, they, they \[00:15:39\] live in, in our, well, our marketing team. And so, \[00:15:42\] our, our engineering team is separate, but we're, we're \[00:15:45\] partnered closely, so we're in meetings daily with them. And so \[00:15:48\] everything kind of flows through the product team, uh, \[00:15:51\] the ideas and they decide, you know, Hey, \[00:15:54\] this is something that we really want to add to the app, and then it. \[00:15:57\] then they work with us, the engineering team, and then, you know, we, we \[00:16:00\] implement the whatever they ask was. And \[00:16:03\] so, prior to Flutter, um, \[00:16:06\] you know, we had, we had a basically two, two \[00:16:09\] teams. We had an Android team and a iOS team, \[00:16:12\] um, about, think we had about \[00:16:15\] 12 developers on each. Um, \[00:16:18\] and, um. What we \[00:16:21\] saw was, you know, we tried to keep 'em as one team, but you know, when you \[00:16:24\] have a team going off and implementing something, another team \[00:16:27\] going off and implementing something, oftentimes \[00:16:30\] they're not implemented the same way. And there would be little nuances between \[00:16:33\] Android and iOS. Most of the time it wasn't major, but \[00:16:36\] you know, some, sometimes there'd be nuances. And then also you have \[00:16:39\] obviously. Unique set of bugs in each code base, \[00:16:42\] right? And so, you know, something would get, report \[00:16:45\] mobile's really hard, just in general when you have millions of users, right? You've got all \[00:16:48\] these devices, all these different screen sizes, you \[00:16:51\] know, network connectivity issues, and, and just. \[00:16:54\] Lots. I call it the wild west. Basically your, your app is in a \[00:16:57\] hostile environment. It's not like in a controlled data center. \[00:17:00\] So, you know, we'd get an issue reported \[00:17:03\] and then the, you know, the first thing you would have to find out, is it the iOS \[00:17:06\] app or is the, you know, Android app. And so there was always this \[00:17:09\] kind of, I. Divergence of things that would happen, \[00:17:12\] you know, in two different paths based on, uh, what the, \[00:17:15\] what the device was. It's also hard for qa, right? You have a QA team that has to \[00:17:18\] test an Android app, everything in the Android app and \[00:17:21\] everything in the iOS app. And so, uh, it \[00:17:24\] also was intensive for QA and the product team, product team had to \[00:17:27\] sign off on, you know, two different code bases. And so, \[00:17:30\] Moving to Flutter, it's greatly simplified how we, \[00:17:33\] how we do things. And so now we think of everything as one. Yeah, there's, \[00:17:36\] there's some little nuances between iOS and Android, but for the \[00:17:39\] most part, our business requirements and the things that \[00:17:42\] we're doing, you know, with within our business logic is the same in \[00:17:45\] both apps. Um, and so it becomes really easy \[00:17:48\] for the product team to sign off on things because they're \[00:17:51\] really only. Looking at, they're looking at the same code base for \[00:17:54\] the most part, uh, as well as the QA team. \[00:17:57\] Uh, and so it's, it's been really nice. And then \[00:18:00\] the, the, the team itself, we basically combined \[00:18:03\] our, both of our teams into one team. And so now it's just \[00:18:06\] the mobile team. Uh, and we don't have to think about, \[00:18:09\] uh, iOS and Android, you know, teams \[00:18:12\] being separate and, and trying to make sure everything's together. \[00:18:15\] It's really given us. The opportunity to \[00:18:18\] flex our resources around, right? And so if we have a \[00:18:21\] feature that's, a pretty big feature, um, you know, we \[00:18:24\] can just put more, more developers on that feature. \[00:18:27\] Whereas, you know, before we were, we were limited, right? If \[00:18:30\] we had a big feature, we were limited to only the iOS \[00:18:33\] developers and only the Android developers on, on the Android side. And so \[00:18:36\] now we have a bigger pool of developers to kind of, \[00:18:39\] uh, you know, shuffle around onto different things as needed.\[00:18:42\]  \[00:18:43\] **David:** I That's, that's really insightful and, \[00:18:45\] and that's an area where I think we, with these multi-platform \[00:18:48\] solutions, people often focus on the developer \[00:18:51\] productivity. Um, but I think what you're highlighting is the \[00:18:54\] impact on all the adjacent teams that the engineering team has \[00:18:57\] to interface with, the design team, the product team, the QA \[00:19:00\] team, and there's a lot of savings and \[00:19:03\] efficiencies on those teams as well. And the, the relationships there. \[00:19:06\] Um, so you mentioned, I love that notion too of like you \[00:19:09\] switched down just the mobile team, just the one mobile team. But what's \[00:19:12\] that like culturally? Because you said you got 12 people, \[00:19:15\] iOS and Android, they probably identify as, I'm an iOS \[00:19:18\] developer, I'm an Android developer. You got two flutter engineers \[00:19:21\] going out, kind of rebuilding it and kind of keeping pace \[00:19:24\] and then maybe even going a little faster than them. What was \[00:19:27\] that like from a culture perspective and like a team perspective when \[00:19:30\] you were making that transition and that shift?\[00:19:33\]  \[00:19:33\] **Brian:** Yeah, we, we had a few developers, so, um, \[00:19:36\] most of our mo our mobile developers are, are, are, uh, were, \[00:19:39\] contractors and so. most of them were \[00:19:42\] excited, uh, about moving to Flutter. Uh, we had a couple \[00:19:45\] that were, you know, they're diehard native developers and, they, they \[00:19:48\] just didn't wanna to move to flutter. And so, \[00:19:51\] so we worked with that, You know, uh, but for the \[00:19:54\] most part, everybody was super excited and, the way we \[00:19:57\] really messaged it, there was really not any issues. Look, \[00:20:00\] when you have a, when you have a bad code base or a code base is \[00:20:03\] not great, uh, and it's bloated and it's been, you know, \[00:20:06\] passed down from multiple teams. Look \[00:20:09\] it, it's not a knock on the current team, right? It's just, it \[00:20:12\] is what it is and it's, it becomes unwieldy and difficult really, in \[00:20:15\] my opinion. It really just solidified, you know, as we were \[00:20:18\] doing stuff side by side, uh, it really solidified that, \[00:20:21\] uh, moving to flutter was the right decision, \[00:20:24\] um, because we were able to, to move so much faster. \[00:20:27\] So, um, really, to be quite honest, David, there was \[00:20:30\] not really any. Any negative piece, \[00:20:33\] you know, negative sides to, to moving to flutter with the teams. \[00:20:36\] Everybody was excited. And, and since then, \[00:20:39\] uh, Mo you know, now that we're fully a flutter, flutter shop, \[00:20:42\] everybody loves it. And, um, you know, one of the things also, \[00:20:45\] you know, when when we were done building it, you know, we \[00:20:48\] were. I don't remember what the final number was, but we \[00:20:51\] were, we were high nineties in test coverage, like 98, 90 \[00:20:54\] 9% test coverage in the app. And so it just, \[00:20:57\] it just, it was just a pleasure to work with for the developers \[00:21:00\] because they know if they break something, uh, they're, they're \[00:21:03\] more than likely gonna know before, you know, before we handed off to QA  \[00:21:05\] **David:** \[00:21:06\] That's awesome. So in, in addition to the efficiencies of \[00:21:09\] being able to just ship faster, it'll, you're doing things \[00:21:12\] now that just weren't possible to do before in terms of like making sure you \[00:21:15\] got the time to write tests. \[00:21:16\] **Brian:** Yeah. Another, another, uh, \[00:21:18\] very pivotal thing that we invested in. When we moved \[00:21:21\] to Flutters, we worked with our UX partner and we, \[00:21:24\] uh, we built, we built the design system. And so our new app \[00:21:27\] is, our new Flutter app is completely using a design \[00:21:30\] system. Um, and so, you know, our, our old app, like \[00:21:33\] just an example, we, I think we had, like, we counted, we had \[00:21:36\] like 10 different shades of red, uh, Wendy's \[00:21:39\] red, and uh, you know, and. You get a new \[00:21:42\] developer in there and they, they may go grab the wrong \[00:21:45\] color, whereas now, you know, it's all built in the design \[00:21:48\] system and so somebody comes in, they just use the, the, the prebuilt \[00:21:51\] components and it's, it just makes the \[00:21:54\] experience much more unified. I. \[00:21:56\] **David:** I have a \[00:21:57\] question around the, this cultural shift too. Do you feel like \[00:22:00\] now that the team is all aligned and more unified as one \[00:22:03\] team, has it uh, improved overall? The general team \[00:22:06\] culture and collaboration? \[00:22:08\] **Brian:** Yeah, I think \[00:22:09\] so. Um, there was really not a lot \[00:22:12\] of, even though we had two mobile teams, I mean iOS team and \[00:22:15\] Android team, we were really, I. We all shared \[00:22:18\] most of the same ceremonies together. So there there was not a lot of \[00:22:21\] us in them in type in our culture. we did, we \[00:22:24\] worked really hard over the last four years to, to build a \[00:22:27\] very friendly engineering culture. and so didn't, \[00:22:30\] didn't really see a lot of that. But, um, one of the \[00:22:33\] things it has done for us as an engineering team is it's \[00:22:36\] built a lot of trust with our business partners and our \[00:22:39\] product team. And, uh, you know, \[00:22:42\] now they, they really. C that yeah, it \[00:22:45\] was the right decision. And they're, and having them all on \[00:22:48\] board and, and moving with us, uh, \[00:22:51\] to move forward is, has been awesome. And, and the, and the, \[00:22:54\] and just having the trust. Right? And, and don't get me \[00:22:57\] wrong, the, the, the lack of trust was, was, was \[00:23:00\] something that we had, we had earned. Right? And so we had, we had to earn \[00:23:03\] that trust back. Trust is earned. And so, um, this was a \[00:23:06\] really, been a really positive, uh, thing for, \[00:23:09\] for our engineering team as, uh, as a team, as \[00:23:12\] a whole. \[00:23:13\] **David:** That's something we've heard before from some \[00:23:15\] other companies that have switched to flutter is like repairing this broken \[00:23:18\] trust that the technology has. Where when the technology's moving \[00:23:21\] really slowly, you know, there's, \[00:23:24\] I think I, one of the things is like we have this reputation, like \[00:23:27\] everything's gonna take twice as long and cost twice as much. And technology, I. \[00:23:30\] Uh, I've said before, it's like, what, what a terrible \[00:23:33\] reputation to have like in as a profession. Um, \[00:23:36\] and then you get something like this where you have an opportunity to repair that trust and you're \[00:23:39\] able to move faster. Uh, you get that trust back \[00:23:42\] and that confidence. What kind of like business \[00:23:45\] values, do you think that, or business impact that shift \[00:23:48\] in trust creates for a technology \[00:23:51\] team in terms of velocity? Is it just shipping faster? Is \[00:23:54\] it more responsive reaction, um, fixing bugs faster? \[00:23:57\] What are, what are the impacts? \[00:23:58\] **Brian:** Yeah, I think it's a little bit of all the \[00:24:00\] above. Um. Last year we had to, we had a couple \[00:24:03\] of partners that we had to, integrations that we had to change. \[00:24:06\] and we had two of them. One of 'em \[00:24:09\] was, was kind of a Y 2K event for, for us. \[00:24:12\] We had to, we had to get moved off by a certain date \[00:24:15\] and, We had made the decision at that point. It was before we \[00:24:18\] launched Flutter. we launched Flutter in April and we, \[00:24:21\] we found out in December we were gonna have to move, uh, \[00:24:24\] partners and it was a major piece of our, of our, uh, \[00:24:27\] integration and our application. And so, we were \[00:24:30\] given a very tight timeline. Like we were told by the company, \[00:24:33\] they don't think that we could get it done, within that timeline. \[00:24:36\] And had it been native, I don't think we would've got it done, but \[00:24:39\] we were able to deliver, On time \[00:24:42\] with Flutter and get it out the door and \[00:24:45\] launch it, in time. Uh, we launched in April. \[00:24:48\] Our, our Y 2K event was July. Um, \[00:24:51\] and so we started, we launched, uh, our flutter app first on \[00:24:54\] Android and started doing the staggered release \[00:24:57\] and watching, you know, our KPIs as, as we \[00:25:00\] increased and, you know, several hot fixes, right? 'cause once you \[00:25:03\] throw something out in the wild, um, you're gonna, you're gonna see things that you've not \[00:25:06\] seen in, in a lab. And so. so the \[00:25:09\] ability to hot fix, like, and so prior in \[00:25:12\] Native, we were doing about one release a month, and now we're doing \[00:25:15\] a release of sprint if we want. Uh, so we're doing two, two \[00:25:18\] releases a month if, if needed. Uh, and as much as we've been \[00:25:21\] moving lately, we've been taking advantage of that. So we've, \[00:25:24\] we've been able to basically deliver much faster.\[00:25:27\]  \[00:25:27\] **David:** So not only more efficiently, more productive with the \[00:25:30\] team, but also just shipping value to customers much, much \[00:25:33\] faster and, and like the same to both places at all \[00:25:36\] times. Not like, Ooh, is Android a little different? Is iOS a little different? \[00:25:39\]  \[00:25:39\] **Brian:** And also too, it becomes hard to keep two \[00:25:42\] teams, uh, in sync, right. And two products in sync. We have, \[00:25:45\] we, we still have, like, you know, we also have a, a \[00:25:48\] mobile, or not a mobile, a web ordering site as well. And keeping \[00:25:51\] that in sync is hard. And so, uh, you know, when you had \[00:25:54\] three different things trying to keep in sync from a customer \[00:25:57\] experience perspective, it, it made things harder. And, and with \[00:26:00\] it, with the mobile app just being flutter now, it becomes, you know, \[00:26:03\] it's in sync. You launch it on. You launch it \[00:26:06\] and it's launched, uh, you know, on both platforms, you know, we upload it to \[00:26:09\] the app stores and you're, and you're done. \[00:26:11\] **David:** Yeah, \[00:26:12\] so that's interesting. Your, it changed your release schedule and, and \[00:26:15\] how you guys are shipping to customers. Have there been any \[00:26:18\] other sort of changes to the way your team works as a \[00:26:21\] result of this shift or change? Um, any, \[00:26:24\] you know, build pipeline, CICD, even maybe just the \[00:26:27\] design system was one you  mentioned.  \[00:26:29\] **Brian:** Yeah. So \[00:26:30\] everything we do, automation is, is key, \[00:26:33\] right? So we, we try to automate everything. Uh, some of the things that \[00:26:36\] we, when we build and release, that we try \[00:26:39\] to do, we, we use a lot of feature flagging. and \[00:26:42\] so I. You know, mobile wants you, you know, you gotta get the app store approval. And so, \[00:26:45\] uh, when you get it out there, it's hard. You can't really \[00:26:48\] roll it back. Right. It's not like a an API that we control. \[00:26:51\] Right. And we just roll back, you know, we just run the pipeline to roll it back. \[00:26:54\] So, we rely heavily on, on feature flagging, \[00:26:57\] uh, within our mobile app, uh, so that we can turn it on \[00:27:00\] for, you know, for whoever we want, as well as we've, \[00:27:03\] we. Take advantage every time of the, \[00:27:06\] the staggered rollouts in the app stores now, you know, being able to \[00:27:09\] roll it out to a small percentage of users those two things \[00:27:12\] have allowed us to continue to move fast and take some \[00:27:15\] risk because, you know, we can easily, you know, \[00:27:18\] turn off or, or slow down, uh, if we do find \[00:27:21\] issues and, and how Fix it. \[00:27:23\] **David:** \[00:27:24\] Has this, uh, has it also improved the relationship with \[00:27:27\] the, uh, like the marketing team and the promotions team? I, \[00:27:30\] I would imagine quick service where there's a lot of like \[00:27:33\] regional campaigns and, you know, maybe there's \[00:27:36\] something big happens in, you're doing a big advertising campaign in sports or \[00:27:39\] some, some sort of, uh, seasonal promotion or something \[00:27:42\] like that. Does this, has this improved your ability to like, \[00:27:45\] keep up with the ideas of the marketing team and all the pace of \[00:27:48\] that as well? \[00:27:49\] **Brian:** Oh yeah, for sure. and that was, that was \[00:27:51\] really the, one of the biggest drivers, right, is we couldn't keep up. \[00:27:54\] Uh, for Wendy's, March Madness is one, we call it our \[00:27:57\] Super Bowl. So we, we. We do a lot of heavy promotion during March \[00:28:00\] Madness. and so, uh, some of the things that \[00:28:03\] we've done, uh, in the app will allow us to dynamically \[00:28:06\] change things and, and, and stuff like that to, whereas before it was, \[00:28:09\] was kind of hard coded and require, \[00:28:12\] require an app release. Um, so we've done a lot of \[00:28:15\] things. when we rebuilt it, we, we kind of \[00:28:18\] architected in a way that would allow us to, to keep up, uh, \[00:28:21\] and allow us to change things without, you know, involving the \[00:28:24\] engineering team. \[00:28:25\] **David:** Super cool. Do you guys use any like rev \[00:28:27\] tech or MarTech tools like, uh, the braises or the loyalty \[00:28:30\] programs, stuff like that? \[00:28:30\] **Brian:** Yeah, yeah. We're heavily in, we, we \[00:28:33\] have a, we're heavily in CRM as well as loyalty. So \[00:28:36\] loyalty is a huge, a huge aspect for Wendy's \[00:28:39\] digital. It's, it's one of the primary reasons, you know, someone \[00:28:42\] uses a, an app for QSR is, is a lot of the, the \[00:28:45\] offers and rewards, uh, of, of ordering through those.\[00:28:48\]  \[00:28:48\] **David:** And, uh, no risks. I know sometimes, sometimes the \[00:28:51\] people we talk to, they're uh, a little worried about like, am I gonna be able \[00:28:54\] to use my favorite loyalty tool or something like that. \[00:28:57\] But have you guys felt any, any pains from this \[00:29:00\] transition or this change across the board? Is there anything you miss from Native or where \[00:29:03\] Flutters been Not the greatest, \[00:29:06\] uh, thing. \[00:29:07\] **Brian:** No to be, I \[00:29:09\] mean to be quite honest, no, it's been, uh. \[00:29:12\] That was my biggest concern, right, was that there's gonna be some things that we \[00:29:15\] miss and, you know, for QSR app, \[00:29:18\] there's a ton of integrations, but there's nothing \[00:29:21\] super native that we needed. \[00:29:24\] So, um, I. we could overcome it very easily. \[00:29:27\] There's, there's still, we still have a couple SDKs that don't have, flutter \[00:29:30\] SDKs, but we just, you know, built the bridge and, \[00:29:33\] you know, use it that way. So, but for the most part, you \[00:29:36\] know, the, some of of our key integrations are either, uh, \[00:29:39\] API driven, so it doesn't matter. Or, they've \[00:29:42\] released an SDK for flutter. So it, it's not been an issue. And we haven't, \[00:29:45\] we haven't missed native at all. I've, I've, not, I've not heard \[00:29:48\] anybody say, uh, man, I wish we would've, you know, rebuilt this thing in \[00:29:51\] native. \[00:29:51\] **David:** Hmm. I think there's fear and there's \[00:29:54\] concern about like, well, what, what don't I know? And maybe I'm gonna hit \[00:29:57\] something that's unexpected. But certainly listening to your \[00:30:00\] story today, I mean, it'd be a no brainer to do it again. \[00:30:03\] You know, if you guys, if you go back and tell yourself, you'd be like, it is a hundred percent \[00:30:06\] the right choice. Um, not hearing a whole lot of like \[00:30:09\] negatives or reasons not to. Why do you think some people might \[00:30:12\] be sitting on the sidelines with some of this technology and not embracing this? \[00:30:15\] We see obviously the benefits are massive, right? In terms of \[00:30:18\] cost, savings, speed, time to market, all those things. \[00:30:21\] What do you think is holding people back on \[00:30:24\] this? \[00:30:24\] **Brian:** just the unknowns, I think probably more than likely, right. \[00:30:27\] Um, for us, I can be a risk taker at \[00:30:30\] times on technology. So, I've been around long \[00:30:33\] enough that when I see something, that I think \[00:30:36\] is, is the right solution, uh, I. And \[00:30:39\] I, and I kick the tires and, I take a look at it and it, \[00:30:42\] feels right and looks right and, and looks like it checks all the \[00:30:45\] boxes. I'm willing to put a little bit of time into it and, and, and make \[00:30:48\] sure, uh, that it is the right solution. I \[00:30:51\] think, you know, for Flutter, the biggest concerns \[00:30:54\] were, uh, you know, it's just a. It's not \[00:30:57\] native, right? And so you're worried about what's gonna hap, you know, is \[00:31:00\] Flutter gonna be here a decade from now or, or five years from \[00:31:03\] now? Or whatever, uh, or, or is it gonna see the \[00:31:06\] adoption rate that, you know, that React, react native is seen? \[00:31:09\] And so, so that was really the kind of the. \[00:31:12\] Skepticism that I saw from within \[00:31:15\] myself and the team. and like I mentioned earlier, \[00:31:18\] we did the six month test and that really, \[00:31:21\] in my opinion, is what made the decision easy. \[00:31:24\] Uh, it would've been a little more difficult for us to go in \[00:31:27\] and get. Get everybody to, to \[00:31:30\] say, let's do this. Uh, flutter is absolutely the \[00:31:33\] right decision and, and go all in and do it right. And \[00:31:36\] so, kind of really hedging it a little bit with that six \[00:31:39\] month, POC really checked all the boxes for \[00:31:41\] **David:** \[00:31:42\] Hmm. So it sounds like this definitely had a pretty significant impact on \[00:31:45\] the bottom line for, uh, for Wendy's. \[00:31:47\] **Brian:** Yeah, for us, \[00:31:48\] we, we still, we didn't, uh, we didn't, you can \[00:31:51\] look at it two ways. you know, when you, when you do something like this, when you \[00:31:54\] combine the code base, you could look at it as, well, we could, you know, we could \[00:31:57\] cut the team in half. Or you could look at it as, \[00:32:00\] I can take my existing team and deliver, you know, close \[00:32:03\] to double of what we are delivering, uh, \[00:32:06\] now. And for us, that's the, that's the road we went because \[00:32:09\] we weren't able to keep up before, uh, with the team, \[00:32:12\] you know, in the, in the code base. And, and now, you \[00:32:15\] know, we're able to keep up. And so for us it was, \[00:32:18\] you know, the value of being able to deliver. But \[00:32:21\] there's, there's two different ways that a, that a company could look at it. You could look \[00:32:24\] at it as. Uh, you know, I'm gonna save \[00:32:27\] resources, cost, and resources, or I'm going \[00:32:30\] to really save because \[00:32:33\] I'm getting double the value out of the same resources.\[00:32:36\]  \[00:32:36\] **David:** Nice. Yeah. I like fair weather and fa weather \[00:32:39\] stories there, you know? \[00:32:40\] **Brian:** Yeah, it just depends on whichever way you wanna, \[00:32:42\] you wanna look at it, but there's, uh, either way I've \[00:32:45\] like. The, the value of of having one \[00:32:48\] code base is, I I can't speak to it enough, \[00:32:51\] um, in mobile and, and it's just \[00:32:54\] so hard to maintain two code bases that need to try to do the \[00:32:57\] exact same thing, especially if you have an app that \[00:33:00\] has a lot of complexity to it. \[00:33:01\] **David:** Yeah. Have you guys \[00:33:03\] considered, um, like going to any other platforms, like doing \[00:33:06\] any kiosk work or in-store ordering type stuff or anything \[00:33:09\] along those lines? \[00:33:10\] **Brian:** So we looked at, uh, we looked at \[00:33:12\] doing it for web. I, I challenged the team and said, you \[00:33:15\] know, Hey, let's, let's just do a \[00:33:18\] little POC and, you know, and and \[00:33:21\] take a look at it for web. End of the day we \[00:33:24\] decided not to, to proceed with web. There's some \[00:33:27\] still, in my opinion, is still a little bit immature around \[00:33:30\] SEO and, uh, a DA compliance. And \[00:33:33\] so those are some of the things that are important to Wendy's and some of the things we wanted \[00:33:36\] to improve on. And so, uh, for us it just made \[00:33:39\] sense to, to not move forward with, with flutter on web. \[00:33:42\] We all, we, it's being used, uh, in other parts of the \[00:33:45\] company as well for some internal, uh, applications for \[00:33:48\] mobile. Um, and then I'm, I'm kind of looking at it for \[00:33:51\] some other internal tooling, uh, type \[00:33:54\] applications.  \[00:33:54\] **David:** Super cool. Yeah, I'm, I'm pretty hot on the idea of like \[00:33:57\] kiosks and sort of non-traditional devices. I think there's \[00:34:00\] just a lot of, uh, that's the real multi-platform aspect \[00:34:03\] of it that I think is super fun. And then you get real fungibility of people where \[00:34:06\] you can take people on your mobile team, work on those internal apps you're talking about, \[00:34:09\] work on the kiosks and you know, whether it's Linux or \[00:34:12\] Windows or whatever else. And I think there's a lot of untapped area there.\[00:34:15\]  \[00:34:15\] **Brian:** Right. And I agree. Our, our, our next gen kiosk, we \[00:34:18\] decided to, uh, use a partner, uh, for, but if, \[00:34:21\] if we would've internally developed a kiosk, I was a hundred percent gonna \[00:34:24\] use flutter for, for it because it just, it would've fit \[00:34:27\] perfect and we would've been able to reuse a lot of our, \[00:34:30\] our mobile code base, uh, to, to, uh, to de develop it. \[00:34:32\] **David:** \[00:34:33\] Awesome. Well, we've talked a lot about fluter. I wanted to get a sense \[00:34:36\] for your, uh, some of your like, management approaches, you know, what it takes to \[00:34:39\] lead teams and run through, uh, some teams through these \[00:34:42\] transitions. What are some important lessons you've learned as a \[00:34:45\] result of going through some of these big technology transformations and mo \[00:34:48\] moving a team through navigating that sort of broken \[00:34:51\] trust to regaining the trust, making a big technology choice, doing that \[00:34:54\] pivot? What are some things you've learned in terms of like \[00:34:57\] leading teams to be successful through these moments?\[00:35:00\]  \[00:35:01\] **Brian:** Yeah, the, \[00:35:03\] some of the big lessons on a big project like this is, you know, \[00:35:06\] building something that works is easy, right? Like \[00:35:09\] getting it to a functioning app that's. \[00:35:12\] Uh, that works is easy, but building something that's \[00:35:15\] production ready is hard. And so, \[00:35:18\] um, especially if you're replacing an, an existing app, right? Like \[00:35:21\] we had, we had 40 million users \[00:35:24\] and, you know, we had to, we had a lot of challenges, uh, because \[00:35:27\] you know, it's not a new app that we're just gonna launch in an app store. \[00:35:30\] We had to figure out how to. you know, the next \[00:35:33\] day they're, they're on a native iOS app and they \[00:35:36\] get an app store update and all of a sudden they've, they have a Flutter \[00:35:39\] app. You know, how do we not break everything, uh, \[00:35:42\] that they have, you know, stored locally on their, on their \[00:35:45\] device for, you know, around off tokens, things like that. So there \[00:35:48\] we had a lot of challenges around that. So while we got \[00:35:51\] something, uh, up and running quickly and, and, but \[00:35:54\] dialing in and getting it production ready for, for the \[00:35:57\] consumer, there's a lot of work between. yeah, it works to, \[00:36:00\] to, yeah. It's ready to, to be launched. And so, \[00:36:03\] that's really for us, a lesson that we \[00:36:06\] learned and, and something we already knew. But, you know, when you're working on a big \[00:36:09\] project, you, you're, you sometimes look \[00:36:12\] past that, but, um, just getting it ready for \[00:36:15\] production, there's a more work there than getting it built, to be quite \[00:36:18\] honest. \[00:36:18\] **David:** Yeah, I think people, uh, we, we like to \[00:36:21\] do the, the fun new stuff. Build a brand new app, you know, get \[00:36:24\] something new, fresh, but those, uh, transition \[00:36:27\] points and making sure you don't leave anybody hanging. It's, there's a \[00:36:30\] lot of details to, to manage there. What \[00:36:33\] advice would you give to, uh, other leaders or if there's someone else who's \[00:36:36\] hearing your story and be like, wow. This is a no brainer. \[00:36:39\] We gotta look into this. Um, what advice would you give to other \[00:36:42\] leaders that maybe would be about to kick off a similar, \[00:36:45\] similar story? \[00:36:46\] **Brian:** find some type of \[00:36:48\] supporter or ally within your, in your leadership, whether that's, \[00:36:51\] you know, your CTO or your CIO \[00:36:54\] or, or your CMO or whoever finds somebody \[00:36:57\] that. That you can get behind you on the project and, \[00:37:00\] and help evangelize the project, uh, with \[00:37:03\] you. you know, you're not always gonna get buy-in, right? Like, you're not always \[00:37:06\] gonna be able to go convince all the stakeholders that this is the right \[00:37:09\] thing to do. Be creative, kind of like we did and, and \[00:37:12\] try to find a way to get it started and, and, \[00:37:15\] get something tangible that you can show the, the \[00:37:18\] people that are skeptical and, and like I I, if \[00:37:21\] you can show them and they can get their hands on it and, and \[00:37:24\] see the value I. You know, you'll instantly turn a, a \[00:37:27\] naysayer into a sayer. So, that's my biggest advice \[00:37:30\] is, you know, you, you have to have some type of \[00:37:33\] buy-in and support from somewhere in your organization. \[00:37:36\] And, uh, if you can't get the, the type of buy-in and \[00:37:39\] funding that you need to just, you know, say, Hey, let's go do this.\[00:37:42\]  Let's go all in and do it. Try to be creative and, \[00:37:45\] and see if you can get some type of funding for a skunkworks project \[00:37:48\] and, and, and see what you can get going. \[00:37:49\] **David:** Hmm. That's \[00:37:51\] awesome. You mentioned a couple times about having the CTO partner like \[00:37:54\] that buy-in. Is there just a general like leadership \[00:37:57\] philosophy that you think makes a, a technically minded \[00:38:00\] company more likely to, uh, to be able to \[00:38:03\] embrace these things? Like what made it about your CTO even that \[00:38:06\] had the right mentality for you to go off and do \[00:38:09\] this? \[00:38:09\] **Brian:** you know, there was a lot of trust between myself and him \[00:38:12\] and, uh, we, you, we had worked together before, \[00:38:15\] so, um, there was a little bit of history there, which was \[00:38:18\] good. Uh, as well as, you \[00:38:21\] know, a good CTO will listen to their, to their \[00:38:24\] engineering leadership, right? So, He was a big user of the \[00:38:27\] app and saw a lot of the challenges and he, uh, as \[00:38:30\] we were rebuilding our engineering organization, you know, he was pretty \[00:38:33\] close to everything that was happening and, and seeing all the challenges \[00:38:36\] that my team was having. And so, uh, seeing \[00:38:39\] that and knowing that and, um, he \[00:38:42\] knew that, you know, like I said, we came before in road and it was either \[00:38:45\] refactor or rebuild. And so, um. \[00:38:48\] You know, because I had the, the \[00:38:51\] relationship with him and, and the trust, you know, when I said, let's \[00:38:54\] rebuild, he said, let's do it. And so, um, and then, so then we figured out a \[00:38:57\] way to do it to where we \[00:39:00\] could, kind of hedge a little bit, right? Because we didn't have full support and \[00:39:03\] we didn't have the funding to go pull all of our native \[00:39:06\] developers at that time and, and go rewrite it. We still had things we needed \[00:39:09\] to, to deliver for the, to the business. so we found a way \[00:39:12\] to do it, kind of, uh, skunk works. \[00:39:14\] **David:** That's awesome. \[00:39:15\] Yeah. That trust and that buy-in and that little bit of risk \[00:39:18\] tolerance is pretty important. Well, this has been awesome. I think the \[00:39:21\] Fluter community is gonna really value you telling your story here. And, \[00:39:24\] um, sort of last question I wanted to ask you is like, looking ahead, obviously \[00:39:27\] there's a lot of change going on right now in technology world. \[00:39:30\] What are some exciting technologies or, or \[00:39:33\] changes you're seeing in the landscape of technology these days?\[00:39:36\]  \[00:39:36\] **Brian:** I'm gonna give you a lame answer of ai. \[00:39:39\] Um, but, but in a different kind of, maybe a different, uh, \[00:39:42\] different perspective. I'm really excited \[00:39:45\] about AI as just to boost developer \[00:39:48\] productivity, um, kind of being your, your pairing partner, \[00:39:51\] right. To help. I don't see it nowhere near \[00:39:54\] anywhere ready to replace developers. Um, \[00:39:57\] I see software engineering as an art \[00:40:00\] computers are very hard with. Being cre creative. \[00:40:03\] Um, and so, I don't see it from, uh, \[00:40:06\] oh, hey, we're all gonna lose our jobs to, to ai, \[00:40:09\] but I really see the value in, um, having \[00:40:12\] an AI partner that can help, you know, sit with \[00:40:15\] you and pair with you and, and, and make \[00:40:18\] recommendations and, you know, help, throw out a bunch of boiler plate code \[00:40:21\] for you just to increase your productivity, uh, help \[00:40:24\] you with, you know, writing tests that cover, you know, what needs to \[00:40:27\] be covered, et cetera. So. That's what I'm really kind of \[00:40:30\] excited about is having a, an AI partner that helps \[00:40:33\] with the, with the pairing aspect. And, and if you think about \[00:40:36\] that, right, you know, uh, pair \[00:40:39\] programming has been around for a long time. Think about this. That's two \[00:40:42\] developers, right? Kind of working through the same \[00:40:45\] thing. What if you had they able to take, split those \[00:40:48\] people up? Pair with, with an ai right now you've \[00:40:51\] got double the productivity. You've got two developers working on different \[00:40:54\] things, but they still have a pairing partner to help with the second set of \[00:40:57\] eyes and, you know, some second, you know, uh, just, as a partner. So, yeah. \[00:40:59\] **David:** Oh, \[00:41:00\] that's awesome. I, yeah, it's obviously, when you say it now in \[00:41:03\] retrospect, I'm like, oh yeah, it is a paraprogramming and AI is \[00:41:06\] just a natural progression of that when you think about it makes a lot of sense. \[00:41:09\] Or a, like the rubber duck thing, you know, where you explain your problem to rubber \[00:41:12\] duck, sit it on your desk. It's a, a rubber duck that can talk back and \[00:41:15\] actually help you out, you know, I think  \[00:41:16\] **Brian:** I always joke \[00:41:18\] like, the reason I don't think AI is anywhere near ready to replace this \[00:41:21\] is, you know, when you're working with, uh, trying to get \[00:41:24\] requirements, we can't even get good requirements that humans can \[00:41:27\] really. Action on a lot of times. And so \[00:41:30\] imagine, you know, a for AI to be successful, you have to \[00:41:33\] have a really good set of requirements and be able to \[00:41:36\] tell it exactly what you need. And that's just really hard for human \[00:41:39\] humans to do, is to tell everybody what you know, to tell somebody what \[00:41:42\] they really need. It's really easy to tell somebody what you don't \[00:41:45\] like, but it's hard to tell people what you really need. And so until we \[00:41:48\] can get there, uh, it's, it's, we're we're just not \[00:41:51\] gonna be there with ai, in my opinion for a while. But, but as a, as a, \[00:41:54\] to help with productivity, I, I, I think it's exciting. \[00:41:56\] **David:** That \[00:41:57\] makes a lot of sense. And it's really all the things you were saying about flutter too. \[00:42:00\] I, I mean, AI's a progression of that, if you think about flutter as a \[00:42:03\] way to make your team more productive by having one code \[00:42:06\] base and simpler sort of things to interface with. \[00:42:09\] AI is just a natural step in that direction too. Making your team \[00:42:12\] more productive. \[00:42:12\] **Brian:** and that's really what I'm as, as you know, as an engineering \[00:42:15\] leader, that's what you're. That's what really what we're tasked with, right? How, \[00:42:18\] how can you make the team more productive, whether it's getting rid of some \[00:42:21\] processes, paperwork, you know, things that, that \[00:42:24\] slows them down and, and keeps them from like being, \[00:42:27\] you know, banging out code, which is what they're, what we really pay 'em \[00:42:30\] to do. So, um, so yeah. So I'm always looking at it \[00:42:33\] from the productivity perspective, \[00:42:34\] **David:** That's awesome. Yeah. It \[00:42:36\] connects with your thing there of like not trying to reduce the team, trying to, \[00:42:39\] you know, use the team. We have to do more. And so if you've \[00:42:42\] embraced it that way, it makes a lot of sense. So. Well, thank you so much. This has \[00:42:45\] been awesome. Really appreciate you telling the story of your \[00:42:48\] career, but also the experience at, uh, Wendy's, um, making this \[00:42:51\] transition to flutter and the benefits that come from it. Where can \[00:42:54\] people find out more about, uh, you and technology at \[00:42:57\] Wendy's? \[00:42:58\] **Brian:** Uh, yeah. So you can, uh, either go to \[00:43:00\] wendy's dot com. we post all of our, job \[00:43:03\] openings on, on wendy's dot com. And then for me, uh, I'm on \[00:43:06\] LinkedIn. Uh, you can find me at, Brian Abston. Just search for Brian \[00:43:09\] Abston. You'll find me. \[00:43:10\] **David:** Alright, fantastic. Well, I \[00:43:12\] can't thank you enough. This was awesome. Uh, again, stories \[00:43:15\] that, uh, we really want people to hear and, and see out in the world. 'cause \[00:43:18\] uh, you know, you're making out making a difference every single day with these \[00:43:21\] apps and driving business for Wendy's and having a great experience \[00:43:24\] for your team and your, your leadership and, uh, that's what this is all \[00:43:27\] about. \[00:43:27\] **Brian:** Yeah, I do, I do wanna, I do wanna add one thing. Uh, we, we, \[00:43:30\] we may or may not used, uh, some of these \[00:43:33\] tools called, uh, very good. just, just FYI. So, uh, \[00:43:36\] yeah. So we may have, we may have done a, \[00:43:39\] uh, the very first thing we did in Flutter may have been a very good, \[00:43:42\] uh, create using CLI. So just, uh, \[00:43:45\] I don't know if you know anything about those tools, but that's, that is what, uh, \[00:43:48\] we \[00:43:48\] **David:** I've heard about them somewhere. I'm I'll. I'll have to go ask the team. Maybe \[00:43:51\] they've heard of 'em. I don't know. \[00:43:52\] **Brian:** So, yeah, my team wanted to tell you thank you. \[00:43:54\] And for the contributions that, uh, your company has made to, to \[00:43:57\] flutter. And they were, they were super excited. I told a couple of folks \[00:44:00\] that I was gonna be doing this and they were like super excited to, \[00:44:03\] so, So we appreciate it. \[00:44:04\] **David:** Love to hear it. That's what this is all about. \[00:44:06\] It's all about raising the tide for all boats and um, you know, \[00:44:09\] getting the best practices and standards out and helping teams be \[00:44:12\] successful. So that's why it's awesome to have you come on here and, and \[00:44:15\] tell your story as well. 'cause I hope that you can go out and \[00:44:18\] inspire other companies, other leaders, other teams to do this \[00:44:21\] same work, um, as a result of the success you've had.\[00:44:24\]  Awesome. Thanks so much. \[00:44:27\] \[00:44:30\] \[00:44:33\] \[00:44:36\] \[00:44:39\] \[00:44:42\] \[00:44:45\] ### AI-Native Workflows for High-Impact Solutions Source: https://verygood.ventures/podcasts/dan-hou-eskridge-driving-high-impact-solutions-with-ai-native-workflows/ Dan has spent years identifying major shifts in technology. Now, he helps mid-market companies develop AI strategies that drive real impact. From improving internal workflows to reshaping user experiences, Dan breaks down practical approaches to AI adoption, key use cases, and why companies must act now to stay ahead. {% audio_embed url="https://player.cohostpodcasting.com/8525968d-fe9e-4bbf-b009-69851626560d/714f2595-7512-41f4-8a12-5e1e37124f09" /%} ## Transcript **Dan:** \[00:00:00\] you really want to have high confidence and conviction about the problem that you're working on. And I feel the same ethos applies here. Inside of your business, hey, we know there is inefficiency and problem areas in our sales teams and how they operate. let's go focus on that problem and iterate your way into it. **David:** Hi, I'm David, and this is Build to Succeed from Very Good Ventures. Today, we talk with Dan Ho, founder of A. I. Consultancy Eskridge. In this episode, Dan's going to tell us some very sound advice about how to get started with A. I., both for internal efficiency and productivity, and to improve your digital products. So, without further ado, Let's get into it. Alright. Hi Dan. Thanks for joining the Build to Succeed podcast.  **Dan:** Very excited to be here. I was saying this is, I don't think I've done a podcast before. I think it's my first podcast  **David:** Well, Let's uh, .  well why don't we start, why don't you just give a quick introduction. Who are you and what are you doing?  **Dan:** Yeah. My name is Dan. I live in DC. I am the co founder and partner at Eskridge, which is an A. I. consultancy that works with, mid \[00:01:00\] market companies to deploy A. I. and yeah, that's what I'm up to these days. **David:** So A. I., obviously a lot of people are talking about it, so uh, first question, gotta get it out of the way. Do you think we're building Skynet and will the Terminators take over the world? Or what does this mean for humanity?  **Dan:** I feel like we're, we gotta be past that question, right? Um,  **David:** Meaning it's already like, it's already going to happen. We already know that.  **Dan:** I don't think we're building Skynet. I don't think the, you know, the sort of apocalyptic nuclear war happening is, anywhere close to reality. I do think there is a significant possibility that over time as A. I. continues to evolve, it will dramatically impact the work that people do. In some way, shape, or form, I, I don't think it'll displace people, but it'll change what they do, absolutely. **David:** Well, so let's go back to your background. you know, obviously you're deeply experienced in technology and digital products given your, your career trajectory. So I think that's helpful context to how you got here. So I was wondering if you could give us a brief \[00:02:00\] overview of your career journey and ultimately what got you to being fully committed to A. I. at this current moment.  **Dan:** Yeah, I was thinking about how to, to cover this topic. I feel like you might find this funny. So, we were in undergrad together in this program called Management and Technology. And I don't know if you remember, David, we had to write an essay as a part of the college application. And in that essay, I swear, that's the reason I got into the program, because I don't think my, Creds were that, that strong. but I wrote about paradigm shifts and technology and how I really wanted to be a part of these waves. And I feel like, so I spent my whole career in tech, but really, I feel like I've been chasing different paradigm shifts. So I was at Microsoft early on, straight out of school, and I went to Motorola to work on the mobile wave. You could see it coming. I feel like I got the wave right. I chose the wrong company. At the time, Motorola was kind of the hotness. Uh, in hindsight, there was a new entrance to the market that, that I didn't foresee. And then I was at HUGE for many years, \[00:03:00\] and I got to be a part of like this tidal wave of user centric design and design thinking that was really exciting. **David:** and what is huge for people who maybe don't know it.  **Dan:** uh, yeah, so HUGE is a design and development agency, uhm, digital agency. It was one of the, one of the first major players to really embrace user centric design. and capabilities have kind of expanded and evolved over the years, but, similar to like a frog design or like an IDEO, I would say, RGA, in that vein. So I spent a long time there. most recently was at Amazon. and that's when I first encountered generative A. I. Uh, we were, uh, Using very early versions of stable diffusion to try to automate ad creative. And this was back in the days where you could see, you know, if you try to generate a person, there's like a hand hanging off the side of their face or some weird, you know, nightmarish thing that was kind of close, but clearly in, in the uncanny valley. but it was, um, you know, kind of a skunkworks project and only about. I don't know, a few hours a week, for me and, \[00:04:00\] a couple of years ago, I sort of had this realization. I think this is going to be big and I really want to do this full time. So that led to Eskridge. **David:** So tell us a little bit about that. What is, uh, what's Eskridge. up to?  **Dan:** Yeah. So Eskridge, uh, primarily works with mid market companies and we work with them to figure out, where and how, uh, A. I. can impact their business. I think a lot of, seasoned business leaders are trying to figure that out from themselves, and I think a lot of people are going to make mistakes along the way. Right now our focus is heavily on the internal operational efficiency side, primarily because that's where people's comfort levels are right now. more back office applications of A. I. where I have a person in the mix who's operating as a co pilot. Any errors or hallucinations you're not necessarily passing on directly to your customers. I think over time that will start to shift and as people get more comfortable with, I think you're going to start to see more user facing digital experiences that are very heavily leveraging, uh, large language \[00:05:00\] models. and I think there's a marketing application too. Okay, you're going to love this. Just last week, for the first time, I had somebody ping me who was like, Hey, I'm getting a couple business referrals. From chat GPT recommending my company and how do I boost the rankings? And there's a whole like new world of AI optimization that is barely scratched the surface of that is like the new search engine optimization. I think,  um,  **David:** totally. That happened to us recently. Someone on the marketing team said that, uh, somebody. was asking the chat GPT about Flutter. And, and  we showed up in it  And we were like, okay, how do we do more of  that?  **Dan:** Oh yeah, you know, I can't wait to monetize this, I'm sure. **David:** Well, and then there will be a whole battle in warp of like the GENAI companies trying to get around, the people who are gaming it, you know,  **Dan:** Yeah, yeah,  **David:** just like what Google has been doing for the last 20 years.  **Dan:** Exactly, exactly. Yeah, that's gonna be super interesting. **David:** Well, so obviously there's a lot of change here and it's, um, an extremely, \[00:06:00\] Topical, like very current thing. You said like back in the day, when stable diffusion was, but, when you say back in the day, That was like. Two or three years  ago. so, uh, that just shows you how quickly things are moving here. Um, there's kind of two angles I'd love to talk to you today about and tap into your knowledge and experience, given that you're working with companies and spending a lot of time thinking about this, you know, there's two angles and you, you hit on them, uh, there's two angles for A. I. One is I like to call it like the production advantage side of A. I. Like, how do we use it internally for more internal efficiency, things that give us a competitive advantage in terms of operations as a, you know, and then there's the more. Product experience side of A. I., of like, what is the, what is the digital tool, product, how do we integrate this into a product, and kind of like create value for our customers, like, a better experience, and they're oftentimes, I think, kind of looked at a similar set of tools, but kind of different business cases and expectations, Given you were saying, yeah. you've been spending a lot of time thinking about the like sort of internal efficiency production side of \[00:07:00\] things, maybe let's start there. Um, what are some of the most exciting ways you've been seeing A. I. sort of transform internal process and workflow?  **Dan:** I think it depends heavily on the team and, and the business, but there are certain companies that I've seen, and I'm working with some of them where A. I. could potentially completely upend how they work end to end. I'm working with a creative production company right now. And, um, as an example, as you can imagine, creating like banner ads or ad social media content for different sizes and formats like that is, is dramatically shifted and changed, through all of the AI capabilities. there's lots of companies out there as well that, um, rely on video content, you know, podcasts even expect to be significantly, uh, impacted over time. So there's a few different ones that I'm working with. I won't go too much into the details. There's a research firm that I'm working with doing lots of different forms of user research. I think that's ripe for disruption with A. I. But then \[00:08:00\] on the flip side, you look at these companies, for example, I was talking to, like a large fitness chain. And, you know, I don't think it's going to be that heavily impacted. There may be some back office things that may shift, like, you know, which is maybe like 5 percent of the business, but the fundamentals of what they're doing is not necessarily going to change over time. So I think it's very sector to sector. **David:** So if a company were to come in and sort of start to think about how am I going to use A. I. internally to improve our productivity or efficiency, what's the framework by which you start to break that problem down? Because I'm on some levels, kind of every team can be influenced or affected  by it. How do you start to like, tease it apart and zero in on the high impact areas and how to get started?  **Dan:** yeah, I think that's the key is identifying high impact. I just don't think of high impact as well as like, oh, you can define it a lot of ways. So high impact, it has a big implication on either cost or revenue, whether a lot of people are doing it or, or it directly drives revenue, like \[00:09:00\] sales activities, for example, can have an outsized impact. But we also look at it at a task level. You're looking for highly concentrated tasks, right? Cause the problem is if I save you five minutes here and a few minutes there, like with, with context switching and all of that, it doesn't actually impact your day to day that much. It has to be a pretty significant swath of work, that has to be obviated in order for, uh, at a business level, you to start to really see the benefits. So, Yeah, I think the concentration of it, the downstream, you know, financial implications, all of that has to be factored in.  And then the third piece of it is also just even what A. I. is good for,  **David:** Right, yeah.  **Dan:** right? It doesn't equally apply to all use cases, so. **David:** That's an interesting dynamic, and you and I have talked about that before, is um, the balance of people wanting to know what A. I. can do for them, but then the sort of business question of like, well, what kind of business result do you want A. I. to achieve for you? And this sort of infinite loop that happens there where I think business people \[00:10:00\] don't maybe understand the capabilities of the tools or how to take action with them. Uh, and they're looking for ideas, but then similarly, the tools can kind of do a lot of things. So it's like, well, what are you actually trying to achieve? How are you helping companies navigate that process?  **Dan:** It reminds me a bit of like, you know, anybody who studied UX is familiar with the paradox of choice. I feel like that's one of the challenges with A. I. is you can do so many things like the, the potentials are so vast. It's hard to really wrap your head around it. I do think there's an education component. I think you hit on it. Like the only way out of that loop is to educate yourself just a little bit. because even when I work with clients, I can bring in some perspective on what A. I. is good for or not. But somebody as in the client or somebody internally has to have that perspective of what are the challenges and unique problems this business has, you know, and you have to somehow bring the two of those together, in order to get at what you described. So, the easiest path is you just take some time out of your, I'm sure very busy \[00:11:00\] schedule and educate yourself a little bit on, um, on what it can do or what it can't do. The challenge that I found though, is, um, You can do that at, at like a point in time, but staying on top of like everything that's changing and like the capabilities are constantly evolving that, that's a bit of a tricky, tricky challenge, which is part of the reason why S Grid exists, to be perfectly honest. **David:** Well, and there's a constant pressure too, right? You have management or, you know, the board or leadership saying, Hey, what's our A. I. strategy? what are we doing? What products are we launching? What are we implementing? And it sounds like the strategy that's kind of the core of it is like, well, just start educating yourself and just experimenting and learning a little bit.  **Dan:** that has to be the start. And then very quickly, you want to pick, I think, one or two very high value impact areas and just go deep there and keep iterating and keep iterating. Um, You know, you're an entrepreneur. I had a SaaS startup previously. and you know, one of the guidelines that everybody follows is you pick a problem. You can \[00:12:00\] iterate within how you solve that problem, but you really want to have high confidence and conviction about the problem that you're working on. And I feel the same ethos applies here. Inside of your business, hey, we know there is inefficiency and problem areas in our sales teams and how they operate. let's go focus on that problem and iterate your way into it. **David:** Do you think a lot of companies have a good mental model right now of like the low hanging fruit, big problems that they can go after and solve? Is it. all just like chatbots and customer service and things like that? Or have there been any, um, like, are there any sort of like latent Opportunities that you see as big opportunity that that people haven't that aren't like the common thing people are looking into Just curious. What is the range of  of high impact areas you're seeing?  **Dan:** I think the short answer is yes. I think there's, here's the challenge is everybody wants to know like, Hey, this is like the use case, that everybody's doing, therefore I should do. And I feel like that's the wrong approach because most businesses are unique enough that, okay, we know customer service, for example, is a very \[00:13:00\] common application of A. I. but a lot of companies, especially mid market companies, your customer service operation isn't scaled enough where this is your top priority. it shouldn't be your top priority. so, It depends, but there have been some very interesting use cases. Um, I spoke with this one company, and this is a good example of where they weren't quite convinced, to move forward. they did mystery shopping. I think I might have mentioned them to you before, but, you know, mystery shopping, hey, you know, you're, you're the head of operations for Pizza Hut, and I want everybody who walks into Pizza Hut to be greeted a certain way. I want my, Servers to be dressed a certain way. The area needs to be clean and well lit. How do you enforce that or even measure that across thousands of different Pizza Hut locations, right? You have mystery shoppers, who are compensated for going in. They wear these little like, like hidden mics and hidden cameras. and, and it's all video footage and you record their experience. And a lot of these companies today, the way they operate, is they have armies of people who are \[00:14:00\] going down the checklist. Okay, were they wearing the uniform? Yes? Okay, check that box. And they're manually annotating these videos. and the multimodal models now are getting good enough that, I think 85 95 percent of that could be automated.  **David:** Just through the video feed that they're capturing and  **Dan:** Yeah, let's do the video feed, um, without anything else. Um, yeah, it's, it's remarkable. And again, those are the things that like, even if it's only like 85, 90 percent now, it's only going to get better. Um, and you should plan and make that assumption that it's going to continue to improve. **David:** So I have a question for you. How hard is it to do these things? Right? Um, how expensive is it to implement this stuff? What are the barriers for a company to actually do these things?  **Dan:** Man, I think a lot of the answer is I don't think it's that hard to get started at least I think it's very easy to do something. That's at least passable. It can be very very difficult to do something. That's exceptional But for most companies, I think passable is already a lot of value. I think the biggest challenge is You know, there's a \[00:15:00\] fear of A. I. that exists and hesitancy around embracing A. I. Whether that's because people are, you know, there's, you started off with the Skynet question, there's a bit of this like existential risk that people feel associated with A. I. and it's something new and novel. And I think the other challenge is it's, It's on everybody's list of things to do, but from what I've seen, a lot of companies, it tends to be the third or fourth thing on the priority list. so it's, it's in everybody's mind, but whether it's like the number one burning need that we have to solve in Q1, maybe, maybe not depends again on the industry. Yeah, but,  **David:** Do you find though, though, that there is a dis, I mean, I, I feel like there's somewhat of a disconnect still in terms of, it seems like these things and the promise is that it should be easy. Like I should just be able to go into like chat GPT and be like, change my business to be more efficient. And like, it magically does it. Right. but in reality, I think the, the, Maybe what we're getting at is that the technical implementations and the things that are there are reasonable to go after. It's not like you have to \[00:16:00\] like, you know, create a whole new cloud architecture or like, you know, like going from digital transformation where you have like a bunch of on prem servers and you're moving to cloud. Those are like big things. You're like moving servers. You're doing a lot of stuff. There's like the tools exist. There's a lot of good tools and you can kind of hook up and build on things that, are out there. To me, it seems like a lot of the barriers in the hard part, the expensive part is the process, right? Like is your data. Set up correctly, like even picking the right? tool. I mean, I've been looking at this, even for VGV, some of the things, and it's Like, well, where do you even start? I mean, there's so many options. There's so many tools. There's so many approaches. It's like, should I be doing, you know, something like, **Dan:** Like, if you were like, looking at just cloud storage, right? We all know cloud storage is like, oh, man, should I use GCP or AWS if you're just starting? Does it really matter? Like some people will murder me for saying that, but like, at some point you just need, you know, a server and where it's stored is like, okay, it could it be 5 percent better or 10 percent better if you made the right provider decision, but it's not that material compared to doing something \[00:17:00\] versus doing nothing. and I feel the same way about like, whether you're using like Cloud or Anthropic or using Pydantic or LangChain, like it kind of, something is better than nothing rather than obsessing over optimizing like the last 5%, **David:** yeah. I, although I do wonder if, um, large scale enterprises are sort of set up for that in terms of their decision making culture. Like my joke with A. I. is that despite all these new things that are coming out all the time, A. I., the A. I. wars will be won by the procurement teams of very large businesses. Like the same people who pick, we're going to use Microsoft Office and Microsoft Teams, or we're going to use like Google Workspace and Slack. Like whoever makes that decision for 10, 10 employees, the same person's going to be like, we're going to use Microsoft Copilot. We're going to use Chats GPT, right? and that's just like how business culture and decision making happens. So there's a bit of a. Thing that is at odds there, which is like, just get started, just experiment. But then you have this corporate culture of like, we have to have the right answer. That's going to scale for the next 10 years.  **Dan:** and what I'm seeing emerge at a lot of enterprises, and I've had a pilot \[00:18:00\] that kind of ran into a bit of a stumbling block with like, uh, you know, top five retailer. is there's these like internal A. I. review committees now that are, you know, it's like a committee from across, you know, fortune 50, and they're making decisions about what each individual department can or can't use. And on the 1 hand, you need governance, but the other hand, it's very hard to move quickly and experiment when you've got to go through that kind of rigmarole. So, yeah, I hear you on that.  **David:** Well, so how you're like, just get started. Right. Um, for the internal efficiency side of this stuff, the internal production and experimentation, how do you think teams should really think about integrating AI into their process? Because. The challenge is that you have existing workflows, like you still have a job to do. And so I think this is probably something a lot of internal teams are facing where the company and management and external pressure, you read LinkedIn, it's all about A. I., you read all the headlines. We have to do something, but I still have to do my job. And \[00:19:00\] how do I kind of start to experiment and do some of these things without disrupting my current workflows?  **Dan:** I have pretty strong opinions about this. Um, I think there's a path A and a path B and I'll talk through each one. So a lot of what I see what company is doing are path A, which is let's take our existing process, our existing workflows. We're going to apply A. I. tooling, and it could just be as simple as like Gemini built into Google docs or, you know, Office 365 copilot, or it could be something a little bit more, um, bespoke or, uh, novel. They're basically trying to optimize, uh, the current workflow and it ends up optimizing around the edges. I have yet to see examples where that has a very significant impact taking that approach. I think, in part because. It's, it's almost like, you know, that, old chestnut, the, however much time you're allocated is how long the work will take, even if it does drive some \[00:20:00\] efficiency, other things just kind of suck up oxygen in the room, right? Whereas I think path B, which I'm a much greater proponent of, is you take a separate team and you say, Hey, we're going to blow up the way that we work. The end outcome and the end output is the same, but we're going to assume, call it an A. I. native way of working. And we're going to build it from the ground up and it's going to have a different set of constraints and a different set of parameters. you almost, this is not the ideal analogy, I haven't come up with a better analogy. It's almost like you are, are putting a different box around the working process that is not really even possible to do. Whether that's a certain time to market, whether that's a certain velocity or output or a certain quality that you're able to achieve with like a smaller team, like whatever that is, you can kind of like create that and iterate on it in a small little vacuum. And then once \[00:21:00\] you fine tune that, and it's really humming scale it across the rest of the organization. **David:** So sort of like a tiger team approach, like start a little startup within the company.  **Dan:** I think so, because it just lets you kind of question all the assumptions you have, right? Um, there's, okay, you can talk about doing something faster or quicker with less, less people, but nothing's faster than not doing the thing in the first place. And so even though we're talking to A. I., I get enough into a lot of my clients where we're looking at process and There's a bunch of things that are happening today that I'm sure there were good reasons why they're happening, but in the cold light of day, you're like, yeah, we can just ditch that. Yeah, **David:** yeah, that makes sense. It's, you kind of, um, have to carve out people that are really thinking through the new problem.  We've even seen that with, uh, like Flutter adoption, right? Over, uh, over the years in terms of like companies that try to do it a little bit on the side versus actually carving off a team that really focuses on learning from it and truly \[00:22:00\] evaluating it. Um, cause it's too easy just to get sucked back into your day to day and competing interests of what you're going after. and the truth is that every company has a potential, um, Competitors starting like today, that is the future threat and the company starting today has the benefit of starting from a clean slate.  **Dan:** I think you have to have that mentality. Um, in an ideal world, you're like, cool, I'm an existing business. I've already got, you know, a brand reputation. I've got existing clients, you know, a lot of the things around it. and if you can bring A. I. native workflows and capabilities to bear, then that's how you stay on top of all the new entrants. **David:** So give me a quick, uh, um, overview. if somebody were, was interested in, they're like, okay, yeah, I hear you, Dan. Right. Uh, and I, everyone's talking about this and I got to do this. if you were to put someone on this path, right? What's like a three or six month plan look like for them? Like, how do you kind of like march them through a process to kind of start to do some of this stuff?  **Dan:** \[00:23:00\] Yeah, so I feel like all the main decision makers need to get a little bit educated. There's lots of great courses online. Andrew Ng, um, has a lot of free courses that I highly recommend specifically for business leaders, um, around A. I. or bring in somebody like an aspiration to do a workshop for your leadership team. And then I think you have to focus on, on developing a bit of a strategy, which is as simple as to say, what are those one to hide? Uh. And then once you have clarity on that, assign a team and go off and stand something up and experiment. Whether that means getting an off the shelf tool, whether that means using A. I. functionality that's built into your existing tooling. I do think that team needs to, like we discussed, Kind of start with a clean slate rather than, you know, iterate off of the current process. but yeah, that, that, that to me is like the clear path to starting to, to unlock value. **David:** Awesome. That's very cool. Well, let's \[00:24:00\] pivot just from a time perspective. I want to get back to, um, A. I. in the user experience. You have, um, obviously a lot of experience in your career working for, for major brands and in their products, digital products, their, their user experiences and stuff. How do you think A. I. is going to change the user experience? Um, how are you already seeing it reshape digital products and UX and where do you see it all going?  **Dan:** Yeah. I think there is a, a lot of change that we can expect. I think it's still very nascent. Like I said, when I look at most businesses, they're less focused on their digital external facing interfaces, but it'll come. I think it has to come. I think first of all, from a pure UX perspective, I think a chat interface in most instances is a bad experience. because, and I've seen this just even rolling tools out internally, you face a similar like paradox of, oh, what do I type in? Where do I even begin? You know, like nobody wants to learn prompt engineering. Like \[00:25:00\] that's, uh, I think it's, if you have to take a course. In order to learn how to use your product, you've done something horribly wrong, right? From a, from a pure usability UX challenge. I think there's a lot of lessons learned from, um, Alexa, actually, you know, if, if you remember, um, cause, cause to me, it was a similar product experience of open ended conversation, With the device, right? whether it was through voice or through typing and the early versions of Alexa, all the Echo devices were just these little dots, right? That all it had was a bunch of microphones and speakers and future permutations of it, they started adding screens to them because I think the team realized rightly so that there's a lot of information and interaction models, the old UI way that are just more efficient and better. For certain use cases, then open ended, uh, voice or speech or what have you. So I think there's going to be some, evolution of more traditional, what we think of as UX and \[00:26:00\] UI and chat and speech on top of it. Uh, I, I really like, uh, the closest one that I've seen, I feel like, um, is actually cursors interface. Where you got that kind of co pilot thing going, but the transition between the two feels really smooth. I could see some permutation of that working for like e commerce, for example, like complex, um, high consideration purchases. yeah, but, but I think it's still evolving. **David:** The chat thing is interesting because what I find about the chat interfaces is first off, the types of customer service experiences you're used to having with like a chat thing. Everyone hates those. No one likes to like go to a little website and chat with customer service. So you already have like a bad mental model of that, even though the experience could be better because the information could be better and faster. But then also the other thing is like, The way we chat, the way we use iMessage to chat and communicate with our friends or family, or the way we use Slack. Like if I look at my Slack channels, they don't resemble my chat GPT conversations at all in terms \[00:27:00\] of format, right? They're usually much shorter, quick little snippets of thoughts and ideas. And so it's interesting that this main thing we do with chat, It, doesn't really resemble any other types of chats we're having when you think about it, So that's a little bit strange to me. **Dan:** and this is where I struggle with it. It starts to look like coding. Like when you look at like prompt engineering, you're like, okay, I'm going to give you a step by step reasoning, you know, chain to follow. And it's like, well, that's just writing pseudocode at a certain point. And, and, you know, a lawyer or an accountant shouldn't have to learn coding, you know, and engineering, um, uh, mindsets in order to use, uh, use LLMs. I think the other facet, you know, we talk about Flutter and mobile. I think I'm expecting multimodal capabilities to really unlock a lot in a mobile context in particular. I don't know how, I don't know exactly how that's going to shape brand experiences, but it just feels so powerful to, in real time, have your camera on pointing at \[00:28:00\] whatever you're looking at, you know, or be able to just converse, and have that translate into and influence some sort of brand experience feels, feels really important and really powerful. **David:** Yeah, I think, um, when you think about A. I., We spend a lot of time talking about like how to engage with the ai, like how do I talk to the LLM or how do I trigger an image to be generated? Like, that's a lot of the UX question. I kind of feel like where we haven't even really scratched the surface is like, how does AI actually dynamically influence UI so that the UI and user experience you're having is, is, is not even a, an AI experience, it's just that somehow AI is like influencing what you're doing. **Dan:** I want, you know what I want? I want like the Matrix, that scene in the Matrix where he's like I need guns, and it's like, whoosh, like, the, the rack of all the guns, you know, come through, like, I'm imagining some version of, like, e commerce thing, where, like, show me tents, no, no, no, I want sleeping bags,  just, just shows you, okay, now I just want the red sleeping bags, and, like, it feels, something like that \[00:29:00\] feels like it should happen at  **David:** Yeah. I mean, some of the better experiences I've had with A. I. recently are around sort of this new version of search. Where, well, like for instance, we just added Slack A. I. to our org. I resisted it for a long time. I still have a lot of comments I'll reserve for, for another time. I don't want to, I will say that it's, um, not necessarily making me feel super confident about A. I. investments, but, um, You know, the search is interesting, because a lot of search in the past was just like looking up matches, essentially, right? Like, I'm gonna go search my database for, uh, an instance of these characters in a row, or something close. Fuzzy search, things like that. The search stuff is cool, because it'll try you can like, ask it a more open ended question, and it'll try to go back go find stuff, and like, pull it together in an interesting way. I think that's an interesting novel. thing we're seeing. And, and to your point. of like searching for products on Amazon one day, and you're just like, okay, yeah, I need a, I need a sleeping bag. And it's like, no, no, no, I need a kid's size sleeping bag. And just a way to kind of like, uh, search through stuff. Um, I think that seems pretty, pretty \[00:30:00\] compelling or like, you know, uh, even being able to parse it. Give me certain sleeping bags where, you know, people with really long legs don't complain about it in the reviews or something. Like I could see  **Dan:** Something that is too nuanced to fit in like a traditional facet in nav, right? But you know, exists somewhere in the review data or something like,  **David:** Yeah.  **Dan:** um, it's definitely, yeah, that, that would be a dream for sure. **David:** Are there things that you've observed or you're feeling in terms of like A. I. driven like interactions and UX like that? What are, are there other areas that we're just scratching the surface that are like big, unsolvable problems right now that we're trying to work through?  **Dan:** Oh, man. Okay. So there is one that's been lurking in the back of my head that I keep encountering. It's around this idea that we as users of technology have. Everybody is used to this deterministic software world. When I click the button, this is what happens. And if I don't click the button, that's what happens. And just, A. I. fundamentally doesn't work that way, right? It's not deterministic. and and \[00:31:00\] I think there is something around user expectations and managing those user expectations that we have not figured out yet. and I don't know what the solve is, whether it's just like people get so accustomed to it, they're used to seeing either hallucinations or, Hey, I'm okay with this thing being correct 95 percent of the time. Um, but right now expectations are if, if it's on my laptop and it's giving me something, I expect that to be accurate a hundred percent of the time, um, or exactly what I want to see. my, uh, oldest daughter is 11 and she's writing this book for school. And she's trying to generate, using, um, Dali, an image of the main character, right? And she's like, no, no, no, she has pigtails. And I could see her like yelling at Dali to be like, no, that's not what I meant. It should be like one big braid, not two small braids. And there's this, you can just see the struggle with like, I have in my head what I want, and this isn't doing what I want. And again, with deterministic software, you would either say, yep, it does that or no, it doesn't. It's \[00:32:00\] very black and white. I know what to expect. Whereas I feel like with, with A. I., it's a bit, it's, it's more gray by definition. **David:** Yeah. you're right. We get like a toolkit now in our apps. Like if you think about using a Figma or something like that, you go and there's like a paintbrush tool and a color picker and stuff like that. But now it switches to like, it's not, you're not limited to the tools. Cause you can kind of just ask it to do whatever you want. I do think the kids thing is interesting. You know, for me, uh, watching my kids use chat GPT. For them, it's so natural and it's almost like, uh, they're like, I'm like, isn't this amazing? They're like, no, I just expect that this would exist, you know, it's just seems very natural and obvious to them. Whereas we're all like, this is amazing. Like what you're doing. and I also think the interesting fun thing about is like the, the shortening the loop of like an idea to something kind of real. Like my daughter followed one of the prompts that chat GPT gives you. And it was just like, I want to open like a sourdough bakery. And she, we found it like a couple of days later and I was like, Oh my God, what happened? And she like made a bakery, like she made a menu. She like did the branding. She made a 3d \[00:33:00\] rendering of it like on the street, uh, this little town near our house. And it was just really amazing to see that creative energy. Like you could take what normally would have been just a very service level idea, probably never would have followed up on it or done anything. And within five minutes, she had like kind of seen a vision come to life. It's just amazing to think that the acceleration of creativity and ideas that will come from that.  **Dan:** Which is, which is really, really cool, right? I think that's, um, so cool to unfold. we're not opening up bakeries in this household quite yet, so have to chat with my daughter about it. **David:** Well, in that case, uh, it. was just ChatGPT's prompt, right? Like one of the, and that's interesting too, because then you can very quickly go down a rabbit hole of something totally unexpected. And you have this sort of, uh, this helper that can kind of push your thinking, which is really cool. I think for the kids, it's going to be, I mean, for them, this is all just going to be very natural. So, uh, that's a good question though. Like, let's, let's think about that from a, uh, some of these kids who are going to be growing up. They're going to get the benefit where, you know, Five years from now, 10 years from now, \[00:34:00\] a lot of this stuff's going to get figured out, or the titans will have been sort of established, let's say. I mean, you can maybe even say some of them already have, but what's your advice to companies around picking some of these technologies right now? Um, Like, how do you evaluate? Do you just do chat GPT? Do you do Gemini? Do you Anthropic? Like, do you do them all? Um, what's when it, when it comes to generative AI, I guess, specifically, what's your guidance for helping people pick?  **Dan:** I think what you're going to start to see, I actually just published a piece on this. I probably should have elaborated this more, but like the actual foundation models are getting commoditized really fast, right? The prices are dropping, like in the last year, you know, down 30x, something outrageous. Um, and they're going to continue to fall and approach the price of electricity. and so what I think you're going to start to see is the different providers start to differentiate themselves, just like any brand would. And, uh, you know, Around certain vectors that should make it a little more obvious. Which one is \[00:35:00\] right for your particular use case. So, for example, even today, there's only one model that will have a 2 million, you know, token context window. Therefore, you can jam large videos into it, and that's Gemini, right? So, That mystery shopping use case I was talking about, like, uh, doing video annotation, it, it becomes Gemini is a clear choice, for coding applications. I feel like Claude has already kind of made a name for itself and in part due to its partnership with cursor. Right? So, so I think you're going to see. A further pushing apart of these different models or for specific use cases, um, cloud isn't connected to the internet, um, by default, whereas chat GPT is right. So for applications where you do want Internet search baked into it, you want to deal with, like, calling a search A. P. I. separately. You just want to baked in. Then yeah, chat GPT is great. So, I think over time, you're going to see, see more of that. **David:** So some consolidation around just the standards and the models will ultimately become somewhat consistent and standardized. It's really the layers on top. \[00:36:00\]  What about, um, uh, some of the non technical considerations people face with some of this stuff? Um, we get a lot of pushback, or there's a lot of fear, right, from A. I., and privacy, you know, um, leakage of confidential information. Uh, we've had some clients that, you know, say, Hey, you're not allowed to use any A. I. tools because we're just not sure. There's the whole, like, lawyer side of it. The, the legal teams are not, not quite sure what's going on. how, how do companies navigate that?  **Dan:** I don't think there is a silver bullet beyond what you just described. I think, uhm, You know, a lot of the different providers, they'll have, you know, pretty clear now terms of use, you know, associated with different tiers of payment. So, if you have an enterprise license, you get, uh, much, much better terms and have liability covered. Um, I do think it just comes down to risk tolerance and liability, and I think it's going to be 1 of those things that evolves over time as everybody \[00:37:00\] collectively gets more comfortable with it. Uh, just. And like the broader public consciousness, um, that then company comfort level, we'll go up with it. I, there is however, like different, um, I spent a lot of time talking to lawyers about this, surprisingly, I, I got into this cause I thought the engineering was cool. Then I ended up talking to a lot of lawyers. and it is also by application and vertical, right? So for example, in coding applications. You know, in part due to, I think, a culture of open source and like people putting their stuff up publicly on GitHub or Stack Overflow or what have you. It's not a very like litigious environment. It's not a very litigious use case. So if you actually look at the risk of like using A. I. for coding applications, it's quite low. However, if you go over and you look at, um, um, art generation and image generation, uh, the visual art community historically has been much \[00:38:00\] more litigious than the engineering community, right? So if you're going to start using A. I. generated images, a good lawyer would indicate that there's more risk associated with that. Doesn't mean you can't use it, but you should just be aware of the risk that you're taking on. So if you look at Firefly, I think half their value prop, Adobe's value prop for Firefly image generation is a pure liability play. **David:** Hmm. Interesting. Just eliminating that risk for people.  **Dan:** exactly. Yep. **David:** Wow. Cool. Well, let's, let's wrap this up a little bit. And, uh, one of the things I want to do is connect the dots in your experience now that you're kind of focused on A. I. and where things are going and having, some amount of time behind You in terms of helping companies develop digital product experiences all the way from the Motorola, right, uh, through the, the, the mobile boom and everything else there. When you look back on like what connect or what helped companies to be successful in the prior eras, now we're embarking on this new A. I. era. What are the things you think that are going to be consistent across teams and companies that are successful? \[00:39:00\] Moving forward, um, based on your experience in the past, like the, the sort of underlying attributes that help companies succeed. **Dan:** You know, on the one hand it's really different, I think, but on the other hand, I think it's. And there's a lot of differences that we talked about just now, you know, expectations and the nuance of it. But in terms of leveraging it successfully, it feels like a lot, it feels very similar to the previous rounds of digital transformation, right? Like you need teams that are inherently curious about the new thing. Um, you need teams that are, that thrive in ambiguity and are willing to embrace something new, um, and try new things. Which is easier said than done, um, but I think that's true here. And then the third piece of it is like having this clear focus on either the end user or the end outcome that you're going for and not getting caught up in kind of shiny object. Um, I think those things still hold true. Uh, the, the nuances of how you \[00:40:00\] deploy A. I. and how the technology works, I know it's not deterministic. Like, yes, there's some slight differences there, but the philosophy and how a team should approach it. I don't think. Is any different than previous waves of technology. **David:** There's always seems to be a little bit more fear and existential crisis to this one than past waves. I feel like, you know, if you think about it, what you're saying is totally true because we went through the first. com, right? You have all these, you know, brick and mortar stores and. You know, uh, uh, like figuring out how to get online, sell their products online, and then you have the mobile boom and everyone's got to figure that you got cloud stuff where you got to figure out how to move your data center to the cloud and all these different waves of transformation. This one, the A. I. one seems to have a little bit more fear of like just jobs being eliminated. Companies just going out of business overnight because A. I. just makes them irrelevant. When you think about it, the New York Times is still around and people aren't really reading newspapers that much, you know what I mean?  **Dan:** okay, but hold on. If you take a step back, I agree with you. And that's created a lot of hesitancy to embrace A. I. \[00:41:00\] But, the irony is, if you truly believe that, that A. I. is going to make a bunch of things obsolete. You would be in an even bigger rush to embrace it, if you think about it, not then, then, then the other way around, right? Like, you should have even more urgency about it than somebody who doesn't have that concern. that, that, that's, there, there's a bit of a dichotomy there, I think. **David:** Hmm. Yeah, that's a good point. Yeah. Well, so to that end, um, as just a sort of parting thought, like for an executive out there, you've already given us some advice on this front. Um, but maybe there's a company who's been dragging their feet and hasn't really quite figured out a way to, to get started. What's your sort of like quickest advice to get them going? What would you say they should do first?  **Dan:** I mean, I think everybody already has this, but if you haven't already, just use it for yourself. That's like the simplest starting point, is just to try it out for yourself and find one or two small use cases to get it going. And then if you're past that point, get your team to do it. And if you're past that point, then start thinking about, um, strategic \[00:42:00\] implications and, and high value use cases. we're getting old enough where I think about like fitness, you know, and, I've done this to myself many times where I'm like, all right, I'm going to go out and train and I go from zero to 60 and then I injure myself. You know, I think a lot of the smart times just, just go out and do something. Run half a mile, run one mile, do 10 pushups. I don't know, do something very simple. It'll build on itself over time. And this technology is not going away. You know, it's, it's, you're not going to nail it out of the park. It's going to continue to evolve, but I think it's more of the habit of starting to use it and, and getting comfortable with it, that matters more than anything else. **David:** That's very good advice. Well, thanks, Dan. Um, this has been awesome. If, uh, anybody out there listens to this and wants to find out more about you and what you're doing, where can they find you?  **Dan:** Eskridge. co, E S K Ridge. co online. Um, and find me on LinkedIn.  **David:** Well, there will be some, uh, links in the comments. So, thanks a ton, Dan. It was nice having you on here. \[00:43:00\] Learned a lot today. And, uh, thanks for unpacking what is really complicated for a lot of people right now. and there's a lot more to learn. So, maybe we'll have you on, uh, a year from now when  **Dan:** Oh yeah. And we can talk about what's changed. Yeah, sounds good.  **David:** Alright ### Design and Technology in Modern Products Source: https://verygood.ventures/podcasts/patricia-roller-the-intersection-of-design-and-technology/ On today’s episode, we’re joined by Patricia Roller, former co-CEO of Frog Design and current CEO of Vidlet Inc., a company known for pioneering video research in product development. With a rich background in design and leadership, Patricia shares her insights on the critical role of company culture in driving innovation and success within startups. {% audio_embed url="https://player.cohostpodcasting.com/70befda6-c7c5-4d93-b01e-90004648685f/800824b1-8052-4d96-989e-b4a68c04735d" /%} ## Transcript **David:** \[00:00:00\] Hi, welcome to Build to Succeed from Very Good Ventures. Today, we're talking with Patricia Roller, founder of Vidlet and former co CEO of story design firm, Frog Design. In this episode, we talk about the role of insights and culture to successful companies and products. I hope you enjoy our discussion. And if you do, please take a moment to rate us or give us a like on one of your favorite podcast platforms. now we'll get \[00:01:00\] \[00:02:00\] into Hi, Patricia,  **Patricia:** Hi, David. **David:** you. so  **Patricia:** Good seeing you. You just came back from vacation and it shows. **David:** Thanks. Thanks. Um, yeah, we had a nice one. It was good. It was a good little trip. Um, always got to take those moments with the family. So, um, well, thanks so much for coming on. I appreciate you joining our new podcast. Um, you've been a hero of mine for a long time in terms of what you've done in your career and our paths have interestingly been connected, um, both through our mutual, \[00:03:00\] well, Different, but mutual experience at Frog. Um, and then also now by Flutter and work that's happened there. So, it's just an honor to speak with you and thank you for making some time in your day to come chat. **Patricia:** Same here. Thank you for having me. Um, I'm always nervous about these things, but, um, I saw the one, uh, episode that you posted on LinkedIn. You're very good at this. **David:** Well, it's the, it's the guests that matter. So, let's maybe take that as a transition and do you want to just introduce yourself and maybe start by telling us a little bit of your background? **Patricia:** Yes. I'm Patricia Roller. Um, yeah. As you already mentioned, David, um, we share, even though we never worked together directly, right? Share a frog history. I was co CEO and helped, um, to sell the company. And as you know that that was a very special place. Very creative people. So, \[00:04:00\] this was, F. R. O. G. was my first job. Helped the company move from, uh, Germany to the US when Steve Jobs decided to hire a design firm in the early eighties, um, then ran the company for about 20 years, sold the business and then became something that probably everyone that has an exit at one point does. I became a startup investor with a group, um, enjoyed it very much. But then at age 55, I was like, okay, am I going to retire now for good? Um, and I decided to start yet another company that does, um, design research using video, and that's what I do today. And that's why we built a Flutter app, another connection we have, right? Frog and Flutter, FNF, and then you are Very Good Ventures and I'm Witlet. So I think we. We hang out in the same letters of the alphabet. Um, but yeah, so \[00:05:00\] we built a mobile app, uh, and we called people in their environment to inform better product decisions. **David:** Yeah, so that's VidLit, and so can you maybe expand on VidLit, um, what the company does and what, you know, what a client would come to VidLit for, and maybe also what led you to start VidLit in the first place? **Patricia:** So I'll start with the ladder with why I started Witlet. So I fell in love with video as an investor first. Um, the company was actually, or the platform was built by a guy named Karen Farr. Um, Um, who's really an expert in video. And as most people probably don't know, video is a very, very hard medium to manage. We had the conversation earlier with your Aiden, right? When he set us up here, still very difficult, but, um, I was an investor in VidCast and just fell in love with video because you can express so much more when you see it. Um, there is a saying \[00:06:00\] that one frame of video says more than three pages of text. And so I thought that this would be great for us to record people, um, get their insights on a product experience and then take those insights and make changes in innovation. Um, so traditionally at Frog, we would go out and visit people in their home and do these kinds of interviews, design research interviews that inform better product decisions. But I just think if you leave people alone Without hovering over them, especially when they talk about sensitive subjects. It's just so much richer. And also not just asking people what is an experience like. I think most of us are not trained to criticize a product experience. Even though when you go throughout your day, you probably stumble so many times. Whenever you interact, especially with \[00:07:00\] technology, right? It's But we are not trained to express that. We accept technology for what it is. And so that's why we are observing people, um, and then see those pain points. Um, I'll give you an example. We did something with, uh, a ride sharing company. That was probably already like eight years ago. So very early in RedLit. And whenever as users, we fall in love with a new business model and ride sharing was very new. We love that. And we just love that it exists. So when we ask people, how do you feel about ride sharing? They couldn't express any pain points because it just was so cool and so new. And I can call the con and you live in New York, you know what it used to be like hailing down the cab, right? Um, and, but we observe people in the, while they were taking a ride, ordering a ride, and it was incredibly painful at the time. You might not even remember that, but you would often find yourself on the wrong side of the street.\[00:08:00\]  You couldn't really predict when the car actually arrives. So all these pain points that people would not express, but that you would absolutely see when they were recording themselves, **David:** hm, **Patricia:** so that's what Wittler does. Um, record partly, you and I worked on something together on, um, a retail app, right, um, and you probably remember that video. **David:** mm hm. Yeah, no, a hundred percent. It's the, uh, extension of design research, right? Um, which Frog and companies like IDEO and other early innovators in the design, human, you know, factors, human design, um, space really helped to innovate. And, you know, building on focus groups, right? Which everyone, I think these days, I think is a good understanding of there's a lot of group think that happens there. You're not going to get that insight and you've taken it a step further, which is I want to get that. That experience alone, right, where I'm not pressured by the interviewer or having someone else there. Um, and I guess that probably wouldn't have been too easy to do technology wise \[00:09:00\] back when Frog started or even when I was there. I don't think we had the tools or technology maybe to do some of those things back then. **Patricia:** Well, I think, but that bridges what I do now with, with frog, because I did go to frog first. Um, when I started with that, um, because frog in my mind. was always breaking things with new technology. And as you know, now that you run your own company, making those changes is very hard because you have to invest in them. You have to decide, like, this is something my customers are moving in that direction. I need to do this. For example, my very first experience of frog doing that was in the 80s when product design became more integrated with engineering. And so we as designers had to embrace engineering culturally. That is very, very different people. You notice in, in the software you develop, right? So we were one of the first design companies buying CAD systems. \[00:10:00\] Um, big, big computers at the time that filled up a whole room, needed extra air conditioning. So that was a big change that not everyone always embraced. And if you look at Frog's history, that's what Frog always did. Like, if something works really well, let's break it. And so I think Frog should have honestly done Whitlet for design research because design research was way too expensive. And very highbrow, right? Certain people just riding out there, uh, seeing these experiences and then you had to believe them what they brought back. And I think we need to be much more transparent. And for me doing design research with video is very democratic. So, **David:** you have to break them or you want to break them. That's, that's, uh, I think a really interesting statement. I think a lot of companies don't really focus on the things that they're doing well to break. They're trying to fix the things that are broken. But I think \[00:11:00\] that's an interesting perspective about how do you stay on the, on the forefront, right? How do you continue to innovate and evolve as a company to get better and better insight and better, better data and better, **Patricia:** yeah, because if something works, um, someone will copy it and commoditize it. And you, it's sometimes shocking how companies don't evolve faster because it's so comfortable to just live with what you're used to. I mean, Frog at one point had this saying, change is fun. I'm not sure if that was still in your days, but people didn't always feel that change is fun. Um, because you're bringing in new people with new skill sets that challenge your thinking. And this was very obvious when Frog moved from product to what we called a convergent experience at the time, right? The digital experience. I was very uncomfortable because all of a sudden there were the new cool kids on the block.\[00:12:00\]  **David:** Well, that's, that's an interesting part of Frog's story. And I think even your evolution into Vidlet, right? The continuation of that is. Frog and Doord, or as it continues to for 50 years, something, right? And the amount of change that happened in the marketplace and in the world over that period of time, like, yeah, getting into different technologies, the internet, early. com boom, you know, moving into services and service design, mobile experiences. And what do you think enabled? In the early days, and then as Frog continued to evolve with that, to keep that mentality of fixing the things that work or breaking the things that work well to continue to improve. **Patricia:** I would say in the early days were very much driven by Hartmut. Um, who's my husband. Um, and he's just very good at taking risks. Uh, he sleeps well at night when he takes risks, whereas most people don't. Uh, I see this now at \[00:13:00\] Witlett when, you know, you have a bad month and you need to still invest in new technology. It's, it's very hard to do. You know? So I think, um, having someone that takes risk and then I think at one point it becomes a muscle and it becomes part of your culture and then it becomes more sustainable. That's why I think Frog is still around. We sold the company almost 20 years ago. It still has a strong brand name. It's now part of Capgemini, so we'll see how well it will do, um, in a big environment like that. Many design groups that became part of the bigger consultant group Consulting groups are probably regretting it, um, because it is a very, very different culture, but I personally believe in that. That's what, that's how I try to run Witlet. Witlet is very small, but, um, make it about, I don't know, make mistakes, um, learn from them \[00:14:00\]  **David:** So at Frog, you know, you were A consultant, right? Where consultants were working with other, these brands. And you've, you've kind of done the three, like kind of the, the trinity of different types of work, uh, as an investor, as a consultant, and as a product owner. Um, how is the product owner experience different from those other roles as an investor, as a consultant, when you know, you are focused on this one business, this one technology stack, the tools that you've got, uh, how has that been different for you? **Patricia:** Well, and, and I'm not sure if you know this, but frog, at one point in the eighties, uh, build a computer on entertainment computer that was probably one of the best machines in terms of, um, computer power, computing power at the time. Uh, we plugged a lot of, um, the best people out of Apple. Andy, he filtered the user interface and we failed. Um, we failed at the time because. \[00:15:00\] The content was not there. So I think as a product owner, you deal with a lot more complexity. Um, you have to drink your own Kool Aid, right? Um, and I mean, you would say looking at our app, that it's probably not that great by your standard. This is what I would argue, but we pay people to use the app. So we can, um, maybe make some shortcuts. And those are decisions that you have to make. Um, for me, it was very challenging working with developers. I, um, like I said, I inherited this company in a way when, um, Vidcast, um, ran out of funding. I bought a platform. All of a sudden I had a team of developers and I will never forget the early days. We, we had daily standup, um, in San Francisco, an office in San Francisco. And, um, they would tell me like, things are working. And I would say, but customers can't use it. \[00:16:00\] So technically it was working. So I learned to speak, develop or speak a little bit. Working and working can be two very, very different things. But you have to make compromises, right? You cannot tinker around forever. You have to launch a product at one point. And I think that's probably the biggest difference of, um, helping companies create new products from a design perspective And building a product. **David:** Definitely, I can relate to that. Makes a lot of sense. As a consultant, your job is to give the best, you know, recommendations. The best solutions. You're there to provide the best results that you can. You know, and as a product owner, you're the backstop for everything. You, you're the source of the funding. You have to make those hard decisions and prioritizations. Um, has your experience as a consultant and as an investor helped you in terms of making some of those prioritizations? **Patricia:** Well, as an \[00:17:00\] investor, I learned that, um, I really don't have decision power. Um, once you have made the investment, you have to trust the team that's building a product. And that's probably also the reason why I wanted to run a company again. Um, but it has definitely helped me the way investors think about it and why. Ideally now, I don't want to take a lot of money from an investor. We, Whitcaster had funding rounds. We did a small one early on, um, because I think it's a responsibility, um, when you take money in, but it did teach me that, um, the most important thing when you have a company is you need to sell your product. Um, right. I mean, startups often focus a lot on. What PR do I have? How many clicks do I have? How many likes do I have? And I think what I learned from the investors is none of that matters. Um, at the end of the day, you need to make \[00:18:00\] money. **David:** I totally agree. It's, um, I used to tell, especially startup clients that building it is the easy part. Everyone puts a lot of emphasis on what's the concept, what's our business idea, our MVP, are we building scalable technology? And there's a lot of emphasis on that. And it's the, if you build it, they will come mentality. And I would always say building is the easy part. You can, you know, Pay me or any number of firms or, or hire people to build the thing. But if you can't sell it, if you can't get an audience, if you can't get a customer base and have people that are really enthusiastic about your product and want to use it, you don't have a business. Um, and I think there's a, especially in the startup world, there's a lot of emphasis on, on the technology, uh, cool new things that are happening. What is our product? And I think some companies spend a little bit too late in the game. They started thinking they too late to the game. They started thinking about how do we actually sell this, right? How do we, how do we actually market it and build a customer base? That's really into this. Um, \[00:19:00\] and, uh, that's the hard part, getting people to use your app in the first place, getting customers. **Patricia:** And startups often are founded by people that want to solve a problem that they have experienced. You see this often in healthcare, right? Um, my dad suffered from something, so I want to solve this problem. And then you learn that You need to sell this to a lot of people, not just your dad. Uhm, so you need to change it. Witlet had to change a couple of times. That we now do Zoom interviews and not just mobile interviews and combine them and build a platform where we can combine them was a change that I resisted for quite some time. And I think that early change is so important. They go like, okay, where is my market? Actually, it's not just me and my friends. Um, I need to sell this to a lot of people. And that is very, very hard to do. I mean, almost every startup that I have seen had to switch at one point a little bit. **David:** Have you \[00:20:00\] had to, uh, have you had that same experience at VidLit as your, how was your target market changed over the years? **Patricia:** So in the beginning we were definitely, so I only knew research out of frog, right? Design research. And then for a while, we actually called ourselves a market research company. So we talked to a lot of insights people and market research people. And I learned that we speak a completely different language. Um, so what I learned is I need to talk to the right people that have innovation problems and not people that have marketing problems. Completely different. So A. I., who we talked to, and as I already mentioned, that the, the Build a broader platform that can also do deeper interviews on Zoom or maybe we do a follow along, uhm, and go out in the field even though we rarely do that. So that's something I have to change. Think about who my customer is. integrate more product into the offering. Um, we are now also moving to \[00:21:00\] like very early concepting because our customers often could not just live with the insight. They would see the pain points. They would see what was wrong, but they couldn't translate that into new ideas. So more and more, do we have to do that? That's not something I wanted to do. I didn't want to build another frog, right? I didn't want to go into design consulting, but now we Kind of bordering a little bit, just touching on a little bit where we take the insights and help our customers to translate it. So I've become less of a technology platform and again, a services business, whereas before I, if anyone would have just taken the platform the way it is and used it on their own, I would have been very happy. I wanted a SaaS business and turned out that I need to change that. **David:** Yeah, I think SAS businesses inevitably maybe run, depending on what they're doing, of course, but inevitably do run into some need for services of some type. When I think of even in \[00:22:00\] our ecosystem, all the SAS tools that are out there around a mobile app, whether it's an amplitude or a braise or. Those types of tools you would add to your experience. There's still work to understand how to make sense of those tools, how to set them up, how to get insights out of them and act on it. And I think all the tools still require that strategic thinking, that alignment. Um, so that's interesting that the progression kind of getting back to that early, early, uh, experience of helping the client section make sense of the insights. That's, that's very interesting. And does Vidlet, do you guys focus on sort of all stages of the product development life cycle? So early concepting, I want to test an idea as well as all the, when I have a product, I'm looking for feedback, sort of UX improvements. Are you still at this stage kind of doing the full range? **Patricia:** Yeah, pretty much anything that needs some customer feedback. Um, I mean, most of our clients probably evolve a product and don't create a new product or a service. Um, and that can be \[00:23:00\] across any sector, right? Um, we have interviewed people on the roof, um, to understand solar installation. Um, we do. That is a business that exists. You just want to improve it. Um, rarely do we actually, we might do product extensions if someone already has a product and now they say, Hey, under this brand, we want to do something else. Um, but it almost doesn't matter. Um, throughout the product life cycle, you need insights on custom. It's not, you do this once and then you know it. The biggest impact for consumer insights is at the front end, or when you make the biggest investment in a product. There's a chart by DARPA that really shows this, where if you make an investment and you already have defined a product, it just becomes very expensive to change it. And so ideally you want to be at the front end. **David:** But it's like, I like that \[00:24:00\] insight though, that you have to continue to invest in the insights. I think some companies might build the thing and then want to validate it. And by like what you're saying, by that point, it's maybe too late. If you've already invested heavily in the product, now you're validating it. Uh, do you have the wherewithal to make the changes of what you find? If you only do it in the beginning, maybe you uncover the insight, but maybe you don't execute it correctly. So, you know, make sure that you're continuing to generate those insights throughout the entire life cycle of a product is something that I think is really important for, for product owners to keep in mind. Uh, **Patricia:** little bit into concepting early concepts so we can go back. To our research participants and say, is this what you actually meant? Did we understand you correctly? And oftentimes it's still changes at that phase. Um, because we interpret it the one way. Um, so yeah, so in my mind it's around the product life cycle. It's, and that's why I think when we see a bad experience out there, people \[00:25:00\] have forgotten to tap back into who their customer is, um, you know, And they need a refresh. And technology changes so fast, right? I mean, now, I was wondering how long it will take us until we talk about A. I. But, um, Well,  **David:** um, you know, four or five years have been pretty crazy in terms of all the different changes going from COVID and the pandemic all the way up through now A. I. and things along those lines and for what Vidlet's doing, both in terms of like switching to the more mobile context where Things like focus groups and design research in home, ethnographic research and stuff probably put on a pretty hefty hiatus for a while there. And so I would imagine that would have, um, helped you maybe in certain ways. Um, and then now you've got AI and people are like, Oh, just ask chat GPT, what your customer would do. How have you seen those changes influence your company? \[00:26:00\] And what's your perspective on, on some of these market shifts? **Patricia:** I think you already said it. Every tool needs an intelligent person to use it. A. I. is a tool, uh, and I think we are in the very, very early phases to figuring out what it actually can do. Um, I'm not sure if you saw the Google ad during the Olympics that they later pulled, um, where a dad instructs his daughter to write a letter to, um, to an Olympian that she admires using Gemini. And when I saw that ad, I was like, that's the dumbest ad. Because in my mind, so of course you could argue a young girl needs to learn how to write a letter, but that wasn't even my, my first thought. My first thought was like, does that mean we all get the same letter now? Uhm, So I think that was just a \[00:27:00\] bad use case. Um, so I think we still need to figure out where it has a place and where not. And, and I think this is, goes back to what we said earlier, that just because the technology exists, doesn't mean it's always working for users. For me, the best example are chatbots. I have not once, not once had an experience with a chatbot, Ultimately gets me the solution. I'm not sure about you. Not once. So the technology is there, but **David:** Yeah, they're clearly very powerful technologies and have a, there's a number of incredible use cases for these things. I think we as a society recently, I feel like especially over the last five years, maybe I just have, you know, uh, just some degree of time dilation where I feel like it's more, more severe recently, but. We get like obsessed with these technologies for a moment, like Metaverse and, um, blockchain and now it's A. \[00:28:00\] I. And of course, A. I. and machine learning have been around for a very long time, but suddenly chat GPD comes out and everyone's freaking out and needs a, some sort of A. I. enabled chat bot. But I think for me, the interesting aspect of it is the really useful tools to, I think, get the consensus. I joke that, um, there should just be like a CEO GPT that has just read every business book ever. And you can just ask this CEO GPT, um, what to do in a business situation. But I feel like what that would give you is sort of the exact opposite outcome of what you were describing, what Frog was like in the early days and Hartmut's, for example, his, his comfort with risk. Is the bot, the GPT, going to give you the risky answer or is it going to give you the consensus answer? And so to your point of getting the same letter, if we start making more and more decisions, if we're using these tools for customer, for research of our customer, and we're getting the consensus answers, what is that going to do to innovation? What is it going to do for that risky, harebrained idea that is \[00:29:00\] new, novel, that no one's written about, that isn't trained into the model? Are we going to get those kind of outcomes? And that's, I think, the scary thing for me is if we abuse these tools too much and we don't, we just use, if we, if we use them for input to get the consensus, then that's great. **Patricia:** they're good, good springboard. I mean, Harman and I are playing a lot with design and AI, and we are having a lot of fun, right? Um, and you always get a kernel of an idea where you go, this is interesting, but it is a little bit like food that was already eaten before. Um, Like, if you really think about it, you could come up with that too. Um, but it is inspiring and someone will figure out how to use it better and better. I mean, you mentioned the early internet days, right? Um, if you look back at how we thought the internet would shape what we do, we got it so wrong. That's why we went through a bust. A. I. will go through some \[00:30:00\] correction and already is, but at the same time, I admire companies that invest a lot in it, right? Especially the ones that can. Some startups and some investors will definitely lose money. Um, but. **David:** no, I totally agree. Um, you got to stay on top of the technology and it's gonna, it's something not to be shunned or avoided. Um, but I think it's important to keep it in the context. Like what, what Vidlet's doing. I see it as a continuation of, uh, what Frog did early on, and then what, even my experience when, when I was there, uhm, which was, I think, after you, it had been sold, uhm, and we did a lot of design research, and we were doing a lot of, uhm, trends analysis, and doing a lot of exploration, and the goal was to generate the insights. You know, those, those customer stories, those interesting insights that you could build on, that would become this seed, this kernel, that you could scaffold a bunch of ideas and design executions. And I think when sometimes when people think about design, they just assume it's a A designer who's just really good at \[00:31:00\] making art essentially, right? But if you had an army of designers that didn't have good insights to influence that design execution, what are they going to get? You're going to maybe get something like a, an A. I. would spit out potentially. So getting to those insights is super important. And I think it's, it's interesting to see your progression through your career. Um, and that I think at the core still VidLit is about generating those insights and helping product owners know how to act more, um, More purposefully in terms of what they're doing with their product. **Patricia:** Yeah, that's why we are asking a lot of why questions. Why do you do something a certain way? Why, why, why, why, why? Almost like a toddler, because that's where the best insights come. And then also project a little bit, uhm, what problem it actually solves. Can something else already solve that problem? I give you an example. A couple of years ago, we worked on Fresh dog food delivered to your home as a subscription model. And these \[00:32:00\] companies are still out there, right? So not, not necessarily a bad business. But then we also projected what would happen if your grocery store had that too. Right? Because at the time, you could only buy, uhm, dry food at the pet store. Now when I walk down to my grocery store, they have a refrigerator. So why would I get a box from FedEx with all this packaging and freezing stuff and all that if I can get it down the street? So, at the time, the, the company did not project that incumbents can also make changes. And that might maybe not wipe out your business, but definitely slow your growth. **David:** Yep. They need to be thinking about the, how to break the things that are working and what will break those things. That's, that's really cool. Uh, I wanted to ask you, um, obviously the last couple of years have been challenging for the professional services \[00:33:00\] design agency world. Um, you know, I know, uh, companies like Frog and IDEO and pretty much anybody doing any sort of services business over the last couple of years has been really hit hard. Um, I was just curious, sort of like based on your career and your perspective on that ecosystem. Um, How have you observed what's been going on in that space? And what do you think the implications are going forward for companies that are maybe like ratcheting back some of their investment in design thinking and insight generation and good design? Um, what are you observing? **Patricia:** Well, and I know you have heard this before. I think the industry, um, is responsible for that demise in some ways. I think design thinking in its traditional sense, um, was a good idea, but we overdid it. Um, but everyone now could be a designer if you just have a workshop with me, right? You can all be creative. And then a lot of people just didn't have the skill set to be a designer. They would take, um, \[00:34:00\] a four week, six week design thinking course, and then would be a designer. And so I think in some ways we caused that. Um, I think salaries were extremely high. Um, companies were just hiring designers, not even knowing what to do with them. And so I think there will definitely be a salary correction. But I also see some of the good people. So the sad thing is that, uh, people that we often let go from companies who were maybe the higher paid, really good people, and I'm convinced that they'll figure out something new. I, I'm already talking to a couple that have like left Microsoft, left Google, left Frog, and, um, and are figuring out what the industry needs next. Um, because you, you. All this design thinking hasn't really improved products **David:** Hmm. **Patricia:** that much, right? So I think we need to figure out something else, but \[00:35:00\] smart people always figure something out. So I'm, I think it's a good thing. **David:** Yeah, that makes sense. And I remember when I was there, this was, I was there 2006 to 2011. Um, especially in the early part of my time there, we, a lot of what we were doing was convincing people or explaining about design thinking about human centered design, about why design matters. And another factor I think of like, Being too successful was, I think we like were right there was, there's a lot of value to the design process and design thinking and that design does matter and you see more and more companies embracing that, right? Like I think every, and tools like mobile apps and things where user experience really. Shined, like people know you, if you want to be serious and build a high performing products, that's especially consumer, consumer products, you know, you have to have good user experience. It has to be usable. It has to be something your, your customers can use. And, um, I think a lot of companies took that in house as well. Right. Um, so I think that was another aspect where I think design and design thinking and some of that stuff, um, \[00:36:00\] just became ingrained in the fabric of how business got done, but at the same time, yeah, maybe a little bit, you Too highfalutin, with how we approached it. Uhm, But you're right, there's so many good people, there's so much good talent. We at VGV have been, um, uh, beneficiaries of some of the contractions of some of the other firms because we've been able to attract some really great talent and I think there are a lot of really amazing people out there who, you're right, they're going to find something because they're really smart, creative, interesting people, uh, and they're going to, you know, Find a way to be successful, um, and make an impact. In fact, I wanted to ask you about that specifically, um, culture and team, you and I, I think I've talked about this in the past, cause one of the things I really admire about frog and frankly, I aspire for VGV is when I look at, there's some alumni groups, there's like a frog Slack channel. And when you look at where people who went through frog, like what they went on to do, where they've ended up, the types of companies they've worked with and the roles they've accomplished. Frog became this amazing feeder, uh, creator of talent, right? \[00:37:00\] Um, and how, how did you do that? You know, how did you set that up as a culture and a team? And what do you think, like, if you could give advice to other companies and other teams, how do you set up a culture to be this, like, talent factory where you just are spitting out incredible people who go on to make impacts all over the place? **Patricia:** Yes, I think that started even before my time. Um, so first of all, I think about culture when I now invest in startups, I always tell them culture before everything else. Uh, and culture comes often from the CEO first, right? Um, that you are part of the team, that you drive ideas, that you allow mistakes to happen, that you are flexible. Um, but frog. And this was, as I said, before I joined, um, did something that at the time was absolutely revolutionary. And I was buying all the back pages of major design magazines. And people always said, why \[00:38:00\] ads on design magazines? Like, you cannot get customers that way. But the goal was not to necessarily get customers. The goal was to tell designers. This matters. You matter. We want you to come to us. It had this certain attitude. Now you can do everything on TikTok much faster and whatever. But back then, that costs money. Invest in who we want to hire. Um, and then pay people well. Designers compared to today were not paid that well. And demanding that from a customer. And I think once you do that consistently, it's like a muscle that's ingrained, and then everyone behaves that way, and everyone walks a little bit taller, uh, and I think that matters. **David:** Totally agree. Um, I think culture is super important and getting people to be believe in the vision and support the people, especially in consulting. I mean, I think that's something that that frog was great at. And I think, you know, \[00:39:00\] the successful firms out there try to do too, is it's all about the empowerment of your people and their belief in themselves and what you're doing. And that comes from great culture.  **Patricia:** That's why these acquisitions, I think, are backfiring a little bit, right? Because you're putting two cultures together, and if they don't match, then you have pain on both sides.  **David:** hmm. Yeah, I think, totally. Um, I think there's a number of examples we can point to, in those situations, at least from people that I know and various companies, of where those don't work. But, I mean, that's also one of the hard things about our modern world. That's tricky too, is, you know, as founders, um, as companies, you want to grow, you want to be successful and it's, you know, Kind of, kind of sad in some ways that like the outcome of a successful firm is you got to just keep grinding it out and take all the risk or you get gobbled up by a big giant conglomerate, you know, and that's the nature of our world, but you know, there's, there's pros and cons to all of that. And I will say one of the things that I remember Hartmut came to visit us and he said something that really stuck with \[00:40:00\] me over the years. Someone asked him in a, um, He came back to visit when I asked him, like, one of the motivations to, to move on from frog. And it was like the ability to like achieve better scale, to make impact on a bigger level. And that's always stuck with me. Like making sure that you kind of keep in mind where you are and what you're trying to accomplish at any stage of your business. And that to really impact things on a global scale, you are going to have global scale problems. Right. Um, so that's just a thing you have to be able to embrace. **Patricia:** you now have, right? Um, and you have to learn to deal with that. Um, so yeah, you need to have a certain sizing that's even more important now. Like Hartwood will often tell you the stories how he just got Sony as a client, um, very early on. And you probably noticed nowadays to get on board with a client, um, It almost feels like you are giving away your firstborn. We just got, got on boarded with a client yesterday. Relatively small project. The paperwork alone, digital paperwork, uhm, \[00:41:00\] takes someone three days. Uhm, that is very hard to, for a small business. **David:** Oh yeah, **Patricia:** We are working with, uhm, NASA right now, one of my favorite projects, to help them understand how to embrace small businesses and private businesses. **David:** Hm. **Patricia:** It's very hard to do, um, because they have the traditional government contracts and things like that. Um, so I don't know why, how did we digress in that direction? Um, oh, scale. Yeah. So you need to have a certain size so you can handle these things. That's unfortunately the truth, right? Um, **David:** Yeah. Yeah, I mean, you gotta do it. I mean, I think that you can be a young challenger brand and have a different offer, and maybe that's a way to shoehorn in and get something started. But ultimately, to really make an impact on that scale, you have to play their game, and that's kind of what it comes down to. You're right. Long legal and procurement cycles and the ability to work through that, navigate it, and support it, and know how to \[00:42:00\] shoulder that risk effectively. That's just the price you have to pay these days to work on big problems. So one other thing I wanted to ask you, I remember in one of our very first conversations, which was many years ago now, Uh, we got connected through some mutual friends and I was, uh, early on growing VGV and I wanted to ask you, I was like, how, how did, like, what, what, what did you learn from growing frog and how did that make you a better entrepreneur and leader and growth? And I remember you said, well, you know, yes, we grew frog and we were successful and we did it, but now you're doing vidlet and it's like a new thing all over again and it's a totally different set of problems. And so I was wondering if you could kind of expand on that. That insight a little bit about what it is to be an entrepreneur and how do you like kind of leverage your past experience knowing that the new thing is different. And just cause you're successful there, it doesn't make it magically easy. Um, I would imagine people like me come to you often and say, wow, look at this company, you helped start and build that frog. And it was such a successful company. Like you must have the secret recipe. \[00:43:00\] Um, what has been your experience, uh, as moving on in your career? **Patricia:** Well, so I learned that as a startup investor where I was part of a group that has all, they have all done well and now they thought they had a recipe and could advise startups on what to do. And I learned very quickly that does not work. You can have an opinion, um, but every business is different. And for me, for Witlet, the hardest thing was selling. We talked about selling already before. At Frogger, I never had to do that. Now I had to go out and it's a fresh start and you see this often with people coming out of big companies like, let's say, Google here and they want to start their own company and they're used to being catered to by the outside world and now you're really hustling, uhm, And different rules. Um, you don't have an I. T. department on day one. You don't, um, \[00:44:00\] no one knows what you do. Uh, it takes you a long time to even explain what you do and really nail that. So, but I think it's the best thing anyone can ever do. Um, I think anyone should start a business at one point. Um, when I hear people say, oh, it's so great what you do, you know, you have your own business, you can do what you want. Um, I often tell them, why don't you first embrace small companies and the decision making power you have, um, and take some risks in who you work with and then take that step at one point. **David:** Yeah, makes sense. Once you take that entrepreneurial leap, it is a whole new ballgame, for sure. Totally different set of, uh, challenges, stresses. **Patricia:** And I was lucky, you know, I, um, already had money in the bank, already had an exit. Um, I was not putting that on my credit card. \[00:45:00\] I have such admiration for people that do it, if they do it the right way and dumb ideas that people invest into. But anyone that goes out and says, I'll take that risk. And I put my wellbeing at stake, my family's wellbeing. I take a mortgage out, um, on my house to start a company. It's an amazing step. **David:** Mm hmm. Big step. A lot of risk. So, um, just to, as we, uh, thank you for the time, I think, as we think about, kind of, wrapping up, I wanted to ask you, um, given your The career you've had and the experiences you've had. How has your definition of success changed over the years when you think about success personally, but also success of a company and maybe as you're doing your work with the success of a product? **Patricia:** So, I mean, I've been around the block for a while. So success for me in, um, I mean, I was put in a position at F. R. O. G. very quickly to be a \[00:46:00\] decision maker. And in hindsight, I don't think I was always very kind. It was a lot, and this also had to do with women at the time. I think we still had to fight very hard to have a seat at the table. Um, so I was probably a lot more aggressive, a lot more power hungry in some ways. Um, Whereas today I see the young people at WIDLET, I think they are a lot more collaborative. And for me, it is now important, um, that they have a good career, but that's a luxury I have, right? Um, we just said goodbye to a WIDLET person that's going back to school. And, um, she said to me, she had been there for three years, um, that I, that WIDLET had given her confidence. Talking to clients, big, big clients, you know, having big projects on her shoulder and that gave her confidence. And for me, that's probably the biggest success. Like when, or like you said, former frogs \[00:47:00\] going out, building companies and being successful. That is much more fun to watch than being the CEO, you  **David:** And potentially far more impactful because that's where the exponential impact comes. You know, people come through companies, they're a step in their career, even people that are there for 10 years or more, you know, it's, um, It's not the only thing they spend their life on. And so thinking about how do you empower those people to move on and take what they've learned and take that positive experience and influence. Um, and even if the company continues on, those people maybe become customers one day or clients, you know, by moving on.  **Patricia:** mean, there's some companies that do that very well, right? But that's why I would recommend everyone, this is something I didn't do at Frock. Network, network, network. And not only when you need something from someone, but also when they need something from you. Uhm, I, uhm, my godson just graduated from college, and I said to him, like, go out and network. You know already a lot of people. It will be very important for your \[00:48:00\] career. So, I think that's different. Nowadays, we can use LinkedIn, right? And all these things, uhm. **David:** it's still a rare skill though, I think. And these days we're so inundated with spam, you know, and people are reaching out all this outbound automation tools and stuff like that. But I think establishing and just maintaining relationships for the sake of just maintaining a relationship, not having an agenda, just to stay in touch, you know, even people that you only just casually, lightly know, um, just to check in and see how they're doing. Thank you. And doesn't have to have an ulterior motive because those positive outcomes just happen. And even if it's just a, Hey, there's an event, do you want to come? Or. I heard about so and so and it might be a good opportunity to connect them. Like these are the things that, and it really happens. And I wonder if these days of business schools are focusing on sale, things like sales and networking. I mean, I went to Wharton, but this was a long time ago, over 20 years ago, there was no sales 101 or networking 101. You had marketing, finance, accounting. Um, but it's kind of like being able to be a good \[00:49:00\] salesperson and being able to be a good networker and relationship builder are so fundamental. Um, It's, uh, it's something that anybody who's starting a product or an entrepreneur or even, even within a big company, you know, being able to sell your ideas internally and being able to build the relationships politically. These are super important **Patricia:** I think very important. I, uh, was supporting an entrepreneurship class at one point. And these were mostly engineers coming out of big companies. And they had to write a business plan. And when I looked at the business plan, I suggested they sell this to their respective companies. And they all told me, I can't do that. I was like, how do you think you're going to start a business if you cannot first sell it internally? If they fire you, you want to start a company anyway, so take the risk. **David:** Well, Patricia, this has been really fabulous. I can't thank you enough for taking the time out of your day to, to speak with, with me and, and be on the show here. Um, I guess to, to wrap up, I wanted to just throw like a, \[00:50:00\] another just random thing, which is, um, cause I'd like to kind of get a sense for beyond the work, um, and beyond all the things that we're doing in, in our professional lives, what a little insight. So is there a book recently? recently you've read that's kind of caught your attention or, or has been insightful to you? Like what's, what's kind of interesting outside of the, the day to day? **Patricia:** Yeah, so one, and we talked a lot about these things in this conversation already, but one that I really recently adjoined, and also knowing that you are New York and I'm in California, is Early True Believers. It's an audiobook. About Silicon Alley and the early days of the Internet. Uh, I think the author is Vanessa. I forgot the last name with a G. Uhm, but what I really enjoyed, I enjoyed the book for two reasons. And much of that we already talked about. One, I did not remember some of those stories. And how we perceived the Internet. Uhm, and especially in New York \[00:51:00\] that a lot of that was driven by artists. I always thought, uhm. You know, the Silicon Valley perspective where it was probably driven much more by technology. Um, and then as we already talked about A. I., like I think A. I. is at that stage now, right? So I just enjoy the stories. They were very human, not condescending, even though the internet bubble burst. So I can only recommend it. **David:** That's great. We'll have to put that on the reading list for sure. Um, well, thank you again. Um, it's been great to chat with you and it's been great to get to know you over the years and I thank you for, um, You know, being, uh, uh, somebody that I can talk to and reach out to from time to time. And, um, you've always been, had fantastic insights and, um, and Vidlet's a great tool and we've used it and hope to use it even more. Um, and I hope that a lot of people, um, who are building products or, um, you know, building a company or a business understand the importance of insights and to continually \[00:52:00\] check in with what's going on with your customers and your users. Um, so I'll be excited to watch how Vidlet continues to grow,  **Patricia:** Same with you. I mean, you are, um, you are where innovation almost matters the most these days, right? I mean, you said earlier apps need to get someone's attention and need to be usable, but you first need to, um, get real estate. You want to come to my home. You need to show value. And I think that's what you're working on. That's why we had a really good time working with your team and doing some research on an app that needs help. **David:** I appreciate that very much and totally, totally aligned. So, um, thank you so much. Um, appreciate you joining us. Um, hope to maybe do it again sometime. Okay. So we'll **Patricia:** So we are still hanging on. So did you get what you wanted? **David:** That was great. ### Transforming Real Estate With Modern Technology Source: https://verygood.ventures/podcasts/chris-synan-keller-williams-realty-inc-transforming-real-estate-through-cutting-edge-tech/ In this insightful episode, Chris Synan, the driving force behind Keller Williams' technology transformation, shares his journey of leading change in a large organization. From overcoming tech debt to fostering a culture of trust and innovation, Chris reveals the strategies that allowed Keller Williams to accelerate its development process and embrace bold ideas. If you're interested in how leadership, technology, and team culture can drive success, this episode is a must-listen. Tune in now for a behind-the-scenes look at one of the most exciting transformations in real estate technology! {% audio_embed url="https://player.cohostpodcasting.com/a7cab00a-43d1-488c-a912-8951772183ad/0c3e9d0a-2998-448e-b329-b4fd3440df64" /%} ## Transcript **David:** \[00:00:00\] Alright. Hi, Chris. How are ya?  **Chris Synan:** I'm great, David. It's great to be here. **David:** Yeah, thanks so much for joining us. been a little while since we caught up. So,how's the, uh, world of real estate tech been these days?  **Chris Synan:** it's great. it's definitely an exciting,exciting field over the past couple of years. We've had some, really hot times in the industry and we've had some kind of cooling off. we're kind of looking at interest rates coming down to, to pick the pace of things back up again. in, in the tech world, things haven't slowed down at all. We've taken, all this time to continue to. Sharpen the saw with our processes, with our technologies and really working on making everything just continuing to make everything that we've got world class. **David:** Awesome. Well, that's the fun thing about tech, right? No matter what's going on in the markets or anything else, the tech is always there riding along. So  **Chris Synan:** something to do for sure. **David:** yeah, absolutely. Let's take a step back you know, since we're just getting started here, take a moment. You want to introduce yourself and tell us a little bit about, your career journey and how we got to talk today.  **Chris Synan:** yeah, for sure. Thanks. yeah, I'm, my name's Chris Seinen. I am currently, vice president of \[00:01:00\] engineering, at Keller Williams Realty. I joined, just like actually a week before the COVID lockdown happened. So it's been an interesting, ride for me. In addition to that, I came in through the side door. Gary Keller, who's the founder of Keller Williams, Gary's son, John Keller had started up an incubation, a technology incubator alongside of Keller Williams. It was called KWX and they hired me to run technology for KWX. And so I kind of had a unique. Vantage point on Keller Williams, from that, being off to the side, in the orbit of Keller Williams for a while. And then through a series of events, we ended up getting rolled in.  **riverside\_chris\_& david \_ oct 7, 2024 001\_iaw\_trailer recordi:** to,  **Chris Synan:** to KW. so, you know, before I go on, too much with that,I did want to touch on a couple other things. I wanted to give a bit of a background just on Keller Williams and what this company is and their tech journey, if that's all right, if I can  **David:** yeah, absolutely.  **Chris Synan:** cool. Thanks. so Keller Williams, Keller Williams is the largest real estate franchise in By agent count, \[00:02:00\] we have the most agents. it's, at current count, it's somewhere around 160, 000 agents worldwide. and basically what that means in the United States is that a Keller Williams agent is attached to about one fifth of the world's population. of every residential transaction that happens. So every house that gets bought and sold, there's about a 20 percent chance that there's a KW agent involved either on the buyer side or on the seller side. So, you know, we've got just lots and lots of agents out there, just a very successful community of agents. and it was, so Keller Range has seen, A lot of growth since Gary started the company in 1983. Massive growth, for many, many years. And then several years ago, Gary wrote a book called Millionaire Real Estate Agent, what we call MREA. And MREA really catapulted the franchise. people really loved this book because it really lays out there. This is exactly how you can become a millionaire through real estate. It's really step by step. \[00:03:00\] It's really prescriptive, you know, in, in how you do it. and so many people, you know, I, I'vebeen to these, these events. We have two big events a year and you go there. And people are really all in on Keller Williams because they got the book, they did what the book said, and now they've just built a really successful business. So it's, that it's just fantastic. It's just been a game changer, life changer for so many people. Right.so then fast forward a little bit to about the 2017 timeframe, around this time, Gary said, look, we've given them this playbook. We've said, here's how. You can go and be massively successful with real estate. They didn't really have the tools to do it. I mean, there's tools out there, but the tools didn't really work the way that, you know, the MREA playbook said to do it. And so Gary said, you know what, we're going to be a technology company. And they just, they said, this is what we're going to do. We're going to invest whatever it takes, massive amounts of money. and we're just going to build out these technologies. So \[00:04:00\] they set about to build. What's what we call command. Command is our CRM for real estate agents. It takes you through the whole practice from lead generation, contacts,communicating with your, you know, with your, with your contacts. and then when you get into a transaction, it takes you all through that, the financials and everything else, right? So it's kind of an end to end. Real estate practice software. And then in addition to that, we had the consumer facing experiences, such as K2B. com, which has, you can search for homes and you can favorite things. You can create, save searches and all this and mobile app,and things like that. Right.so it's great because they just went all in and they said, let's go do this. it's hard to really onboard a ton of engineers. To go and do a thing, in short order like this. and so, you know, the way that they grew was largely,by just getting contractors, just grabbing as many contracts as we can, throwing them at this work and, you know, the good news is \[00:05:00\] we did, the system got built, the bad news is it wasn't always built. Really optimally. So there was, a certain amount of tech debt and other things. So once we, got the platforms out there, there was still a lot of work to really get everything fully stabilized. and I guess that's where my journey started with Keller Williams because when I came in I was asked to take over.  All the consumer facing experiences. And, when I got there and took a look at what we had,there was going to be a lot of work to do  **David:** Well, yeah, it sounds so interesting. Cause obviously it's such a complicated business and you have so many customers, but you have the consumers and the agents, right? And I think That's, one of the interesting aspects of real estate is you have these complex systems like command and then the consumer side, and you've got to merge those things together. And that tech has become so core to it. I would imagine it was a pretty big transition for them to moving as that tech got immersed into the world, right? Start. What do you say? 1983, they started the business. Is that right? **Chris Synan:** that's right. Yeah.  **David:** Yeah. And so, I mean, what a wild shift to go through, right? and it \[00:06:00\] really, great for them to see the importance of technology and investing in those platforms. So yeah, what is the consumer app stack these days? you know, as you came in there. and you looked at that, you're, there's some technology debt, there's a few things going on there. What were some of the things you identified as you got into the. The consumer side of things. **Chris Synan:** there were some things that were done well, and there were some things that, that really needed some work, right? One of the, one of the factors that kind of, you know, that brought me into that space was, I mentioned KWX and, the kind of the incubator accelerator off to the side, we were trying to do a lot of things, that really needed to live within KW consumer experiences, right? So we were trying to build code and then we wanted to get it into, for example, a, the consumer, the KW Mobile App. and we were having a lot, a lot of trouble doing that. And the reason is because the code base was,it was pretty unwieldy. The word spaghetti has been used. but it was just, we were having a lot of problems I, I'd say both with quality of that code base. and then really \[00:07:00\] with, you know,extensibility was not modular at all. And, you know, when we tried to put new stuff into that code base, we were just having lots of, lots of problems, lots of scaling problems **David:** These are legacy native apps.  **Chris Synan:** this was, well no, these were actually, These were React, this was React Native.yeah,lots of performance issues, I think one metric that I recall is, I think 20 percent of our sessions on Android ended in crash. 20%.  and also just the pace of delivery. was really bad. And I think that's, you know, that's just one of those symptoms as well. when the code is really unwieldy, when people don't really understand what it does, when it's hard to read and it's not well organized, it's hard to build a new thing. you can go build a new thing, something else broke, right? And so you're always chasing, these problems. And that's something, that's definitely something that we're having. **David:** Yeah, I think, you know, you're saying 20 percent crashes. That's a stat that I think people can really understand. They see that and they get it, \[00:08:00\] you know, there's metrics you can show crash reporting and generally speaking, your star, reviews in the app stores and stuff, but like that other part that you're identifying is the more insidious. Like problem, right? Where all of a sudden you, you just, your business can't move fast enough. You can't ship fast enough. and I think that's way more costly than having three stars instead of five, in the grand scheme of things. cause how do you even fix it? How do you improve the experience? If you're just kind of felt. You know, like hung up by what you've got. It's, it can be really difficult.  **Chris Synan:** not only was it, just kind of slow, but it was even, you can't get here. You, sorry, you can't get there from here, right? It was, we want to put this in, we were being told, build another app. there's no way we're going to get this into that. And I looked at it and I said, what we're trying to do here is not that hard. Right? I mean, we're not trying to really invent,we're not trying to break the laws of physics here. we're just, it's just an app. It just hits a database. It shows some things, right? and when I was put in charge of this,we took a really close look \[00:09:00\] at it.  And, I wanted to work with the teams and I wanted to give people the opportunity to step up and say, okay,I'll take a look. I'll give it a shot. and we did this, a couple of different times is we took a team and said, Go spend the time, do what you gotta do, and figure out how we can untangle this app,and how we can make this thing work. that team ended up kinda throwing up their hands. You know, so at this point, I'm reaching out to,consulting partners, and I'm saying, listen guys, I need some serious expertise in React Native. I need some people that really, really understand this technology. And I said, I'm willing to pay. I've got some money. You get me somebody that's, that's a,a committer on the project or something. I need serious expertise. I couldn't find anybody, you know, and so that's where I thought that we were really stuck. the teams were just basically saying, let's rewrite this thing. And I said, well, I mean, I kind of get that. But. You know, there's the old saying about doing the same thing over and expecting different results, right? I \[00:10:00\] said, how do I know that, because I think part of our problem is the technology, but that wasn't the only problem. I think we had problems with our processes. I think we had problems with just even our own, kind of culture. With respect to how we deliver software and in our commitment to testing, our commitment to quality, our commitment to automation, there's just a lot of areas that, that we really needed to kind of reconsider and reboot. **David:** Yeah, it's so interesting because I think so many companies are in this situation, where, that you have the mobile boom, everybody needs a mobile app, but we all have limited resources and constraints. And, we're trying to move the best we can. And like I said, the higher subcontractors you start building up a team. And,we had a chat with Sam Moore at Betterment and he described it as these like lava layers where you get these new people, there's like a new eruption of work. And this like layer gets bigger Built and Hardens on top of the old stuff and before you know it, you guys just got all these layers that you're trying to, but no one knows what's four layers down because that was a long time ago and it's like a lot of rock between you and there. and so it's getting to that point of \[00:11:00\] right? What is it? The tech? is it the, is it react native? Is that the problem? probably not because there's other apps. hat very users that use reac and probably with some degree of architecture, is it the ti You know? And then you're ha this, how you were able to with your leadership of li a bunch of money on this al But how do you go about c team and a leadership gro generation, I think there w **Chris Synan:** Yeah,that's definitely one of the, one of the biggest challenges with a huge transformation like this, right?  and it really does depend a lot on, your leadership and the culture of the leadership and it depends on the level of trust in the organization. because it is a tough sell. you're saying, look, we just spent a bunch of money to build this. We need to chuck it. Right. not a great story. but if, I think if you really, take the emotion out of it and just say, look, Here's our \[00:12:00\] velocity. Here's the challenge that we have. And here's a hypothesis. The hypothesis is, if we go down a different path,we can achieve a lot different levels of success and we can achieve, you know, a much higher level of throughput, we can achieve much higher levels of quality. But it's not just You know, dump it and write a new front end. there's much more to it. Right. So I think that's part of it too, is because we all, we all want to, we all want to create stuff and we all want to build stuff. That's new. I mean, I get that, but that's really not what this was about. This was about changing kind of everything we do, like really, this was a piece of that larger puzzle of just utter and complete transformation of the entire way that, that we operate. **David:** Yeah. So not just tech, but culture and process and just even like how you think about technology within the organization. Do we just always go to contractors for every little thing, piecemeal, project by project? Or do we have a capability really, right? something \[00:13:00\] we believe in is an asset to the work.  **Chris Synan:** that was, and that was definitely part of it too. As we said, listen, we have a different staffing strategy, right? Obviously, I'm not going to go replace, 200 contractors with 200 full timers, overnight, but here's a strategy that, you know, that we can start making progress. Kind of bring in house the really key critical roles, the thought leadership, the decision making, that expertise, and then we can continue to have that balance with FlexLabor. And I think that's worked really well, but again, that's just another piece of a really holistic strategy that says,we got here however we got here. That's, no point in fingers, no blame, doesn't matter. Here we are, and here's the list of things that we really need to go and address and transform, and it's all the things, and so here's how we're going to do this, here's how we're going to do this, here's how we're going to do this, so, you know, really, really focused on, on Agile, brought in some serious Agile practitioners, transformed the way that we work, all this stuff too, \[00:14:00\] is,You know, anytime you bring in, anytime you introduce, significant change, there's a certain amount of,angst that kind of comes with that, right? A certain amount of anxiety, a certain amount of resistance to change. and that's always got to be part of it too, is, how are we going to work with people to, to get them on board with what we're trying to do here? **David:** Yeah, that's a huge one. Cultural side of it. You know, it's a massive. So,you mentioned React Native that you guys were using React Native and you were looking for help with React Native. I know you, from our work with Flutter. So I know ultimately that just fast forward to that one. That's where you ended up, but how did you, what was the process of evaluating like React Native, Flutter? How did you end up with the choices that you made?  **Chris Synan:** we're all technologists. We all have the things that we like and that we don't like, and I don't want to get into like a religious battle about it. but it's going to be hard to not sort of, you know, convey my own stuff a little bit, because for me, I, I'm a leader of people, I'm a strategist and all these \[00:15:00\] things, but for me, a lot of this stuff is informed by me getting my hands dirty, me spending nights and weekends. beating this stuff up and finding out what I like and what I don't like. I'm not really a front end guy. I'm much more of a kind of a data guy, integration guy, you know, other, other things. Um,I never really learned any of the frameworks. I basically peaked at jQuery and never really got into, the views and the reacts and all these things. Right. part of it for me was I spent some time in the code base. I was utterly confused, for me, since I'm not a deep react or react native guy, I didn't want that to weigh too heavily. I also, I think the, one of the other things that's really important about,leadership is. One is surrounding yourself with people that are smarter than you, right? and two is knowing when to really lean on them and knowing when to, knowing when to just,lean very heavily on, on what their advice is, \[00:16:00\] right? And so my principal architect at the time says, Hey, you know, I've been looking at this and I've been playing around with some different stuff. You should look at Flutter. He said, you should just, you should take a look at it. And so I decided having no background in, in Dart or Flutter,you know, I just, one, one weekend I just popped open the computer and followed a couple of tutorials. And I said, here's what I want to do. We, so we have, you know, with home search,a map view is really, key part of home search. You say, I want to look in this neighborhood. You get the map view, you see where the houses are. that sort of, digital experiences is really critical, right? And so I said, all right, I've got a Saturday morning all to myself. I'm going to see if I can implement that in Flutter. And so I just went, file new. And by the way, this is before chat GPT. So this is, you know, this is really, **David:** You had to really  **Chris Synan:** on old school, the stack overflows and the, you know, the Googles and stuff like this. and,and, I had something going in maybe four or five hours. that I really implemented that using our GraphQL, A. P. I. I had a kind of a working thing. It wasn't beautiful, but \[00:17:00\] it was working. And I said, Ooh, okay. So I like this. And then I said, all right, but what about, some of these challenges that we have? Cause I think we really have challenges with automated tasks. and,and, things like that. So I said, okay, this Flutter, you know, what, what does it have? What does it have for different kinds of tests, testing frameworks and things like that? and so I started looking around and I found some things and one kind of common thread as I'm looking at all these great, tools and different open source things in the Flutter community, I kept finding this common thread that kept pointing me back to very good ventures. And I said, Oh, this is interesting. I hadn't heard, I hadn't heard of, of VGB at that point. but it just kept popping up, in all this little research that I was doing. And I think, I think maybe what really, the sort of the light bulb moment there, you know, was I had so much to find someone that was just an authoritative expert. And I think, you know, I think maybe what, what really, you know, the, the sort of the light bulb moment there, you know, was I had struggled so much to find someone that was just an authoritative expert. And React Native that could just say, look, I \[00:18:00\] understand this inside and out. This is not how you do it. You do it this way. Right. Cause this was my concern. If I let the team go rebuild this, with React Native, but if we don't have that level of expertise, how do I know that we're going to get something that's really great, and I think when I started seeing very good ventures pop up everywhere, I said, maybe this is what I've been looking for. Maybe this is the experts that can say, okay, well, if you're going to build a thing and you want it to be modular and you want it to be expansive, and here's your long range plans is this, and this, here's how you want to build that. and that's when I reached out and started talking to your team. **David:** Awesome. I'm so happy that, that you found that stuff. we put that stuff out largely cause we, we believed in kind of working in the open, open source. And especially in those days, it was, we felt it was so important not to keep the learnings to ourselves because so many people were out there. trying to figure this out and Flutter was new. And so I think that there's some risk in any new thing of people making those mistakes again. And we were very fortunate to have some really amazing people on our team who had some \[00:19:00\] really great experience working at a place like BMW and having like real big production apps and having pushed hard against a lot of large organizational scale problems. And how do you make this tool work for you? I think, cause at the end of the day, like one of the things you're describing here is that. The tool matters like having a good hammer versus a crappy hammer matters if you're building a house, but at the end of the day, the best hammer in the world is not going to hammer the nail if you don't know how to swing it properly, And so I think that there's just some of that stuff around making sure that we're as a community and as, as engineers, right? Are we doing things that we feel like are scalable and we can believe it? And I also think one of the things that's key, right? Is it doesn't. If you're, there's a lot of different ways to build stuff. And as long as you, how you're doing it is a good way. And it's scalable. The key is to stick with it, to have an opinion, And to believe in it and know, yeah, maybe there's some other way, but like this way works for us and it's good enough. You know what I mean? And we prove it again and again that it's going to be scalable. and so I think that's been something that's been really cool. And even as Flutter's \[00:20:00\] grown, we've seen a little bit of regression for that, Right? Where there's more different ways, and people have a lot of opinions, and it's like, Hey, your mileage may vary, you pick the things that you want to do, but just stick with it, whatever it is, So that was our goal there, and I'm glad that, that was helpful to you guys.  **Chris Synan:** Yeah, it was awesome. And you know, there was another element that I, I kind of found.  bouncing around my head just in terms of decision making criteria. you know, at the time React Native had, a really, really big third party libraries, right? Tons and tons of different things to the point where it was almost too much, right? there's almost too many ways to do a thing and it wasn't clear, like this is the good path because you could just go all over the place and end up with kind of a real hodgepodge, right? and so one of the things that, that I really started to like about Flutter and, working with Very Good Ventures was, it was like, there's some choices, but this is the one for you. **David:** I mean, you know, to the point that you're just making, is it. Is it the, is it always going to be the best thing in the world for everybody? I don't know, but you \[00:21:00\] know what, put it in there. It serves the purpose. it's something you can build on. It's something that, that can, grow with you and your team and with the app and you don't get lost in analysis paralysis, Yeah. And there was just a lot of factors like that. Yeah. Yeah. Was there,So this, you found us, you found some of those materials, and you had some documentation and some directions, and you experimented on the weekend, got that confidence in it. Were those all things that you then brought back to your team and the organization around getting that confidence internally? to not repeat the past mistakes. I would imagine, your leadership probably was aware of a problem, but now I guess I've always been curious about how, as a leader within your organization, as a technical leader, how do you convince the rest of the organization that like, what we're going to do next is actually going to be better, cause there's a lot of hesitation. I think there's a, people have been burned in the past, right? You get the, like you said before, you get the same mistakes again. How did you, translate your learnings and your findings into like confidence building for your leadership and the rest of the team?  **Chris Synan:** \[00:22:00\] yeah. that's a great question. It, it is, um.it's about managing and balancing risks, right? and there's a little bit of knowns and unknowns, right? we have this app, we know about how long it takes to do things. We know about what, what our measures of quality are for it. and it's not very good. And so there's risk there. There's risk in continuing down that path. and it's, it is kind of a known risk because I don't think magically it's going to organize itself and be, you know, in sort of all of a sudden have a great and really sound architecture. It's going to be what it is. And we're going to keep, putting different, packing different layers around it. that's just covering up some of the core problems, right? And then you contrast that with this is a different kind of risk, but look at what we've done. we've, we did, and a couple of the other things that we did, I think you guys were kind enough to come in and do a couple of lunch and learns. give us a couple of kind of training sessions, even before we really kicked off,I challenged the \[00:23:00\] team, with doing a couple of little mini proof of concepts of their own, and, offered, some, some other different kinds of training as well. So there are a few different things that we were doing to start to bring up that confidence with the team. and then there was another element of it, which is that from a, Product and design perspective. We were also going through a big overhaul. So we were really trying to take it in this way. It was, you know, the timing, I think, was pretty good. It's because,business wasn't saying, Hey, what we have there is great. Don't mess with it. They're actually saying we want to go this whole new direction. We want to do all this really ambitious stuff to and stylistically, And from a branding perspective, we want to go a whole new direction also. And that helped that, there was that appetite for change coming from that side as well. And I think I was able to tap into some of that and say, listen, here's what it's going to take to go back and, get this crufty thing to look and act and behave the way you want it to. And here's what it would take to just start with something \[00:24:00\] new. but part of it too was. I mean, I'm trying to explain. There were kind of a lot of elements to making this good case, right? Obviously.and I guess I can't underestimate or understate, how important leadership and, trust is in all that, right? and the culture that we have, you know, my, my boss, Chris Cox is just an unbelievable leader and, he just, he's a servant leader. he's absolutely trusts. the group that, he trusts me and he trusts all of his leaders and I think that's a big part of it as well. but we were able to go back and just say, look, here's what we've been able to do in a pretty short time. based on what you guys want to do and where you want to go with this product, I really think we're going to be faster. even abandoning this old platform and starting with almost nothing, we're going to be faster, but wait, there's more. We're not really starting with nothing because Very Good Ventures, one, they're going to come in and help us. And two, they have this entire, framework, Very Good Start. Is it, was it \[00:25:00\] Very Good Start? **David:** Yep. Mm  **Chris Synan:** I mean, yeah. you have this whole Very Good Start thing that basically gives us a lot of that sort of bootstrap stuff that we need for our app to really get us up and running. And so when we looked at the capabilities of Very Good Start and we said, Hey, It's going to give us all this stuff. That's going to leapfrog us, two or three months, maybe just in terms of stuff that we would have to go figure out how to build. We're going to get all that stuff for free. So, you know, holistically, when you looked at all this stuff together, it started to get pretty compelling. and, there was one other element too, that, that kind of felt like a bit of a gamble at the time, but it just sounded so compelling that I had to try. And that is, it's kind of a given. That with Flutter, you're going to get iOS and you're going to get Android. but I think we were just really kind of intrigued by the possibility of getting web experiences too. And, so we made that part of the business case and we took that, that gamble a little bit. And it's been really \[00:26:00\] good for us. It's really worked out well. I mean, obviously, you know,there's some caveats on when you want to use it and when you don't want to use it, but for what we wanted to do, it's worked really well, and we've got, you know, experiences that are, in the I. O. S. and in the Android, and they're on web, and it's the same code base. And that's cool. **David:** Yeah, and that's the promise. That's the allure that I think a lot of these frameworks are promising, and I don't know that everybody gets there, and you guys are pretty early, I think, in the broader ecosystem of a company really investing heavily in that true multi platform, because I think Flutter early on called itself multi platformed as a way to distinguish from cross platform. Cross platform, you get one code base, iOS and Android, That's a React Native or Xamarin type approach. multi platform, but it's hard because not that many people are launching desktop apps these days, Or some sort of embedded IOT experience or something. So the web has been this holy grail and it's been super cool to see you guys go down that path. And, you know, something so critical is like mapping those map \[00:27:00\] pins and where those listings are. I mean, that's a critical part of the experience and, really streamline that developer process to get. One code base, and has it changed? Like, I have to feel like it's probably changed your ability to maintain that in terms of portability of engineers, because now you can just have a single engineer who can work on any of those platforms, right? you don't have to just go talk to the web team, talk to the mobile team, get them to make sure they're doing the same thing.  **Chris Synan:** Yeah. That's exactly what it was before is kind of, Oh, well, you know, mobile's outpacing web, web's outpacing mobile you know, you're starting to get a different experience. so now we've got, we've got the one team that's producing all that stuff and it freed up other people to go do, other cool stuff. but it's been great. And I think in the beginning, I think we're all a little bit wary about it. And, now looking back, there's, there'll be a story and the first reaction from the team is, Oh, this is perfect for Flutter. We can build in Flutter. We can put it on the web. We can put it, at one point it was, come on guys, try this. And now it's. That's where they want to go first. **David:** Awesome.  **Chris Synan:** been, it's been really cool. **David:** and \[00:28:00\] that's what I was going to ask next is,when you pick some of these new choices, you make a big decision like this, a big transformation. There's a lot of promise. There's a lot of, selling points, marketing talk around all these great values you're going to get. And so my question is, you know, now that you've got some time behind you and you brought the team along, what kind of improvements or outcomes have you seen as a result of this? did you, were you able to really convert over to the new thing where some of those old problems aren't recreated? how are you guys doing now?  **Chris Synan:** Yeah, I think we have. And I think the one that, that I just remain so proud of. and so proud of the team and so proud of the leaders for really sticking to this commitment is the level of test coverage that we have. that was one of those, Sort of reboot things that we really had to do. It was, it's easy to get caught up with deadlines and just skimp on the tests. And it takes a lot of discipline to say you don't check that in unless there's, code coverage. And then I think there's also a little bit of a struggle there with, Meaningful tests and, not as meaningful tasks, right? \[00:29:00\] So we just decided that we're going to do this and we're going to commit to 100 percent test coverage. And that's just going to be, you know, part of our culture going forward. And we've maintained that and we've really been able to do that. So,right now we are I think we are four tests shy of 3, 500 tests for our app as of,as of the last sprint. yeah, 3, 496, tests and that's pretty great. You know, before we, it was, it was in really bad shape. here's the other thing that we've gotten. And again, this is, look, Flutter has been a really important part of this. There's been a lot more kind of larger transformations also that have supported this, our CICD practice,the level of automated tests, the way that we've got those wired in,really full on agile adoption, et cetera. But if I rewind to when we first got here. We were lucky to do one, maybe two releases in \[00:30:00\] a quarter, and they were ugly. They were ugly. it would take three weeks to get it out the door, and then it would take about another three weeks to stabilize the mess that we'd made in production. It was just so bad. And, you know, a big part of this transformation,now, We're in a much different story now. we release every single week. We do a release. We alternate. Currently, we're alternating web and mobile every other week, but we're so good at this. At this point, we could do whatever. In fact, we've got a so dialed in that we could do an individual feature change. a developer checks that in. A product manager clicks release in JIRA and it gets released. **David:** Wow.  **Chris Synan:** So we've just really, really transformed  in so many ways. Yeah, it's just, it's **David:** I mean, I, I think what you just described to someone who maybe hasn't been through the BuildTrain experience or that pain you were feeling of one, two, maybe three releases a quarter. And then all of a sudden you're able to whether you want to or not, the ability to \[00:31:00\] ship by the click of a button and the confidence in that you're going to be able to release that, that rapidly. Is such a massive, shift. I think it's hard for people who haven't been through and seen that, it's hard to really, truly comprehend the impact that has on the business. Cause now you guys can, I would imagine like next week if somebody comes and they're like, Hey, I got an idea, right? Or there's a thing we need to adjust or change or new thing we're trying to roll out. And you guys have the confidence now that you can do it, right? it's not going to be some, okay, hold on. Six months from now, maybe we'll get it out. Right.  **Chris Synan:** Oh, yeah. I mean, our predictability now is like, we've really got it dialed in. we're doing like data science around our throughput and it's been fantastic. It's yeah,it's really pretty amazing. In fact, this one like just blew me away. we had this, you know, kind of a leadership meeting. and my counterparts in the business side basically came to us and said, we need you guys to slow down all the releases. And that just killed me. I'm like, Wow. that feels so good. That's just, that's such an **David:** Mission accomplished, man. \[00:32:00\] Congrats.  **Chris Synan:** can do this. So fine. We've got feature flags. We can do this, but wow. What a transformation, **David:** Yeah. and think about how many businesses out there, probably where you guys were when you started, Where the technology isn't seen as this like growth platform. It's seen as this sort of difficult thing you have to manage, right? Like this, like difficult impediment to realizing your product vision. Cause you got to document it. You got to get it into the developers. they got to wrangle a bunch of stuff and then you got to test it and you get it out there and it's always painful, right? And one thing I've always thought about relative to our industry. Is this thing like where it's just a common thing where people say I just always expect everything's going to take twice as long and cost twice as much. And what a horrible, like reputation for our industry, but I think a lot of that's just because of the systems we built up, the technology debt you establish, and some of this stuff, like we're talking about some companies are enormous. They've got. 30, 40 years of technology they built or something. And I get it. It's complicated, but when you get to that clear air where someone can have an idea and the business believes \[00:33:00\] in the team to execute quickly, that's a massive competitive advantage compared to other companies that maybe don't have that  **Chris Synan:** Yeah. it. is. It is. And you know, David, looking back on it,  I think there's just some, there's just some important elements of that because I think there, it really does depend on a certain kind of culture. Right?for example, the ability to have a difficult conversation,listen, just saying go faster. It's not really very helpful, right? what I'm saying is. Sometimes to go faster, you have to start by going slower. You have to start by discipline. You have to put in this investment. I actually have to work less on the shiny features and a little more on getting our act together, right? And so there is a large amount of trust involved in that. For me to go back to my business partners and say, Listen to me, I know that you want all these things, but I need you to trust me. If you give us some time and let us go clean our house, then we can come back and then we're going to be ready to rock and roll. Then we're going to be moving fast, right? and so having \[00:34:00\] that level of trust is really critical because if you don't have that, I don't think we could have done this transformation with this team if I didn't have that level of trust and didn't have that level of partnership that says, You know, trust me, this is going to look kind of scary for a minute as we just rip this thing apart. It's going to look scary, but you got to trust me on the other side. You're going to go, wow, that was worth it, right? **David:** And to stick  **Chris Synan:** know that. And I think that, not every place that I've ever worked, not every culture that I've been in, would, would necessarily have been one where that could work. I'd love to say, Hey, parachute me in anywhere and I can work magic. I don't know. I mean, I think that, that really, that sort of culture and that level of trust with the leadership team is so critical. It's such a critical piece. And then, you know, I think the extension of that is you have to cascade that down among the team as well, right? Cause I think what the teams get locked into is this is the way we do it. And I've managed to keep this job for a while. So just \[00:35:00\] let me, continue to do this and you really have to, you have to help them break out of that, that shell and just break out of that, very sort of fixed mentality. And so we kept challenging the team and saying, Guys, are you happy with this? Or do you think we could do better? And they would say, yeah, we could do better. Because the teams know. They know. Every day, they fight against some dumb thing and they go, I wish that was better. And so I said, make the list. And I've got your back. We're gonna go figure these things out. We're gonna line them up. And we're going to knock them down and we're going to go figure this stuff out. But it was just an extension of that same, trust, right? It's basically an extension of that says, you guys, I'm going to give you the runway. I'm going to give you the air cover to go figure this stuff out and to go solve these things. Okay? And eventually they come around and they say, okay, I'm in, let's go do this. And that was a big part of that whole mindset shift that had to happen with the team to get everybody \[00:36:00\] to do this. **David:** That's amazing. questions. Are you hiring? I would imagine there's probably a lot of engineers who would be listening to this and be like, wow, how do I get. How do I become part of that culture? And cause we talk about technology so much and the choices, and even before when we were talking about, Oh, react native or flutter native. And I think so many people get caught up in that flame war of the actual tools. And the reality of it is that. Tools. They matter, of course, but not really because what matters is these sort of softer things you're describing that leadership culture, right? that trust in you and the ability to stick with it. When you make that choice, they, the other thing I could say, Oh, okay, I'm going to trust you, but only for one month after one month, if it's not working out, I'm going to pull the plug on this. And then that could be really damaging too, right? Yep. And then how you even approach this, like in our conversations, this obviously isn't the first time we've talked. And one of the things I always thought was just really cool about how you guys approach this was really embracing this as okay, we're going to make a product change, some design changes, some overall things we're trying to do differently with the product \[00:37:00\] itself. We're going to make these technology changes, but along the way, this is a very unique opportunity for us to reset our culture and to establish a way of behaving, working, supporting each other and collaborating and that trust that. Really takes that leadership culture all the way through the team. And it sounds like, that's really changed how you guys operate and has just created so much trust and confidence, not just with the leadership, but like in the whole team. Which is just an incredible outcome.  **Chris Synan:** Yeah, it has. And it's also, it's one of the, one of the real joys of leadership is.you believe in people and you tell them, I believe in you. I think you're a superstar. I want to give you everything you need. I mean, my job is to get out of your way, remove roadblocks and just have your back. Right. And to see people rise to that challenge and people that in the beginning were, maybe a little, suspicious or they weren't really on board, they weren't really engaged, whatever, whatever the case. And then to look, throughout this journey,and just to see the way that the team has \[00:38:00\] believed in them and that the way that they've come back around and found their role and found their niche and then are really showing up as leaders. And it's just been, it's just been such just such an amazing thing. yeah, the technology we've built is awesome. But to really see people develop as professionals and to. take that chance and put themselves out there and just try to, speak up and try to have more, you know, impact and more influence and, just get more done. And it's just been, it's just been great. It's just fantastic. **David:** So to, to cut, thank you, Chris has been so amazing and you're such an inspiring guy and great story here and it's such an amazing outcome and like, just kudos to you and to Keller Williams for having the confidence and leadership to To do that and to make that change, not only for the product, but like for the people, like you're saying, it sounds like you've made an incredible team where people are empowered and feeling good now that you're in this moment. Right. And you have had some of these changes and transformations, like, what does the future look like for you guys? Like, how has it cleared \[00:39:00\] the skies for you as you look ahead? And fundamentally, how do you think it's changed sort of the confidence of the business or what the future holds?  **Chris Synan:** so I think,you know, we've got kind of various pieces of technology in the organization and, Partly because consumer was a little bit insular, right? and also because it was, it's not the largest, code base or platform that we have, there's much bigger, there's much bigger platforms. but I think one of the things that's afforded us a little bit is that flexibility to, to get creative and try new things. and so one of the things, almost like an incubate, an incubator of sorts within the larger organization, right? And so there's other things that we've said, let's try this or let's, let's take this different approach. And where we've had success with that, we now have other areas within the organization that are looking over going, Hey, that looks pretty cool. what you guys doing over there? Right. And so I think that's the phase we're in right now. And that's not to say we're the smartest people in the world. And we invent everything. That's not what I'm saying. I mean, we've certainly \[00:40:00\] leveraged a lot of great stuff from the other teams and that's how a good culture works. but we did sort of,in a few cases, just throw out the old and adopt a new thing. and in some cases, that's starting to catch on. So that's part of the process, that we're doing right now. and then just, as the market continues to kind of evolve and interesting things are happening there, I think where a couple of years ago, the business would have all this really, entrepreneurial, really innovative ideas. And we were saying, hang on. We gotta, we gotta do some cleanup here. Now we're saying, bring it like we're ready. Bring it on. that's really sort of the turning point that we've had recently. and that's exciting because I want to move that fast. I want to go do all these amazing things. And it's, you know, it's frustrating when you're just kind of cleaning your house and trying to get all ready. But I mean, now we're ready, now we're, opening the doors. We're open for business. Let's go. let's make this happen. **David:** That's amazing. And that's what every tech leader wants. Cause you want. You want more, right? You want more budget, more \[00:41:00\] teammates, more opportunities, and that comes from when the business has confidence that you're going to be able to deliver the bold ambitions and the fast moving ideas that come with it. So that's amazing. let's wrap it up. one sort of final question maybe is, as you think about, where you were a couple of years ago, or earlier in your career, or if there's some other company out there who's maybe finding themselves in a similar position, right? Where you got tech debt, you shipped slowly. there's a lot of frustration maybe by the business leadership. what advice would you give to them in terms of how to get started on something like this? Like how to get from that zero to one, get out of the inertia that maybe is holding you back and take a leap.  **Chris Synan:** Yeah. that's, that's a big question. and, I think I have to come back to, you know, trust. I think that, that trust is such a big part of it. becauseI think,I think you, you have to get a very clear picture of where you're at, right? there's no software out there that doesn't have bugs, right? All the software has some issues, right? \[00:42:00\] So what are your sort of business challenges? is it just that things are slow? is it that the quality is poor? I think you really have to get that assessment of what's working, what's not. and the team culture and the communications, there's so much, we didn't even really touch on this, but there's so much that we've done. Also, just around communications, around transparency, around kind of interdepartmental, dependencies, and how you communicate those, and how you plan for those, there's just, there's a lot of things,there's a lot of things to it, but I think, So much really has to start from the leadership team. And so the commitment to excellence really has to start there. and really the willingness to take a chance, the willingness to do a scary thing. I mean, you know, you're not going to get the amazing things in life by just playing it safe all the time. sometimes you have to be willing to say, listen, this is, you know, I'm talking about doing a kind of scary thing here, but if we do it right, and here's why I think we can do it right, we're going to leapfrog. \[00:43:00\] And we're going to be way, way, way, way ahead, but it does take a lot of trust. It takes a lot of clear communication. It takes a lot of demonstration that you've done the diligence, that you've got a plan and that this is something that's going to work. It's not a crazy long shot. It's a very, very well considered. **David:** Wow. Well said. Well, thank you very much, Chris. incredible story. you're an incredible guy and I appreciate you spending some time with us to tell the story,and tell about Keller Williams. is there any place people could go to learn more? Just kw. com or where else should they go to learn more about your team?  **Chris Synan:** Yeah, KW. com. Go check us out. It's,we've got the two letter domain, which is pretty cool these days. Two letter. com domain. but yeah, there's information there about,about our development team, about our technology. we've got,dev hub portal where innovators can come in and hook into our systems and do all sorts of really awesome, innovative things. So definitely come check us out. KW. com. **David:** Awesome. thanks again. Appreciate  **Chris Synan:** Yeah. My pleasure. Dave is really great. \[00:44:00\] Talk to you. I appreciate the opportunity ### Scaling Mobile Platforms to 100M Users Source: https://verygood.ventures/podcasts/thiago-ghisi-nubank-scaling-mobile-platforms-to-serve-100-million-customers/ Join David and Thiago to explore the challenges of scaling mobile platforms for millions of users, the importance of fostering innovation, and how strong engineering culture drives long-term success. Tune in to hear how Nubank stays ahead in a rapidly evolving tech landscape. {% audio_embed url="https://player.cohostpodcasting.com/972c8605-a4dd-43d7-a975-a95e8bba3e40/63a6c538-be34-4ade-89f1-80258ff87c0c" /%} ## Transcript **David DeRemer:** Hi, I'm David. This is Build to Succeed from Very Good Ventures. Today we speak with Tiago Ghisi, Director of Engineering at NewBank. They run an app that serves over 100 million customers on multiple platforms in many countries. With a thousand screens and delivering updates every day. We're going to dig into how NewBank manages this complexity with Flutter and server driven UI, and stays at the cutting edge of mobile. So let's get into it with Tiago. Okay, Tiago, welcome to Build to Succeed. Thrilled to have you here. Um, you've had just, uh, such an awesome career. I know you were at ThoughtWorks, Amex, Apple, now NewBank. All incredible companies, just amazing, uh, resume you've got. And so, um, I wanted to just get us started by maybe you could take a look back and kind of tell us a little bit about your career, all the places you've been, and maybe some themes of your career that you've identified over time. **Thiago Ghisi:** Amazing. So, thank you so much for the invite, David. It's a pleasure to be here. I think, like, you, you're doing an incredible job, like, by interviewing folks on the mobile slash Flutter community, and it's a pleasure to be here talking about what we are doing and my career in general. So, I think, like, my career, so I started Almost 20 years ago. I would say I was the typical, started with like Java, then kind of like did web, then like did QA, did other things, and then eventually, maybe back, I don't know, it was 2009, 2010, started work even before Android with JavaME, right? Did some projects back in the day with that and then transitioning to Android and then like, After that, I did a lot of like full stack engineering, like migrations to the cloud. I'm a very generalist person by heart. And, over the last, I would say seven, eight years, I have been on the engineering management path, so. More on the managerial side of things, but still pretty much working with mobile all this time. So like, even though I, I'm not, and I'm generally against like people that only work in one platform. I have been working a lot with mobile and a lot of fintech over the past like eight to 10 years. And like to, to talk a little bit about my career, I think like two general themes that I. That I have identified to myself, and I have written a lot about this. I think one is, is about kind of like being the glue person. And like the other one is about being a full stack, engineer, or I strongly believe on the idea of being a full stack engineer. Not a jack of all trades, but a jack of many trades, a master of some, right. And I try to apply that into my career, like moving from, let's say, mobile to infrastructure, to sales engineering, to consultancy, and like going back, you said ThoughtWorks, I think like have been a consultant myself. That's also been a great background to me. Like, I think like. The idea that I had to work with clients in the past and have seen other very senior engineers acting in, like, more in a consultancy mode gave me skills, foundational skills, I would say, that prepare me to then be a, a good engineer and especially a good, manager and director, and on the other side, on, like, being the glue person, right, I think, like, I always try to be the person that was filling the gaps. Even if they are not part of my job description, right? Like I think Tony Riley has this amazing talk called Being Blue, right? That's about, it's all about, it's not about kind of like how you grow using that as a leverage, but it's more about how sometimes people like women or like diverse or like minorities in tech get exploited because they end up doing all those words that don't get recognized. As the, they're trying to be promoted or all those things. Right. But I think like there is an upside of doing that because like you ship a lot of projects, you become essential and you can navigate across the end to end. I think like having that like end to end ownership of all the projects of everything and being able to do the 80, 20, like you're not expert by any means on everything that I did in my career, but I know the 80, 20, right. I know, like. How to do many things and in some areas I can go very deep, not in all areas. So I think like, those are the things that I would say that allowed me to grow in my career and RCO kind of like teaching me lessons again  and again. **David DeRemer:** I love that. Yeah, I think a lot of young developers, we, you and I maybe came up through different times, and if you're a young developer coming up now, you might feel the pressure to specialize super early, and, yeah, I, I agree. I think you gotta get a nice rounded perspective. It's the only, like, if you're just doing mobile development, and you've never done any back end APIs, it's maybe hard to know what the peep that team is facing, you know? yeah, yeah. so you're kind of describing that like T shape idea, right? and I love that idea of like, maybe not jack of all trades, master of none, but maybe jack of many and, you know, maybe king or queen of a handful of them, you know, if you're going through the, for the card deck metaphor. **Thiago Ghisi:** And David, I think the pressure to specialize was always there, at least in my career. I think I was always like, there was always more attractive to be the expert in one area, because there's always like super bright and shiny things coming up. But I think you almost have to fight against that. in some ways, In a very assertive way, like try to fill the gaps almost like if you see your career as like a skill blocks that you see almost like a Tetris game and you're trying to feel the the whole line, right? You're not super narrow or one thing. But, anyways, I think that is like good. There is a good and bad of like specialization, but I'm always I try to avoid that even though like. If you stay a year or two years on a project, you get plenty of specialization just by going deep on whatever the project is. **David DeRemer:** Yeah, it's the same thing. I was just talking to a, a dad who lives nearby. I mean, you're talking about sports these days, kids from a very early age specialize in one thing. If you want to play travel baseball, you got to. Do it all four seasons and he's like back in the day when people would do different sports every season. You, you learn different, mental models, you develop different muscles physically and that ultimately it makes you better in the long run. And I think that metaphor kind of tracks to this. You need to kind of round yourself out a little bit to build those other muscles to be able to react when you get into some ambiguous, uh, moments or difficult challenges. And maybe that corresponds to that your ability to be that glue function as well because you feel comfortable and confident flexing into a lot of different situations. real, real quick, like to the point of you being a, you know, an engineer, into engineering manager, what was the hardest, thing for you to learn or adapt to switching more to the management side? **Thiago Ghisi:** I would say it was definitely some aspects of people management. I think one thing, even though I was already doing a lot of what the engineering manager is supposed to do, even as a senior engineer. Helping to break down projects, talking to stakeholders, all those things, right? There are definitely lessons there, how to navigate in organizations, like when to escalate, when to Like, how do you avoid triangulation? All those things, right? But I think the hardest part was definitely to learn to give feedback effectively and to learn to delegate effectively. And I'm still learning those things, by the way. I think, one mistake that a lot of managers make, and myself included, uh, is, Either you are the manager that's shielding all the feedback from your people, right? Uh, or you being the pass through, right? You're the one that just passing whatever you receive without actually processing, without, without actually compiling the thing and actually asking yourself, do we agree with that? Do I, do I see any that that person is that bad or is doing that? Did I? I think I made many mistakes in the past, kind of like by being more a pass through layer versus like actually digesting and compiling any and delivering something to the engineers or report to me that were kind of like more. more actionable in some way, right? and it is a skill that's very hard to manage. And on the delegation side, I think it's like, it's very easy to get to the point where you just break down tasks and give tasks to people. And don't let them actually  be the, the task master, let's say, and taking the ownership of the full project because people are going to ask, what should I do next? And it's very easy for you to get into that aspect of like, Oh, my, my job as a manager is to, to break as many tasks as possible and to, to be the one that's gluing it all together. Right. I think like those were very challenging skills to me. And I think as you scale, as you get more people on your org and you have bigger projects and more managers reporting to managers, all that, there are other challenges, but I think at the beginning, those were the two that I remember. **David DeRemer:** So now you're at New Bank, right? and you've had a, this long, lengthy career. Tell us a little bit about NewBank, just for people who are, who are not familiar with it. Cause I know it's a Brazilian company. Um, you're in New York obviously, but you know, it's a Brazilian company. Why don't you just do a quick catch up of, of how you got here and tell us a little bit about NewBank. **Thiago Ghisi:** Yeah. So Nubank at this point is, I think the largest fintech outside China by number of active customers. I think like, so we are a financial service company. I mean, we cannot say we're a bank, and I think like the Nubank story is very interesting, especially to me because I'm Brazilian and the bank started in Brazil. Thank you. Was that they went after, and they were at the right time to, to try to like pretty much revolutionize how banking was done in Brazil, right? By instead of like having branches and all those things, they went, what if we did something that's a hundred percent mobile? What if we actually try to like have a credit card that was like, uh, Like, no fee, right? in the U. S., it it seems crazy to think that you don't have that option. But I think ten years ago in Brazil and many, many places, Out there, that is a very hard, it's very hard to have access to credit, right? I think like Nubank was a pioneer, like, in Brazil with like doing so many things early on, and then like, I think we are, we are now not only in Brazil, but also in Colombia and Mexico and in growing like there. And, I think at this point, more than 50 percent of the adult population in Brazil is a Nubank customer. So. Very, very big there. And I think another, like, there are many things about Nubank, but I think the other thing that was remarkable was when the Brazilian Central Bank decided to launch PIX, that is kind of like, uh, it's kind of like Zelle and other things that we have in the U. S., but it was widely adopted and mandated by the government and like 100 percent of like no fees and like instantaneous transfer. Nubank was like the app that pioneered that and like today is responsible for a big, big amount of the transactions that go over PIX in Brazil. And yeah, so I think, we did the IPO, I believe was end of 2021. And since then we have been growing a lot and we just crossed this year, a hundred million customers. That's **David DeRemer:** Wow. hundred million customers. Wow. And a mobile first company, at that scale and, um, you know, coming out of Brazil. It's just such an incredible story. And of course the reason I know you and I know of NewBank is that, um, Flutter was a big part of that story. And obviously Flutter is a big part of BGB story and NewBank was an extremely early adopter of it as well. So when you think about building something that big. And, you know, making a choice that is so important, if mobile's core to the strategy, how, how did that decision get made, you know, and, and sort of how, how does a big, bold, innovative decision like that, become a trusted, confident choice when you're betting your whole company on it? **Thiago Ghisi:** So, I mean, first thing that I have to say is that that was before my time, but I have a lot of context and, uh, and I can give you all the details, but I think, if I had to answer that was like. The answer would be by doing POCs, by trying out the different technologies, by seeing what's possible to do with the alternatives that were available out there. But David, I think like the, the thing that's important to mention is that Nubank actually started as a native, with native apps, right? and we, we actually had like a, even a Windows phone app back in the day. So we started and for like more than five years, all the way to 2018 or so. It was mostly native and, Nubank, one of the product areas at Nubank had already started to play with some cross platform frameworks. And I think with React Native and, uh, had been super successful, but it was only one product out of the whole app, but the core, the foundation was always native. And then 2019 came to a moment where. The mobile engineering team at the time was feeling completely overloaded and not able to, to deliver everything that the business needed, right? Everything that was on the pipeline. And at the time, I think like mobile was being a bottleneck to innovation, bottleneck delivering features and different areas, different engineering teams in the company were trying to innovate and As you know, it's pretty hard to, to staff engineering team when like among many things, not only back end like databases and all those things, because at the bank we have like full end to end ownership, every single product owns end to end the infrastructure, even though they leverage platforms, right? So I think, like, the idea of Flutter came when, there was a moment that it was, the backlog was, six months to nine months of work. It was a central engineer, like, mobile engineer organization that had maybe 50, 60 people that were, like, overloaded with work. And we need to scale in some way. and then it started with like a few folks doing POCs on that. And then there is a very famous blog post. That's way before my time where Nubank actually published the rationale and all the dimensions that Nubank considered to, to make the decision. And I think like it was very. very tied between Flutter and React Native in many aspects. But then I think developer experience and the, the community and the fact that it was decoupled, all the dependency issues with React Native, all the build problems it had and like, and the fact that you could keep the native side and the Flutter side working together for, for, for a while, while we migrate things, while you get more confidence. Was also a big decision factor, but it was not an easy decision and it was very traumatic in some ways. I would say that the transition, when, when you do that, like when you transition platforms, and I think like you had a podcast interview a couple of months ago about the Betterment story. And I think there's a lot of similarities in there. It's like you, you get folks that are very passionate about iOS and Android, and the fact that you have additional layers, it's not. You're going to be kind of like at least a couple of months behind, right? Whatever Apple and Google announced to catch up. And like, there's a lot of trade offs, but I think I would say it was a decision made out of like, uh, frugality almost, right? Because it was very hard to maintain every single feature paired across two, two platforms. And then. Once you have that, usually have like, sometimes one platform is, is ahead of the other in terms of features because you have like different people with your own species like specialization and sometimes they're not all available with the same thigh. Sometimes you have crashes in platforms and all that. And I think Flutter filled the gap in so many ways because it made easier for folks that were not necessarily, didn't have a mobile background to To understand things and to be able to contribute, right? And also it reduced the amount of code we had, because now like you no longer had like two different code bases and all the, all the drown backs you have with that, right? So I think like the context was, was there, like it was the need to scale. It was like trying to, to find a platform we could leverage for the next, like many years. And, uh, it was like something we could. Get started, but not necessarily migrate everything at once. And I think that was, yeah, that was what happened at the time. And we had our challenge because it was early on for Flutter, right. It was like, we, we created frameworks. We, we had a lot of like issues integrating between a native and like all the, the Flutter sandwich problems, all the, you know, integrations with, with native SDKs and all that. But I think, uh, on the other side is a much, it was a much better path where we could have, instead of like, I don't know, 50 to 60 engineers to have hundreds of engineers working on the code base and delivering things independently. And think like the modularization is also easier. The fact that we transitioned to a monorepo back even before we adopt Flutter actually helped with so many other aspects. So I think like. Yeah, like, a lot there. **David DeRemer:** Yeah, it's, it's interesting as I, we hear these stories and sort of the decisions and, and, um, it gets so wrapped up usually in like a technology choice, people start comparing the programming language, the framework, you know, how maintained is it, you know, what's the frame rate performance and all that sort of stuff, and, and I think the nuances, I've talked to more and more people about this is, Kind of like those things don't actually matter that much to the decision. I mean, they do, but they're like table stakes, right? Like, of course the app has to achieve your performance goals. It has to deliver a good experience to the end user. But what's been fascinating to me is that when people talk about these things, they're really talking about the human element. They're talking about developer experience. They're talking about portability of people to be able to work on things. They're talking about getting out from that huge backlog and the stress that comes from that. It's actually like solving all these organizational problems and like the thing you said before about like you can't keep up with the business kind of, right? Like the technology becoming this bottleneck. So I feel like, you know, in a way where the App Store came out in what, like 2008, 2007? and you know, Oh, we're in this like second or third era of it where I think people are like, all right. we no longer need to just have apps on these platforms. We need to do, we need to do it in a way that we can move fast enough and keep up with the business. And so it's just really interesting to me that it's almost like more of a business and team sort of culture and human relationship choice than even like a, you know, programming language or some other technical choice. But you guys are also, um, you're on the forefront of so many things and, and, uh, being so early to, to a tool like this, like an ecosystem or a community, I think companies like NewBank are also at the forefront of sort of adopting some of these new technologies and solving sort of the, the leading edge of, of challenges. And one thing I know you've been talking a lot about recently, um, is like the server driven UI that you're adopting at NewBank these days. And I was wondering, um, if you could, you know, dig into that a little bit. because I think server driven UI is something, for whatever reason, actually, in the last three months or so, I've been hearing about it everywhere. Like, it seems like everybody I talk to is like, oh yeah, we're going down server driven UI. so maybe just start, like, can you just explain that concept for, for, as you understand it and, and how you're using it at Nubank? **Thiago Ghisi:** So, I think, like, the idea of server driven UI, I think, is, is a very old idea, and it is, like, I think I, I remember hearing about variations of server driven UI, like, even, like, five years ago, even more. Is the whole idea of what if the app could be driven by the backend, right? What if we had, I mean, it starts with like, what if we can have the whole content being like on the backend and then, Oh, what if I can actually drive different, different designs or different like variation of the same screen from the backend, but then like very quickly you get into the level of complexity that like, Oh, this is not going to scale forever, right? Like it's and people make a lot of mistakes, a lot of similar mistakes when they're, they try to go on the, let's say the backend driven, uh, content, in web app, in mobile apps. so the idea of like server driven UI is like, is expansion on that is like, what if you could like, not only have the content being dynamic, but what if the whole design system could be dynamic? Right. And then, and then you get like, Oh, what if, like, instead of returning a JSON that I have to parse, and then I still have the whole UI on the client, what if, instead of sending that, what if we have like a whole dynamic design system and I could also send functions and I could also send the whole like, UI structure from, from the server side. And the app is basically an interpreter almost, right? The app can compose itself, uh, because everything is, is, is coming from the backend and the app has. The only intelligence that you have on app is the ability to get whatever the backend is sending in, like, and build a UI that follow your convention and, be able to interact with the user. But then, like, there are many, many anti patterns. And I think at Bluebank, we are on, I believe, the fourth. Generational server driven UI. I think we have tried a lot of things. Like initially, I mean, even, even I would say before we migrate to Flutter, there was attempt to do like some server driven UI with like forms. And I think like for, for flows that are more like wizards, you can easily do. Like a variation of server UI where you can compose almost any kind of flow and like you can adapt that for almost any kind of like, basic form, right? And you can do that. And if you have a lot of those in the app, it can pay off very quickly and you have the ability to quickly change things. And do like, let's say, basic stuff in the app from, from the backend, especially if it is a not so sophisticated UI and not so like intense, uh, like you don't have very intense needs of interacting with native and all that is like very standard, right? And then the next variation after that is like, is to what if you actually have a full interpreter on the client. That not only can, let's say, build screens, but can also have local functions, can also have, like, the whole logic to handle not only one screen, but a whole flow, right? What if you have, like, a thing that you're able to send, not a particular screen, but the whole flow, like, all driven from the backend, and do that? And what if, then, you could use the same language that you use on your backend service? to write the, the languages, what if they could live on the same repository that you have your BFF or like even your, your APIs, right? And, and you get to a lot of like different challenge and is more, Oh, now I need to have a way to send functions that are executed on the client, but I send in code that's pretty much, uh, that can be compiled or interpreted. Like, in Dart or whatever language, I'm not like, because it's unpractical for a lot of things. Every single time you have a click. You have to send, like, a network request to then do something, right? You need, like, you need to build a lot of things to be able to support that. Now you need stack trace. Now you, like, now you have, like, almost a full blown interpreter there. Now you need, like, to send But then you get to a, like another trade off that is now you have very big payloads. Every time you open a flow, you have like a huge payload instead of I don't know, 30 kbytes, like a hundred kbytes of JSON. mean, even if you gzip and all that, now you have like, I don't know, 500 kbytes because you're not only sending the content, you're also sending the structure over the network, right? And now you have also additional layer of like processing because you cannot render like all the optimizations that you have at build time. Now you don't have, because you might have different permutations of components that need to be loaded and they are not, let's say, like in memory already. So you get to a lot of those things, but the, at Nubank, so we have played a lot with server driven UI. And, uh, we, we have a very successful case of serverless UI, but it was not something we built overnight. It was not something we built over the last three months. It was something that took years and like many attempts and something that we were able to leverage, like the way that Flutter works, the way the material works. That's kind of an almost big team on the SDK, right? And like build like a design system on top of that and be like, almost be able to be decoupled from what's on the client so that we can send everything like over the network. And like, there are many, many variations of that. Like we created DSL so you can code enclosure on the backend. And then we have like some interpretation, like via. What is that? The protobuf that gets on the warrior. And then like, so we have a very strong contract between the backend and the client. So, and like, I think like the high level, I think that's like server driven UI is the ability of like having any dependency on the release train or on your, your new build getting to the app. Uh, but then there is different. Also styles of server driven UI, right? I think like that is, uh, the style that you check for the new, if there is a new version available at the startup and then you pull the, almost the binary and you have that kind of mechanism that the user is always one version behind. So every time they open the app, we load the new version, the background, the next time they launch, we get the latest. But there is the server driven where you actually, you don't do that. You don't, like, you have a binary that's static, but it has interpreter. And every time you, you call the server side, you get the, the entire thing that we work on that version. And you're not, Like checking once, Oh, do I have any updates? So there are different variations of that. **David DeRemer:** It's, uh, and so correct me if I'm wrong, the business case for why you'd want to do this is to get around the build train and the releases you have to put out to the app stores, essentially, is that the, like, what drives somebody to go down the server driven UI path? **Thiago Ghisi:** There are many reasons. I think like the initial reason might be that you want to do continuous delivery. I would say like the, the biggest reason in my opinion is, is that it's almost impossible to. To do bug fixes or to, it's like, it's very hard for you. It's like impossible for you to to ship a new binary every time you have a new PR merge, right? Like forget it. User is not going to be downloading a new binary every day, like multiple times a day. Then like there are some like business needs that's like if every time you need to like to do a bug fix you need to Wait all the users to catch up on the new version and every time you need to launch a new feature, you need to make sure that the user base is lagging on a version, right? So it's like very challenging. to coordinate launching of things because then you don't have like the code or that version. So users are not going to be able to see at the same time. the other thing is, so I think like I mentioned, yeah, the, the limitations release training, shipping the binary multiple times a day. And like the hard thing about like bug fix. And the other thing is like, even though like Flutter is very good technology and very easy for, for engineers to learn, it's still not super high leverage or, or how can I say? It's like, there is still things you can do, especially if you are on a big company and you want to make as easy as possible and create platforms that abstract away the nuances in having Imagine that you have, like, as we have at Nubank, we have like a 2, 000 engineers org, right? Like we have 2, 000 engineers that are, that like, not all those 2, 000 engineers are working in product facing squads, but like a good chunk of that. So he's like, imagine that every single one of those squads now, like they own like the product end to end. And if you have, every time you need to build a new screen or build a new flow. You need to go and do the, the Flutter screens and then you need to go the server side and you need to know like database and all those things like Kafka, like, like Event Driven, like all those things is very hard, but if like, it's not very hard, but it's not very optimal. Right? And then. Imagine if those engineers have like higher and higher level platforms that they can leverage that abstract away many of the nuances and they can do like, for example, like to the mobile flows along with the, the service layer. And imagine if the same way that we deploy like microservice multiple times a day, you can also deploy the, the screens, the flows, like in...imagine that now you don't have a new A.P. I. you have to maintain, because if every time you have to do a new flow, you need to call A. P. I., like, do a POST, do a transaction, whatever, what if we could, like, have a framework that's able to handle that, and we push that, like, all, all those integrations, all those things to the backend? Now it's like, instead of having I don't know, like five engineers, you can do, have like two engineers doing the whole thing. So I think it was more, there is a business demand. And then if you get into things like experimentation, it's like very hard to do experimentation in mobile. If you are operating under the constraints of shipping once a week and you buy an area and every time you need to do like, Oh, we want to do a new variation of this screen. But if everything is server driven. And you can play along with that and you can bug fix those things. It's like the case of server driven UI at Nubank was like, was a platform that was born to solve one project needs that had like something like, it was very bullish on this, that had some needs that were very particular. And then like the moment that the different product areas start to hear about that, they start to adopt organically. Oh, we want to use this platform. We want to contribute. We want to make it better. We want to put engineers to, because they saw that as the next, like the next lever, if like native was here, Flutter was here. I mean, severed UI was here. And, and like that, I cannot say how revolutionary this was for us. **David DeRemer:** Just moving up the abstraction stack, and the same, so if I'm hearing you correctly, the same sort of motivation behind, going something like Flutter, where you used to be maintaining two apps, now you're maintaining one, it makes it easier for those developers, now you've even squished it even more. And I think that maybe gets back to your point about being, um, a developer that isn't so focused on one thing. Cause that would maybe be a difficult way to develop if you were like hardcore only, you know, iOS developer. That's all you do. I only write Swift. and now it's sort of changing the notion of what you're doing to being like, I'm not a mobile developer or a backend developer. I'm a product developer. You know, that's it. Like, what we're doing is we're working on making the product ship. It's very interesting. Has it, has it changed the way that like designers and back end engineers and front end engineers all work together? Because I would imagine the, the role of then the Flutter app is essentially to maintain that interpreter. It's almost like you built your own little like, browser essentially, right? With it's own like, rendering rules and things that you put together. I would imagine how does that flow from design into back end development front end development and because it's the lines are blurred now a bit. **Thiago Ghisi:** Yeah. Yeah.  It is a great question. You know, I would say. And we still have a lot of native, uh, and like mobile complexion, so we still have platform chains. I think like, there are maybe two things that are very important to mention, like Nubank Has like six engineering principles that are very strong in our culture, but there's two that I think are very relevant to this. I think like one is called canonical platforms, canonical platforms widely applied. So it's like, We try to have canonical platforms that are applied widely across the stack. And that like started with like, for example, things like Clojure, Kafka, Kubernetes, right? Those are high level platforms that we try to apply across the stack to everything. Like there is no exception of, Oh, I'm going to build a microservice in like, I don't know, in, in Python or. It was always like, no, the canonical platform disclosure, we, like, there might be some very particular case to build some in another language, but if you do that, you're going to lose all the kind of needs to write all the, the tooling we have to like all the common libraries, all the things, right? So I think like, this is very strong. There's another engineering principle that is called like product ownership or end to end product ownership. That's like more than being, let's say, uh, an engineer, like you are a problem solver. But more, more than this is you're not a mobile engineer. You are like an engineer. So it's like you, you don't like. You solve problems. You don't like force solutions, right? Like what is the problem we're trying to solve? And you go after the problem. You don't go after, like, you don't get passion, like passion about different solutions. Right. So I think like, and then like, this is very important because the only way to achieve that, like the only way to have canonical platforms and, and to have like end to end product ownership is if you have high, super high level and highly effective. Platforms that are enabling those folks to, like, they're hiding the complexion, the chaos as much as possible to, to those folks that are maintaining the product side. And like, so it's not that we don't have front end engineers or mobile engineers is that almost everybody is a mobile engineer, right? Like, is that, it's not that I don't, we don't have infrastructure engineers or backend engineers that. Like with all the, the platforms and the platform teams we have supporting that and all the documentation. And because we're doing everything in one way is super easy for folks to transition and navigate across and to, to find, to find examples and to kind of like, and to replicate patterns or to learn, right, because it is high level and is widely applied, right? So, but it means that we push a lot of complexity to the bottom of the stack. Because in order to maintain like a server driven UI framework that's hiding all the complexity, like to have a good performance is a huge challenge because now we have to deal with all the maybe inefficiencies that we have along the way to, to, to create that efficient, velocity, right? Now you have a lot more, a lot bigger payloads. Now you have like an interpreter on the, you know, now you have like, Like things that if you're building native, you get optimization out of the box. Now you have like platform chains that are, their full job is to find needs and to kind of like pushing the, the level of abstraction even higher. And also we need to provide documentation. We need to provide things so engineers can quickly onboard those platforms and they can quickly put something together that's going to solve their needs. And, and even like, if they have some very particular need and the platform team is busy with other things. They can go in and do inner sourcing. They keep going and contribute to the, to the platform. And I think that was what happened with our like, uh, internal case of like server driven ui because like everybody was like, this is revolutionary. Like now I, even though I cannot do what I want now, I go and contribute to the platform and then I do my future later because then I know next time I need, I'll have something that is gonna speed up things even, even more. **David DeRemer:** You said a word there that I think is interesting like revolutionary, right? Like, the people looked at them, they're like, Oh, this is revolutionary. So let's give it a try. Somehow, um, you guys have established this culture and an acceptance and sort of a momentum around doing these bold, innovative solutions that are hard. Yeah. And that, you know, some teams may not bite off because it might be, well, what if we go down the server? Like you said, it took years and there are probably some teams that like tried it, did it a little bit. It was like, Oh, this is going to be really, really hard. And they probably backed off, but you did it with Flutter. You, you, you know, you, you tried it, you, you, you got into it. You eventually did more and more and more of it. You got server driven UI in there. You've been working on that. What is it about NewBank you think that has, made that team, um, more amenable, like more able to embrace these sort of bolder, more innovative technology choices. **Thiago Ghisi:** I would say, like, is, I think the, the culture is a big thing, but I would say is, it's also the founders in some way that live and breathe that those values is like the, the early, like, The early joiners in the company that are very bullish on this approach was, it was the fact that we established those, those foundational components, those foundational principles early on, and we, we were very good at enforcing it company wide, right? And I think, Ad, like Ad is the, one of the co founders and was the CTO for many years and. He was on the ground with people. He was like even when he was CTO, he was still very influential on like everything we did and like in being super bullish on this idea of like canonical platforms of platform teams of like, you know, Like product ownership of like, like full product ownership, right. And not letting the, whatever, every time that, every time a new engineer comes from a different company to corrupt the vision, to corrupt the, like the, the guidelines and being super, I think like strict is the wrong word, but like, is like the fact that we give time to everybody to learn and like, you know. But we, we try to keep the, the, the canonical approach canonical, I think, like, and by doing this, we have found ways to do things that would be so much easier to say, Oh, I need new, this, this library that's not available in, in closure, or I need to do something that's out of the box. Let me, let me spin a new, a new microservice, or let me break out of the, the canonical way. And like, And so I think like it was a combination of all those things. And also the kind of engineers that join NewBank, they're very passionate about the mission of the company, right? It's to fight the complexity that is to like, to serve like customers that have been unserved, right? Like to give access to credit, to kind of like, to make their life easier. And I think like. is to challenge the state of the school in so many ways, and also in engineering, right? Like, we need to be frugal, like, we need to, with the scalability issues we had, like, we had to make those choices, otherwise it could, I mean, it could go sideways very quick. **David DeRemer:** So commitment to that path, I like that word canonical, you know, you make these canonical choices. It sounds like the founders respected those and pushed and made people adhere to those as the company grew. Because I could definitely see the company grows, you get new leadership. People change over and, uh, you know, Sam in, in the talk with Sam from Betterment, he talked about these like lava layers that come over and like a new lava layer comes over and you just get these layers that are constantly eating the thing before it and before you know it, there's just all these little changes over time that became, make it, uh, unmaintainable. Sounds like, uh, the founders there put, developed a culture that, Help people to stick to their guns while embracing innovative choices, right? Along the way. Um, cause certainly you've made a whole bunch of those. and to that end, I was wondering, like, um, given all the interesting innovative choices and challenges that you guys have overcome and doing, like as you look ahead with technology, um, what are some things that excite you? Or even maybe, maybe frighten you a little bit when you look ahead and changes, like what are the challenges or opportunities that you think are coming for engineering teams? **Thiago Ghisi:** Perfect. I, I will answer that, but I, I have to go back to add two more things that I was just thinking about, like, what made Newbank's culture so successful? I think, like, Being a bottom up culture and having full ownership and autonomy to do things quickly without actually, like, being super top down, where, like, the values that were always there, they're still there, and those are, like, those are, so crucial. Like, the fact Nubank was pushing to have like on the same squad, not only engineering product, but also customer service also like everything that was marketing, truly, truly like multi, multidisciplinary teams that they could propose the roadmap, they could propose the OCR, they could propose the path to get that product to market fit, allow the company to market. scale very, very rapidly and with like many things at the same time. So I think like the, the level of autonomy and trust and like, and like, in the leverage on top of the platforms were like also another big thing. And another thing, David, and sorry for doing this, I think like for server driven UI, there is another thing that is huge is governance, because like when you have everyone working on, even on a monorepo on a big,  native app, you have to all be like passing the same build and compiling on the same binary, right? But the moment that you're able to like do server drive UI, almost the idea of micro front ends, right? Where you have the coupled chains moving different repositories and deploying different places and. This architecture of serverless UI is able to keep it up even with folks deploying different speed. And if one thing fails, you don't fail the whole app. You don't compromise the whole app, right? And you are able to create something that's robust enough to handle all those, those permutations. And you have like a revolutionary, like almost, uh, modularity that is very hard to get in mobile. Like you're basically pushing mobile to be a distributed system. Instead of being like a client side  thing. **David DeRemer:** It's a lot. There's a lot going on there. Um, no, I appreciate that insight. Thanks for, thanks for jumping back to us. And yeah, just, just to wrap us up, you know, as, we get into this, uh,  you know, go back to that question before around sort of opportunities and challenges ahead and maybe some insights you have from your experience from NewBank. just, uh, wrap it like pulling it all back together, you know, what would you, uh, recommend to other people, to be successful with technology? **Thiago Ghisi:** So I would go back to the two things that I said, uh, at the beginning that I think are the things that my opinion make you like, and I think this applies to companies and to also to individuals, but Is like this idea of like being the glue and this idea of like being a, specializing generalist or generalizing specialist and of course you need to like surf different waves and now we are on AI, now they're like new technologies coming up. They're like, they're a challenge here and there, right? And I'm not saying that you shouldn't, go and invest in a new technology, I'm not saying that you should do everything, that is not on your job title and your job description, I think there is a limit to that, but like, Having the idea of like full end to end ownership, and being able to navigate across the stack, going on the, all the layers of abstractions that are sitting below that technology you're using. I think this, those things are going to be valuable again and again and again. And like now with JNI, with whatever, like whatever is coming up, right? Like quantum computing, like if you don't actually. Go, underneath the concepts and you're not going to be able to innovate in an effective way. One, one reason why Nubank was able to innovate in a very effective way, especially on the server driven UI, was that you had folks with different, that had seen different things in different parts of the stack, and they were able to put a solution that leveraged all those ideas. And also had some elements of mobile, right? If you had someone that was only doing mobile development, and they had now to do like a whole distributed system architecture, you're not going to be able to do it because they don't understand what's going on, what's possible, what other areas have seen and failed, right? So I think that is the same thing. And I think with technology, there is the law of leaky abstraction, right, that I think is, there's a famous, blog post from, uh, Joan Softer on that. And I think, like, you need to be careful when you're operating at too much high level and you don't know what's going on underneath, right? And that applies, especially if you work on a company that has, like, Frameworks on top of frameworks, and you're working on something custom in house that does a bunch of things, but you don't know how to debug, you don't know how to go layer by layer, you, you don't understand concepts that are kind of like neighbor to your area, but they are like different languages top there. So I think like, those are the things that I think made Nubank successful because a lot of those engineers. They didn't have like, Oh, I'm, I'm like, I don't know. I'm a Flutter engineer. I only work with  Flutter. It's like, no, Flutter is a tool and I leverage it as much as possible. But if it's not helping me to solve the problems of my customers, of the business, I'm going to drop it and I'm not going to regret it. And I'm not going to attach my identity to, to a technology. And it's easy to get into that thing where you see almost a technology or a new innovation as. I think it's your new kind of like, your new identity would say like those things, if you keep those principles in mind and you push yourself to try to take into ownership, navigate all the layers and being the glue there, I think you can be very successful. Like anywhere in technology. **David DeRemer:** That's super well said. Very, very important concepts and increasingly more and more, I think, uh, as I see more, more companies talk to more people and see, uh, challenges and successes that people face, it really does boil down to, I think, the people you got and the culture you've established and so many people blame the technology for low performing, you know, products or something like that. And a lot of times, it's not really the technology that's the problem. It's the, the team dynamics, the cultural setups, the organizational design, and all the other things that kind of come along with it. And like you said, I think it's, that's a really insightful thing. I think right now people probably are scared of AI. A lot of people. Are they going to take developer jobs? And to me, I just see it as an extent. It's like just something else like Flutter. It's just going to make developers more productive. And like you said, if you are the glue type person, who is that sort of generalist, specialist, specialist, generalist, Who can adapt to these things and be productive in a lot of different ways. That's gonna make you and your team successful and adaptive. And it sounds like that's what's helped NewBank along the way. So I just want to thank you for coming here and, and sharing your journey and your stories with me and with, uh, everyone who will listen to the podcast. Um, what we'll do is, uh, we'll get some links from you and we'll put them in the bottom of the podcast. So if anybody's, interested in learning more, you know, if you guys are hiring or I know you've got some articles on your server side driven UI approach and maybe some talks and other things. So. We'll put those, um, but I just want to thank you for taking time out of your busy day. I know you got a lot on your schedule and these days everyone's working super hard, especially right now. So, thank you so much for coming on and, um, we'll let you know how things go. **Thiago Ghisi:** Thank you, David. Appreciate it, man. Thank you for inviting. ### Customer Obsession Drives Better Software Source: https://verygood.ventures/podcasts/michael-gyarmathy-pga-of-america-how-customer-obsession-drives-better-software/ In this episode, Michael shares how defining constraints early helps teams stay focused, the importance of embedding digital products into real-world business programs, and how modular architectures support scalability across multiple teams. {% audio_embed url="https://player.cohostpodcasting.com/b735940a-f10f-45c1-ae1d-6555bcc21c17/77ff7225-10b7-4415-8bba-46fedfeeeb99" /%} ## Transcript **Michael:** \[00:00:00\] I was not a golfer before I joined and so that was, you know, part of the journey of building this product was also my own journey into. golf and, following a longer customer's journey, through, you know, their shoes in my own experience.  **David:** Hi, I'm David and this is Build to Succeed from Very Good Ventures. In today's episode, we talk with Michael Germathi, staff software engineer at PGA of America. We'll talk about the importance of customer obsession, engineering culture, and of course, leveraging multi platform tools like Flutter. So without further ado, let's talk to Michael. Hi, Michael. Welcome to Build to Succeed. **Michael:** Hey, David. Yeah. Thanks for having me. I'm doing great. **David:** Great. Great. So, you know, um, given that you work for PGA of America, the question I'm sure you get asked all the time is, uh, what's your handicap? **Michael:** Yeah. you know, unfortunately, I, so I'm, I'm a father of two young girls and, my time out on the course has, has diminished quite a bit since they've entered the world, but, um, thankfully, I do get plenty of opportunities to play here at \[00:01:00\] the office. so, yeah, my goal right now is to break 100. I'm right on the cusp of it. and hopefully with some more practice, I'll, I'll get there soon. How about yourself? Do you play? **David:** I, um, had a little stretch where I played quite a bit with some friends, and it never got good. It was more just the socializing, the fun, but then similar story. these days with a couple of kids at home and. Busy at work and stuff. It's kind of hard to get five, five hours or so to go out and hang out with your friends on the golf course. But, um, we'll get back to it one of these days for sure. **Michael:** Yeah, honestly, like 9 holes compared 18 kind of underrated. Like, uh, you get in, you get out in about 2, 2. 5 hours, depending on, you know, your group. And, you maybe grab lunch afterwards or something, but that's, uh, that's a. You know, just enough golf for me. **David:** Yeah. One of the things I've always found interesting about golf as a sport is it's one of those sports where you're really kind of playing against yourself. Um, you're out there, it's got a social element. It's a lot of fun. You're playing against some other people. Maybe you're doing a best ball or something with, you know, some sort of betting or whatever, where it's a little bit of competition, \[00:02:00\] but it's still a competition against yourself. Have you found as you've gotten into the world of golf, not only as a player, but professionally, are there parallels between like a golf game and a coding game? **Michael:** Yeah, without a doubt. I mean, um, you know, I kind of view software engineering as a craft. some people view, you know, more is like just a skill you learn. but a craft is something where you really, hone it in and perfect it over time. And also it's constantly evolving in the way that you you practice it. and so I view golf the same way. You know, you can spend hours out on the range perfecting your swing, but then you get into real game time situations. And some of that has to go out the window and you have to rely more just Intuition, um, and the same thing applies to software engineering, you know, it's one thing to, to, build software on like your local development environment where like everything is sort of contained, but as you enter like production environments and real world scenarios where you have other users besides yourself,that's where,\[00:03:00\] the skill becomes more of an art, um, less, less of a science and more of an art. **David:** Awesome. Yeah, I love that. And it's, uh, for me, it's always been, it's really fun. You got to keep it, keep focused on what you enjoy. It's uh, a skill that is, nearly impossible to master, right? You can always get better. And, I think like when you map those through your professional career, so it was, it was a good parallel. So now a, um, staff engineer at PGA of America, right? Can you take us through your, uh, career journey of how you got there **Michael:** Sure. So I started my career,in the consulting world. I spent about 5 years there. I worked across a bunch of different industries. and to me, I feel like,with consulting, sometimes you're stepping into,chaotic situations. They're bringing in help because you have real, tough problems to solve. Uh, and so that was a great way for me to cut my teeth and see, Software and product development done in a lot of different contexts, but the, the flip side of consulting is sometimes you don't always have, like, final say on the product or the direction of the products going. You have \[00:04:00\] clients, you know, rather than customers and so, by the time I had, I'd gotten through, you know, my first 5 years of consulting, I feel like I built a really solid foundation of, software engineering skills and product development skills and business skills. but it came to a point where, like, I wanted to really own the product that I was building and help shape the direction. And so that's what, what ultimately brought me to PGA of America. I started in April, 2020, which was a period of transition for the company. Uh, **David:** world? **Michael:** In in the world. Yeah. Yeah. Uh, not, not to be understated. but yeah, the, uh, the PGA was in the process of relocating their headquarters from Palm Beach Gardens to Frisco, Texas. I'm a Texas native, and so it worked out that, the office was being built about 30 minutes north of my house. And, So got reached out with an opportunity to start a brand new team and build a new product, which we'll probably get more into here in a bit. but, uh, yeah, that's essentially how I found my way to PGA. and then as that product grew over \[00:05:00\] time, My influence on both our team and our business around the product also grew as well. And that ultimately is what elevated me into that staff engineer position. **David:** Awesome. And in, uh, one of our conversations, you called, uh, PGA Frisco, like the Silicon Valley of golf. And, uh, I'm curious if you could put that on the map for people and explain a little bit what you mean by that and what's going on down there. **Michael:** Yeah. So that that was a term coined by our former CEO as HQ is being built, you know, uh. Not only is PGA Frisco, um, our headquarters and what we call like the home of our members, but it's also part of a much broader campus, with, the, Omni Frisco Resort, and then also the PGA Frisco, uh, fields ranch golf courses. So, you know, a neat part about my work environment is I sit and I, from my desk, I look out and I see a driving range. And then beyond that I see two, two awesome. Golf courses, which is pretty \[00:06:00\] inspiring. but going back to Silicon Valley of golf, really at the root of that was innovating the game of golf. the, and we do that in a number of different ways, whether it's through new products we're building. We also have, a an investment fund. We're replacing investments in other golf related startups, and businesses, that are helping advance the sport. Uh, and so really we see ourselves as like pioneers of the game. and so PGA Frisco is sort of the mecca of that innovation. **David:** That is awesome. I think this is maybe a good point to take a moment to explain the difference. I think people out there, um, you know, PGA of America is a special business. And can you maybe just describe a little bit about what you guys do? **Michael:** Yeah, absolutely. So, PGA of America was founded in 1916. So, 1st, right off the bat, we're like a 100 year, 100 plus year old organization. and what we represent are, golf professionals in the golf industry. Um, so oftentimes \[00:07:00\] we get confused with the PGA tour, which really represents the interests of professional golfers, not golf professionals. Um, so think of the likes of Tiger Woods, the Jordan's beasts, et cetera, that isn't necessarily our, our organization. Uh, what we represent is the, industry professionals that run the golf courses, across the country that teach the sport, uh, that develop. Products and equipment within the sport. you know, the, it's a pretty broad representation. We're actually the largest,not for profit trade association, and sporting organization. We represent 31, 000 professionals. So it's a pretty wide network. and. As part of being a PGA member, there's really some, um, sort of credibility that you have based on the path that you have to take in order to become a member. **David:** Awesome. So when you're starting, uh, 20. 2020 and you're there to sort of help stand up this Silicon Valley of, of golf. And you're looking at \[00:08:00\] technology and a hundred plus year old company and a very physical outdoor kind of sport, um, that you play with your hands and an iron and a ball. some wood sometimes, right. Um, tell me a little bit about like the types of technology you guys are looking at and how did you kind of just get into building mobile and what kind of problems were you solving for customers? **Michael:** Yeah, absolutely. So, you know, within that. 31, 000 members. There's a couple of different segments and job roles or professions within that. and one of the larger segments is teaching and coaching. So these are teaching pros that, work at different golf courses and, help introduce people to the sport. Um, you know, taking a step back, the mission of the PGA is to serve our members and then also to grow the game of golf. And so we, we view, um, You know, any opportunity that we can take to put a club in the hands of someone new and introduce them to the sport really, really seriously. and so, those \[00:09:00\] teaching professionals are, in a lot of ways, the gateway into the sport. And so we wanted to find better ways to serve them. And, what we arrived at was ultimately a product that we call PGA coach. so your typical teaching pro, um. Most of them have been teaching the sport for decades, and rely on more older, you could almost call it antiquated methods of running their business. And we wanted to help, um, accelerate them kind of into more of a modern, customer experience. So, if you think about it, today, like, if I'm going to go book a haircut, um, I can go find my barber online. I can see his schedule and then I can, you know, Book my time slot, book my appointment, and pay right there up front and it's, you know, convenience both to,to the barber, but also to myself, and being able to, like, schedule and make those arrangements. Um, what we found is a lot of our teaching pros were using pen and paper, relying on phone calls, and sticky notes and \[00:10:00\] et cetera,to organize and operate their business. And what they were really passionate about is being out on. Either the practice tee or out on, even more importantly, out on the golf course teaching the sport. and so what PGA Coach provides is a suite of business tools. Uh, everything from scheduling to, payment collection to messaging. Uh, and a couple of other capabilities that help optimize and run their business. **David:** So how did you as a team, um, obviously, you know, these people, you have access to this network and sort of the customer who's looking for coaching as well, but take us through a little bit, the process you went through to kind of figure out what this thing should really do, like how to solve the right problems. Cause with these types of experiences, right? Like you said, they want to be out on the golf course. So you want to create the right amount of technology and the right amount of product features, I would think to be helpful and not. Obstructive to the experience they're trying to have. And if I'm correct, like around this time, you also are sort of like, you know, new, new company getting into golf. There's a, uh, you know, driving range right outside \[00:11:00\] your, your window. Um, did you leverage your own personal experience to help guide some of the product development too? **Michael:** Yeah, definitely. I mean, so we looked at it from 2 different angles. 1 is, like, what are some of the tools that coaches are using today? and what we found is that it was kind of this, you know, very, uh, segmented or scattered set of tools that they were trying to integrate together. And that's really, I think, Part of the value proposition of PGA coach was integration of and kind of consolidation of all these different, SAS products that they might've been using before. so that was 1 piece. Um, 2nd was like, how do we help them reach new golfers? and so leveraging our brand, um, we, we promote all the coaches on the platform on PGA dot com. You know, so from that standpoint, like, we're meeting customers where they are. And then ultimately, it was a matter of, like, um, you know, for people like myself, who, um, so disclaimer, like, I was, I was brand new to the sport as well. I was not a golfer before I joined \[00:12:00\] and and so that was, you know, part of the journey of building this product was also my own journey into. Into golf and, following a longer customer's journey, through, you know, their shoes in my own experience. Um, and so like one of the first challenges I had when I was trying to pick up the sport was finding someone who could teach me the proper techniques and, um, And skills, uh, just to even be comfortable out on the course and not make a total fool of myself. Um, and so the 1st piece of that was locating your coach. that was close to me. and so that was the 1st capability that we built out was like, a coach search,kind of feature set or capability. and that. On the coach's side became this lead generation engine of of new business. from there, we took it to the next step, which is,you know, maybe I've made 1st contact with the coach and I'm, you know, I like their, their methodologies or, you know, their, their schedule \[00:13:00\] aligns with mine. Uh, so how do I schedule a lesson or how do I on a regular recurring basis, um, schedule time with them? So that's when we started to build out the scheduling. Capabilities. and then finally, it was like, how do I maybe, I'm practicing during the week, but I want to, ask them a question or something. And that's where we started to build out some of the messaging capabilities as well. helping kind of separate, Coaches like personal relationships from their sort of client or like student roster. Um, was something we came across. and so that kind of led to the next feature. So, yeah, some of it was like me stepping through that and some of my teammates stepping through that. And then also just listening to, listening to our users. andwe pride ourselves that PGA coach was built. Yeah. By the PGA of America for members with their feedback included. And I think that's a core part of like, our overall product development. ethos is like being obsessed with our customers \[00:14:00\] and, making sure that their interests are met. **David:** Awesome. I love that. Being obsessed with the customer sounds like you've mapped out that customer journey. And if I am hearing you right, you iterated the feature development process over time, adapting to the next big thing that people are really looking for. Um, so it's like, did you build like what time period did you build those out over? **Michael:** Uh, it was over the course of, um, maybe a year and a half, two years or so. So every six months or so, it almost felt like we were launching a new product within this sort of suite of tools. and, and certainly iterations, you know, another, another part of our product development methodology is experimentation. So before we commit to. 3, 4 months of development time, we want to go validate that,we're building the right solution to solve the problem that we're trying to solve. And so, we, we make heavy use of, Your product experimentation tools, and come up with a hypothesis that would lend enough evidence to say, well, with maybe a week or 2 weeks of. \[00:15:00\] Development investment, we can get strong enough signal that would then validate that we should pursue this, you know, in full fidelity. Um, and so that's been a lot of, a key part of our success as we've built out this entire suite of products. **David:** It sounds like you guys got really good too at sort of defining the chunks you wanted to take on. I think a lot of companies finding that right balance of what do you launch with. Um, and there's always some degree of feature creep and more and more and more. And we need this in order for the product to be successful versus the MVP mentality. Um, sounds like you had a very structured approach to that.  **Michael:** Yeah, well, I think to, like, the other kind of interesting part, I think that gave us some, some staying power. So, like, you know, you mentioned this earlier. Maybe I mentioned this on a prior conversation. Like, this very much felt like a startup within this sort of legacy organization. And so there are risks to building, that sort of startup where. you need to be able to prove that like you are providing enough value to company to the organization, that there is some immediate ROI on what you're doing, in order to continue \[00:16:00\] it. And part of, I think, what helped us have that staying power is also finding other opportunities within our business, to sort of anchor the product to these other programs or things that were going on. So, initially we were moving in the direction of building out. coaching products, but very quickly we saw opportunities, through, PGA reach, which is our, our actual, uh, nonprofit arm. PGA REACH has a couple of different programs. One of them is PGA Junior League. It's a junior golf program. and so you can think, you know, scheduling, building out team rosters and stuff, pretty, pretty closely adjacent to the coaching product that we were building. And so by. Extending what we had, to meet the needs of that program. and that junior league was already very much well established. Uh, and so that, you know, enabled, um, those business owners to move off of a third party, system and use an in-house product that they had full, you know, say on the direction that it could go or iterate there. and \[00:17:00\] similarly, we did something for PGA Hope, which is our, um, veterans golf program. Hope hosts these, um, golf events where they bring out veterans and introduce them to the sport and try to help them build connections as they reintegrate back into society. And, um, Much in the same way we were able to to leverage the existing product that we had built and anchor it to other parts of the organization. 1 to like, build credibility among our business leaders, but also, to help. Take the product in new directions. **David:** Awesome. Building on those foundations and sounds like you guys have a lot of products then that you've developed over the years for different audiences and focus areas. What is your development methodology? You know, you were talking about that iterative process. How do you structure your teams and how to define the shape of the work you want to do? That's a big challenge. I think we talk a lot about technology and platform and customer insights and user experience. But where the rubber meets the \[00:18:00\] road is the team's ability to actually plan and then execute. sounds like you guys have a pretty good approach to metering yourself through these features. What is some of that approach that you follow? **Michael:** Yeah, happy to share. you know, I think it's something that also has evolved over time as our team has grown and we have, different squads or teams moving in different directions and trying to create some consistency there. but structurally, what we found a lot of success in is, at the core of of a product development squad is a product triad. that's common in other. Organizations, but,it's a combination of a product manager, a designer and a software engineer. and so in tandem, they are making all the decisions of,Identifying new opportunities, coming up with a solution and then ultimately building and executing on that solution. Um, and I think the triad is successful because it helps, there's kind of some checks and balances involved in the triad, where maybe a product manager comes with a really ambitious \[00:19:00\] idea. but then an engineer, uh, sort of brings some,You know, context in terms of feasibility of that idea, or, um, might be able to present multiple options from a tech perspective on how to, to build that product or that idea or implement that idea. Potentially taking it in a completely different direction that they may have not even considered in the 1st place. Um, or, you know, having sort of the field of view to say, we could extend something that we already have in place today. and get to that solution much more quickly than sort of standing up something brand new and then design. Obviously, like, not all engineers have an eye for consistency from a visual or perspective. And so. You know, they, they sort of round out that triad in a way where, They make sure what is being built is actually usable, uh, and, and intuitive. **David:** Do you follow any like specific methodologies or like a scrum or anything like that? **Michael:** We, uh, so, so not like a scrum, I would say a lot of our methodology has \[00:20:00\] been influenced by, uh, the book Shape Up. I'm not sure if you're familiar with the 37 Signals guy, but, uh, Jason Freed authored that book a number of years ago. and we have certainly adopted different, um, pieces or ideas out of that book. you know, some of the most notable ones are, appetites, uh, or the concept of appetites. so when we are building a new, product or feature, the first question that's often asked is what is the appetite for this? how much time and resources are we willing to, allocate or direct in towards the solving this problem? Before we need to reconsider or,you know, whether to re up that or to move in a different direction. Um, and I think that's helpful. thinking about it from that, um. That vantage point, because, You know, in the world of, like, software engineering and my background, I think constraints are a good thing. when you have unbounded problems or like, unbounded solutions, \[00:21:00\] oftentimes that leads to,you know, a ballooning of scope, or, potentially diluting the problem that you're trying to solve in your solution. and so setting an appetite really helps us. I think, Constrain the problem in a really helpful way where we can come up with, like, really creative solutions, that kind of meet those constraints. and also. adhere to, like, the timelines and commitments that we try to set for ourselves. **David:** Yeah, that book is, I, if, if anybody hasn't listened to that or read that book, it's a super insightful book, I think in turn, it's a kind of a fresh perspective on sort of these scrum things we talk about, and you're right, the word choice, like I, when I looked at that book to like appetite, you know, it says so much, right. And I think we all kind of like inherently understand what that means. We tend to talk about timelines and budgets and scope and resources and all these things. But one lens on that appetite thing I'd like to is like, if I, as an engineer have a. hearty appetite for that thing. I can maybe do a thing that if I wasn't hungry for it, I can do it in two weeks. I'm going to be like, I'm going to do this in two weeks. I'm hungry \[00:22:00\] for it. Versus if I'm not super hungry for it, maybe I'd be like, ah, I think it takes three or four, you know? And like, you can make the, you can make the choice how much you want to eat. so to speak and like the shaping idea, I think is really cool. Like you the thing. and even the, uh, what do they call it? The bedding table or something where you just make a bet. Like, we don't know if this is going to work out. We're going to make a bet. We're going to try it. I love some of the, just the word choices that they use. **Michael:** Yeah, definitely breaks the mold of kind of this, like, very rigid,I think, like, sometimes agile can be practiced in a way where it sort of, uh, violates the underlying principles of agile software development or sorry, scrum is practiced in that way, where it was more about,or less about process and more about outcomes. And I think that's also what shape up kind of cuts, um, to the heart of is, um, we're really focused on delivering value to our customers and delivering value to the business. And so what are other ways that we could organize ourselves in a way that, meets that directly? **David:** Yeah. I think it also really embraces the idea that \[00:23:00\] if you have an empowered team. With the right people who are like, you know, lean in type people who have a lot of ownership and pride in what they're doing the right skill sets and you shape it correctly, you can just trust those people to do it. You don't need some sort of rigid dogmatic software process that you're iterating through and all these ceremonies. That's really for large teams that there's some degree of like distrust in the team's ability to deliver. Right. and I think shape up like really is like based around empowerment of your team. **Michael:** 100 percent and I think 2 from, from the engineering leg of the lens of that, um, like, we have, I guess, what we would just call, like. Our tech lead role, who is the leading engineer of that product development squad. That's guiding and directing other engineers. We really place a lot of trust and try to empower them in that role. so we, we don't have project managers at PGA. we sort of fold a lot of that responsibility into the tech lead role. and the underlying principle there is like, our builders know how to build. They don't need somebody else to tell them how to build. \[00:24:00\] and so by allowing them to more self organize and collaborate with 1 another and speak kind of a common engineering language. we really don't see a lot of need or value for that role. certainly then we also. give some of that project management responsibility up to our product managers in terms of helping,coordinate timelines and connect with the business in terms of delivery. but, yeah, very much. We want to empower our teams and trust that they, have good intuition and they're going to make the best decisions with the information that they have. **David:** Awesome. Yeah. It's a long run. I think it's the best way, you know, if you can get those teams structured, but it's a cultural thing. You got to get your engineering culture set up correctly to be able to support that and get your management bought in on that too. Sounds like you guys have made a lot of progress in that regard. It **Michael:** Yeah, 1 other thing just on the culture front that I, I think has been key to our success is,you know, we hit on it earlier, but the idea of customer obsession, but also, like, um, self immersion in the customer journey. and so \[00:25:00\] certainly being here at headquarters, like. I have a lot of opportunities to go play golf and, to be surrounded by people that are deeply involved in the sport. but we also have, a distributed remote team, so not all of our software engineers are here, um, at HQ. They're And other team members are also all across the country, but we heavily encourage everyone to find ways to connect to the industry to connect to the sport. we host this, like, Friday demo call every week where the team helps. Or the team goes around and sort of shares what they've been doing that week. and not all of it is like, uh, here's, you know, a new screen that I built in our app or, here's a customer interview that I did, but it's also like, I took a lesson this week with a pro, at, you know, my local driving range and here's what I learned. And that. In our mind is also just as important,as all the other work. And I think, like, as much as you can get people out of their seats, and involved in the \[00:26:00\] industry and connecting to the customers. I think that helps bring a lot of enthusiasm back to the work that they do because they have sort of that personal experience. **David:** makes a lot of sense. Uh, customer obsession, getting into like inherently understanding what you're trying to build. Good engineering and just general product development cultures and methodologies. And then obviously technology too, you know, how we got connected here was, I know you guys are using Flutter for your apps. Um, and I wanted to tap on that a little bit. How did you, discover Flutter, uh, evaluate it, select it. Um, how'd you get started with it? **Michael:** Yeah. Great question. So the, the PGA coach platform. sort of manifests in two different, um, two different mobile applications. So, we have PGA Coach, which is the coaching side of the, um, system, and then we have my PGA, which is a consumer facing app. So both of those are built in flutter. the initial prototypes of those apps were also built in flutter, with a, third party, um, company that we were working with at the time just to help kind of \[00:27:00\] build out the initial pieces of it. so flutter wasn't on my radar at first when I joined the company, I had, I had prior experience with other like cross platform. Technologies I done, I think, was it like Cordova a number of years back? and then, uh, Xamarin was another 1 that I built a few applications with. and I sort of had a sour experience with them. They were sort of clunky and, didn't necessarily, like, provide all the things that I would hope for. And they very much felt like, um, know, wrappers around web technologies. and so, yeah, there was just something missing there. but as I started to learn Flutter, as part of this job and building out the products that we had, I really enjoyed it. Um, you know, I think there were a couple of key features there right off the bat that made Flutter really appealing. the dev experience is great, with the hot reloading capabilities. Um, and also like the test, like The structure they provide around automated testing, I think is huge. that seems to be for other ecosystems, \[00:28:00\] less of a, um, first class or, or like, you know, primary concern. but certainly that's something that is, is well supported in the Flutter ecosystem. **David:** Yeah, a lot of people I think had that experience, um, or at least heard the stories of the other things all the way back to phone gap, you know, from almost like day one of the app stores when there were **Michael:** Oh man, that's a throwback. **David:** Yeah, right. Um, like people were always looking like, why do I have to do this twice, you know, but the joke was you, you used to be able to always tell like a phone gap app. It was like, Oh, you could do iOS and Android, but it's like, no, like immediately, you know, something's off. And you're like, this is not iOS or Android. It's something else. and then I think there's just a lot of like baggage that comes even react native. Like there was the famous Airbnb post, I think it was Airbnb where they were like, we're moving away from react native. **Michael:** And here's why. Um, and so there's these like, yeah. People tried this stuff and got burned or felt wrong. And, uh, but yet technology moves along and Flutter still has its things compared to other tools, \[00:29:00\] but I think they got a lot of things, right? You're right. The dev experience overall is super good. And generally speaking, I think the quality, and I know the Flutter team really obsesses over trying to get the details of the executions, things like as close as possible so that it feels super native that you never have to really worry about that stuff. we've actually, um, you know, I think helped kind of push Flutter to the limits as well. and gone off. So for one thing too, like, so we started Flutter in like 20, the Flutter apps in around 2020 timeframe. and we've, we've grown with the technology over a number of years. I guess we're on what year four at this point.  transition period where they, they introduced no safety. And that was probably the. Largest hurdle that we really had to jump through, jump over, I should say. but yeah, beyond that, it's been a really smooth dev experience. Um, you know, one of the neat opportunities we had using Flutter is, for a,video recording capability that we wanted to introduce. so. Maybe some background here, some coaching, \[00:30:00\] some coaches do virtual coaching, or some coaches record videos of their students as they're practicing is sort of this, artifact or takeaway that they can provide for them, to help them, Retain the things that they're learning through their lesson, so being able to record video is a big part of that. we had a partner that we, We brought in to, they had an SDK, to help provide some of those capabilities in our app. but what they. Provided were native SDKs, an iOS and an Android SDK. and I was surprised by how simple it was to go build, you know, implement the bridge code to hook into the native SDKs and be able to call that, you know, like some of the interop of Flutter is actually really well done. and so, um, yeah, I was really, really pleased with that experience. **David:** Yeah, that's a big, that's a great point. Thanks for calling that out too, because I think so many, there's this assumption that like when you go down that path, you're going to lose access to all the native stuff, all the things you can do. You're still making iOS and Android apps, first \[00:31:00\] of all, so you can still. Go down to that layer. But I think you're right. The plugin system, the FFI stuff, and I believe on their roadmap, they're looking at doing things that are making it even, even easier to call native APIs from dark. Um, so I think they've made a pretty big effort in that regard. And I think it's, it's like, those would be a major barriers, for you if you encounter that. so I think that's one of the things they got right from some of the other platforms that are out there. **Michael:** Definitely. **David:** I've in terms of, um, the. the. productivity and the influence on the product, like did that choice of Flutter, do you think early on influence the product development process itself, like how you guys approach your code base and your ability to iterate and move quickly? **Michael:** I'd say yes, to some extent, um, you know, we, we were able to build out some really great patterns around feature flagging that helped kind of accelerate, um, some of our development where we could ship, um, you know. Like, sort of shadow releases of different features or be able to preview certain releases to, um, to certain \[00:32:00\] customers. so I think, yeah, that that was pretty key. and then certainly, like, having a well architected CI process and being able to run all of our test suites. I think also really accelerated the development process, knowing, like, if we were going to do any sort of large scale refactor, we have this great test bed to fall back on knowing that what we're about to ship isn't going to break, um, or cause people. Problems **David:** Give you that trust and confidence, ship fast, ship safe kind of thing. And you're a two sided marketplace, right? So you have the player app and the coach app, um, kind of like, you know, Uber would have driver or passenger. Are you sharing a lot of code between the two? **Michael:** we are. Yeah. So the way that we have our, our project structured is we have a couple of different, uh, like modules. and so, um, we have. One module for each of the applications itself. And then we also have a common module that's shared between the 2 applications. there are certain screens, you know, \[00:33:00\] messaging is a great example, where the user experience on both sides is almost identical. Um, and so no, no need to, go build that twice. Just, encapsulate all that in sort of a shared module and then. Share that code into different places. so yeah, that's another way that maybe we've broken out of sort of the,kind of baseline usage of Flutter, um, and tested the limits of it, but yeah, the module system and essentially having like a local package, that's built in, rather than some remote dependency,has proved to be successful for us. **David:** And that's super cool. I think that's like a, you're, you're at pushing the boundaries of Flutter. Most people, I think, still are considering it as this crop cross platform tool, like a react native or, or Xamarin and things like that, where it's, um, I'm going to build it once and I'm going to deploy to iOS and Android. Um, but I think where Flutter really starts to shine and gets that like next gear of business value is like, well, I'm not just building one app. I've got like an ecosystem of products,\[00:34:00\]  um, and they're not all the same, but I start sharing and I have developers who can work on any of them cause they're architected similarly and use common APIs and, you know, standards and testing requirements and things like that. So I have a lot of fungibility of talent, is really cool. Like white labeling type applications where I've got a bunch of different things that I'm doing that are like 90 percent the same. Uh, not only do you get the cross platform, but now multiplied by all those different products. **Michael:** Yeah,  **David:** huge value there. **Michael:** Yeah. And I think, um, yeah, that that module system or package system, um, that also scales to your teams, right? So if you have, multiple teams working on, different, applications or products, basically, whatever the topologies of your teams are, I think you can also match that, um, with the package structure that you set up, whether those are all shared in a single monorepo or. Distributed across multiple, projects. yeah, it all works great. **David:** Nice. I'll have to ask you about your team topology, maybe in a future \[00:35:00\] one. have you heard the book team topologies? **Michael:** I have not, it's on my list. **David:** I got it up here somewhere. Matthew Skelton, um, is the one of the authors. I just love that idea that the shape of your product mimics the shape of your team. Um, which is kind of an interesting,thing. And I've actually found that it's not just in your software engineering. It's like everything you do, is based on how you set up your teams, which is kind of interesting. So, amazing. And so like long run Flutter, like, uh, how's the management view it? Like, is Flutter seen as a, as an asset as a, um, you know, when you think about hiring, recruiting your team culture, Your ability to move product and meet customer value. do you think it's giving you guys some strength or is it, is it sort of largely culture driven, not necessarily technology driven? **Michael:** I think it's both. Um, I mean, certainly like our engineers, especially the ones that came from more of a traditional web background that have transitioned over to that, have picked up the language, like picked up Dart, picked up the Flutter. Technology pretty, pretty seamlessly, you know, the component system is familiar to somebody who's first or the widget system is familiar to somebody that understands react \[00:36:00\] components, or, like, uh, a type language, you know, if you're using TypeScript, transitioning into, dark is pretty, pretty seamless. and so, yeah, what culturally, like, what we value are. Um, and we have a lot of more T shaped engineers that have both breadth and depth to their skill set. and so the ones that are trying to expand the breadth of their skills, Flutter has been a good fit. especially considering some of the other technologies that we have in our entire landscape. Uh, you know, namely React and on the website. but also like we, we also do have a React Native app. So we, I think that's been helpful to our organization and being able to. against one another and better understand the trade offs between the two. Um, we've also had a great experience with react native, particularly through expo,and, some of the like over the air updates and some of those capabilities. And I know, I think that's coming to flutter. I've been following the shorebird project a little bit. Uh, so. Very excited about that. but yeah, um, \[00:37:00\] I think overall, yeah, it's been a great asset. We also find it's pretty easy to hire talent for it. and even if somebody doesn't, come into the company with that specific, um, expertise or proficiency, uh, the learning curve is, is pretty shallow in order to pick it up  **David:** Yeah, no, that's a good point. I'm glad you mentioned that. I think, uh, uh, there's, that's one thing we hear is some concern about job market and we hear from Flutter engineers. Oh, it's hard for me to get a good Flutter job. And then we hear from Flutter companies. It's hard to find like engineers and like, well, something's broken in that ecosystem then in terms of matchmaking. but I think you're right. It's like, the other thing is you can just take your team and convert them over. There's some cultural things sometimes moving from, oh, I'm an iOS engineer and that's my identity. Um, I think we had somebody, David Chen, I think was on here and he was talking about everyone just being like a product engineer, essentially, like you just work on whatever. I think that context of React Native too is good. I mean, obviously we talk a lot about Flutter because it's a large part of what we do, but ultimately our. Stance has always been, there's many good tools. You got to pick the \[00:38:00\] tool that works best for your team and your situation and not be dogmatic or religious about, uh, one tool or another. They all have pros and cons, just like native, you know, could do it with SwiftUI and that's going to have pros and cons, you **Michael:** I mean, **David:** got to make the right choice. **Michael:** Yeah, same same on the website in terms of the sort of J. S. Wars and framework wars. Uh, and then similarly on the back end as well. There's it's always trade offs. And I think that's 1 thing, at least for me, coming from a consulting background is every new client that I stepped into. It was a different tech stack. So very quickly, I think I became much more agnostic and less of a die hard to a specific technology. Um, understanding that. Yeah. All of them can be effective at, um, you know, solving particular business needs and problems. And ultimately the code is a conduit to solving a business problem. **David:** Awesome. And while it sounds like you guys got a lot of good things going, where you have a good culture,good sort of overall process with that sort of shape of kind of approach. And it sounds like you've got a technology that \[00:39:00\] also. And Nate, like if you have an empowered culture where you're relying on this triad and people to really be customer obsessed, and then to act on those things, having a technology that allows those empowered, obsessed people to quickly turn that into production code on all the platforms seems like a really good, Marriage made in heaven, so to speak. So, what else besides that, when you think about what's made you guys successful, with your product delivery or in your career, besides this role, even when you look back on your experiences, where things that have correlated with you guys getting the outcomes you're looking for. What does success really look like for you? **Michael:** Yeah, I think that's a great question. and at least,in my role where I am at PGA of America right now, it ties back to our mission, right? Like I mentioned at the beginning, serving the member and growing the game. And I've seen direct impact of the work that I'm doing and how that has played out. and contributed to our mission. we have coaches coming to us all the time that are saying,I'm working fewer hours, but I'm making more money because I'm running my business more \[00:40:00\] efficiently.and then similarly, we can measure the direct impact of how many people are we introducing to the sport and knowing, like, for myself, how much of a, enjoyable and valuable life experience it's been picking up the game of golf. it's really meaningful and impactful to me to know that I'm creating that sort of experience for so many other people. **David:** That's amazing insight. well, thank you so much for coming and, uh, for sharing your stories and your insights with us. lots to learn here and covered a lot of ground. if people want to learn a little bit more, find you or, uh, find a golf coach or improve their game, or maybe find a job for you guys, where can they find out more about what PGA of America is up to? **Michael:** Yeah. PGA dot com. Uh, if you're looking for a coach, PGA dot com slash coach, you will almost certainly find a coach, uh, just down the street from yourself. I highly encourage anybody that hasn't ever picked up a golf club. give it a shot. Uh, you might, surprise yourself,and have a good time in the process. And, separate from that, you can find me on Twitter. Or just Google me, I guess.but, uh, yeah, David, thank \[00:41:00\] you for having me. we gotta get you out to Frisco. Let's get, let's get a golf club back in your hand  **David:** Let's do it. I'm down. Sounds like fun. Well, thanks so much. Appreciate it. And, uh, we'll talk to you again soon. Hopefully. **Michael:** Thanks for having me. ### Building Autonomous Systems for Real Challenges Source: https://verygood.ventures/podcasts/david-chen-building-autonomous-systems-for-real-world-challenges/ In this episode, David Chen delves into the transformative role of autonomous technology in agriculture and mining. From drones enhancing safety in mining to robotics addressing labor shortages in agriculture, David shares his insights on leveraging tech for efficiency and sustainability. The discussion also highlights the advantages of Flutter in cross-platform development, making it easier for small teams to manage complex applications. Tune in to discover how strategic technology choices can drive innovation and improve user experiences in critical industries. {% audio_embed url="https://player.cohostpodcasting.com/ccd7c250-63f0-4953-84e7-b8e0521549b0/66e5157d-8713-4ded-8f63-ed03432c1175" /%} ## Transcript **David Chen:** \[00:00:00\] no matter how good the underlying tech is, if you present the user that's using this as their livelihood and it, something breaks or doesn't work or costs them an extra hour to figure out how to do something, They're just going to throw it away and never use it again.  Hi David, thanks for coming on Build to Succeed. Hey, David. Great to see you again. A pleasure. **David SP-VG:** let's get started with, why don't you give us an introduction. **David Chen:** Yeah, I'm David Chen. I'm the head of software services at a company called Agtonomy. We're building autonomous tractors to work in permanent crops, and our mission is to solve the massive labor \[00:01:00\] shortage that's happening in ag today. **David SP-VG:** Autonomous, right? So I was just out in San Francisco, actually saw you a few weeks ago, and, got my, first experience in a Waymo. So you're at the,in the center of autonomy and autonomous vehicles right now. what's the vibe out there with the whole autonomous movement? **David Chen:** it's incredible. You'll see Waymo is driving all over the place. And after becoming a user, you realize you never want to ride in a human operated Uber or Lyft ever again, because they just drive so much better. It's comfortable. You don't get whiplash from terrible drivers and. You don't get your ride cancelled because there's, traffic. can't wait till it's everywhere. **David SP-VG:** It was, uh, eye opening for me, personally, cause the cars were both, more aggressive than I expected them to be somehow. And, interestingly enough, I feel like there was one situation where a car pulled over. And it was kind of still in our lane a little bit where even as a human, I was like, are we clear? But it's interesting because it had \[00:02:00\] so much data that it knew it was clear. And we were all like, Ooh, that's tight, you know, but it's still pushed right through. And, um, so that was, that was pretty cool. And,we had a joke because we were, we got, we took a regular Uber and we were just talking about work, obviously what we do, we build apps and stuff. And the Uber drivers like started pitching us his app idea, which is not something that happens in a way, Mo. So, you know, it's interesting. Um, but, yeah, so your career has spent a lot of time thinking about autonomy, right? And, I'd love to just tell us a little bit about your story arc. How did you end up at Agtonomy? **David Chen:** Sure. So, growing up, I was exposed to computers very early on. Got into programming, learned how to program. And one of my first jobs, this was in the 8th grade, was Building a database program for a local, local company that made inflatable boats. And then from there, got other jobs, built doing PHP, web development, and that's kind of what led me down this path. Went to school, did computer \[00:03:00\] science. Did my own startups after that. Eventually I worked at Twitter for a number of years when they were still much smaller. and it was around that time I got really into drones. this was a new thing, right? Quadcopters didn't really exist for the public. At least it was just something that can hover in place. You can fly it easily, control it through an app. And that really sucked me in. I got into drone racing, building drones. And that's what led me to autonomous drones, and I joined a company called Skycatch, who was building autonomous drone systems to map sites like construction and mining. And it was just really interesting work on what, what this technology really enabled. That led me down to other robotics interests and that ultimately led me to economy where we're now building robots for farming, and bringing autonomous technology to help with labor shortage where people just can't \[00:04:00\] hire enough. **David SP-VG:** Wow. I mean, that's an awesome set of experiences and you can kind of connect the dots there. You mentioned about Skycatch and the drones kind of doing like mapping. Can you get,what were you guys doing there? Were you making like 3D maps of these sites? **David Chen:** Yeah, exactly. So for mining, it was open pit mines. Essentially, they dig a giant hole in the ground to extract minerals. Um, and it's constantly changing as they blast and remove materials for processing. They need to know exactly where things are and how much volume of material has been moved. Traditionally, a human goes out there with a GNSS rover and they're measuring point by point and then interpolating that to create a surface. But it's dangerous because you can imagine right after you blast an area, it's unstable or you can't measure to the very edge of a cliff \[00:05:00\] because it's super dangerous. So you're kind of measuring couple feet behind and then just projecting that point to give an estimate. So it's not very accurate. What drones allowed us to do was reverse the process where instead of human going out collecting data point by point, you can send a drone out, map the entire area. Generate 3D terrain that's accurate to a few centimeters, and then you can do all the measurements you need on a computer. So that takes a human out of very dangerous environments, it makes the work faster, and getting that data was essential to the operations of these mines, and the surveyor who goes out there to measure it. It's kind of that bottleneck. Everybody's asking for data. Can you go measure this? Measure that? Calculate these things. So it's on them. And these mines are in very remote, often very hostile environments. So being out there, just going outdoors in some of these areas could be very tough \[00:06:00\] and especially in polar regions where temperatures are, negative throughout the year and you get maybe a couple hours of sunlight. Every second count and being able to do your work efficiently is all that matters. **David SP-VG:** Hmm. Wow. That's, so I think a lot of people when they think of autonomous, they think of autonomous car where you're replacing a human driver and there's some concerns about jobs and things like that. Right. And it's also interesting 'cause you guys that autonomy are solving that problem 'cause there aren't enough people to do the job. But in this case it's like autonomy plays a critical role. where it's just dangerous for a human operator and you're not able to even get the level of data, the quality of data you really need. And so the autonomy really helps you in that regard. These sound like really crazy places to go. Like as a, as an engineer developing software, I would imagine it would be quite difficult to like, Test the things you're doing given that like a drone has to be in a remote mining place like Flying around. How did \[00:07:00\] you kind of like overcome some of the sort of like actual use cases of these things and learn and improve your products? **David Chen:** Well, what we found is to build products for certain markets, it's not enough just to think about and talk to the people who are going to use it. You really need to live in their shoes to experience what they experience on a day to day basis. And for us, that means actually going out to the mine sites, working with them, helping them, training them to use these systems, and then actually experiencing what that process is like. So for me, I spent some time up in the Arctic Circle at, A place called Red Dog Mine. It's a super remote mine, but I think a large amount of nickel and lead is mined from this single site that supplies the entire world. So it's very critical for all sorts of, production downstream of that. flying up there, you take a Alaskan \[00:08:00\] airline, it's a special charter that flies to the mine twice a week, they have their own power plant, they have their own runway, essentially you fly in, there's a compound where a couple hundred people live and work and they go on these crazy schedules of two weeks on site, two weeks off site, pretty much working nonstop in like these insane environments just to mine this material. And. Every minute of downtime is, costs them money. So they need products that just work in any kind of environment. **David SP-VG:** Wow, and so what are like when you're there and you're in the Arctic Circle And this product you're working on probably normally from your, your office, right? Committing code, what kind of things were you able to do and learn on site, as part of that product development experience? **David Chen:** Yeah, sure. So the things that you don't think about when you're just developing software, because for software you think about, Oh, uh, you know, it loads, it does the right things. It, but in \[00:09:00\] these environments you have to think about. It's negative 20 outside. So what does it take to open up a kit, take out the drone, set it up, get it ready to fly? Because that actually is challenging because you're wearing gloves. So we wanted to design systems where it was really easy to set it up. You essentially have everything ready to go. You pull it out, power it on, and it's able to do its job. and getting that data is also important. Where do you process that data? In normal world, you can say, all right, just go upload this 10 gigabytes of photos to the cloud, get it processed. But at that mind, their only link to the internet was satellite uplink. So it was very limited bandwidth, which made that completely impossible. So what made our systems work is that we develop solutions to process this data on the edge in the field. So by the time they're done flying, they can Load that data from the drone into our edge \[00:10:00\] compute device. And by the time they've driven that 30 minute ride from the bottom of the pit up back to their office, data's ready to use. They can pop it into their computers and get on with the next step. **David SP-VG:** Wow, that's awesome. Uh, yeah, it's definitely a more rounded way of thinking about your products. Like, you gotta get out there and it influences your The actual software you write and solving some of those complex engineering challenges. So how'd you end up from mining then into ag tech? **David Chen:** yeah, so I was looking for a change. I'd been at. Skycatch for almost eight years. And a former colleague was at a company called Ektonomy and they were building autonomous tractors. I went out to, uh, founder has a vineyard up in Sonoma. I went to visit them, saw the tractors in action, and it was really exciting. And. Seeing it do work on its own and being able to be remotely controlled and autonomously drive around in a \[00:11:00\] lot.and for me, it was always finding something that not only was I interested in working on, but also what good does it bring to the world if I contribute my time and energy to making this successful product. You know, that's something that drove every single decision that I made in life is like, this is going to have a bigger impact. **David SP-VG:** Was there anything specific around these like hardware, you know, both, of these, um, career experiences you're describing have very heavy hardware angles to what they're doing. And you can contrast that to a lot of software engineers who are building kind of pure software. Like if you're working at Facebook or something, like your entire, Product lives as software, essentially. You're, building software that integrates with, real things that are flying around or driving around. Was that, purposeful or was that luck or is there something that just really excites you about, the hardware software connection? **David Chen:** I think that was purposeful. you know, ever since I was young, I would love \[00:12:00\] building things. that's always been something I've been interested in. In the early part of my career, I did pure software, building web apps and mobile apps. But once I got sucked into drones, it re revived that passion for robotics that I had as a younger and,led me to focus on career choices that involved hardware, because I just think it's so much more interesting when the software that you're building actually interacts with the real world. **David SP-VG:** and the humans in the world, too, right? But that's interesting, because you have, these autonomous vehicles you're creating Uh, the drones or the tractors, but one of the key things you were describing is still the human element of like, I gotta open this box and put it out there. so that's, I just think that's an interesting thing where it's this human hardware, software interaction. I think a lot of people out there maybe don't have a lot of that experience. Is there unique aspects of, as a software engineer, that you think are fundamentally different when you're developing for this sort of hardware, \[00:13:00\] these interactions like this, as opposed to just a pure, Map or website or something like that. **David Chen:** Um, yeah, I think there is some differences. But I think it all comes down to understanding what the user is trying to do. I think a lot of software is built technology first, and then you give someone a product that you come up with. Whereas the products that I've worked on in this robotics realm, it's first understanding what is the traditional workflow that they're doing, What is their steps? And then instead of trying to give them something new, you bring them something that's familiar, but is doing their original task in a much easier or much more streamlined way. So really understanding, their day to day work is extremely important. **David SP-VG:** so you you worked in two very specific Companies \[00:14:00\] right and I'm curious if there were any sort of key tech decisions You made at those companies that really stand out or any sort of even I'd love to know also even Commonalities between those two companies or lessons learned maybe a sky catch if we go back there were there things that like when you were doing that when you're working on that and all the Environmental concerns you were thinking about and all the Product Concerns. I mean, one of the things we're really looking for is how do you optimize success in these efforts? You know, we have limited time, limited budget. What were some tech decisions you made that you think helped you to be successful as a, both as an engineer maybe and as a company back then? **David Chen:** Yeah. I think, for Skycatch. When we were creating the company, there's a lot of hype around drones. A lot of companies building drone software. what we really honed in on is what is the critical piece of this whole system that's really important. And what we found is, The core technology that does the 3D reconstruction was a core thing that we \[00:15:00\] decided we must own. I was able to hire a computer vision team to actually build our own proprietary 3D reconstruction engine and do it in a way that We made it portable. We can run it on embedded systems with lower power, lower, resource capabilities. and that allowed us to put everything onto edge compute devices, so it didn't require a large workstation or systems in the cloud to process. And we built purpose built hardware around that software that we could use in the field. So building ruggedized base stations that ran our computer vision software that also acted as a high precision base station for the drones. That was kind of one of the key products that we built at Skycatch that really made a difference in the customers because nobody else had that. Everyone else, you have to. Fly the drone the same way, but then there's a dependency on getting that data to a \[00:16:00\] workstation or uploading gigs of data to the cloud for processing. **David SP-VG:** So I think that's really insightful because I think a lot of people who are maybe starting up a company The hard problem that is core, you know, your budgets are limited or you might kind of do all the stuff around that and try to find some off the shelf solution, grab an API or some other existing kind of tool that does that. But you guys identified early on that was the core thing and you had to own it and like really investing in that core thing. That's what really enabled you guys to get that portability you were describing and the ability to actually leverage it. Like if you had used an off the shelf solution, you don't think you would have been able to get the same outcomes. **David Chen:** Yeah, I think it would have been a very different path. other companies have done it very successfully, uh, targeting different markets where there wasn't that need. But for us, because we had a focus on this tech, it opened up a lot of doors in, areas that really needed the portability and the quick access to data in the field. **David SP-VG:** So the password is to Agtonomy because I just think it's so cool. when we talk about it, I'm \[00:17:00\] like, ah, it's so awesome. Just these, see these tractors driving around. And, tell me a little bit, maybe dig into that. Like now that you're at Agtonomy, what are some of the interesting challenges you guys have faced and, what has sort of been some of the more exciting things you've been able to crack into? **David Chen:** Yeah, I think a common theme between Skycatch and Ektonomy is when the user interacts with these systems, they're using software that's super easy to use and super intuitive, which is making a super complex process like drones and high precision GPS and all of that stuff. You put it behind a good UX and that makes a huge difference. Thanks. One of the key tech decisions at Ektonomy, and this was something that the team made right before I joined, was, using Flutter to develop all of their apps. I think, at the time, they also tried out, React Native, but ultimately went with Flutter, because it was just easier and quicker to get going. And these guys are not You know, they're \[00:18:00\] not new to programming. They're all very smart, but focus on like more on the autonomy and embedded side. So seeing that, how they were able to quickly pick up Flutter and build a working app was something that was new to me. I had never used it before. Once I came on board and started building with it, I realized that. It's incredibly powerful, and, I hired a team, some from previously at Skypatch, and some from other places that I knew, and they all have never worked with Flutter, brought them in, they were proficient in a matter of weeks, and that led us to building out now three apps on very different platforms, all sharing probably 70 percent common code base. And letting us iterate and move really quickly while maintaining highly testable and reusable components. **David SP-VG:** Yeah, you mentioned the portability thing before, right? in terms of even in the Skycatch story with your 3D mapping tech and making sure that you could get on these embedded systems andit's \[00:19:00\] interesting to hear you say about the 70 percent code reuse. I think when people think about cross platform or multi platform tools, they're thinking about like single code base, multiple apps, like same app, just different platform. But is it correct to say that you guys are basically like actually distinct multiple apps, but where you're sharing a lot of core logic? **David Chen:** Yep, exactly. So for us, we have a tablet app that's mainly for monitoring, controlling the fleet, planning your missions. and then we also have a web app, which is more for analytics and metrics. And we also have the actual on vehicle dashboard itself. This is when the operator is driving manually, they have a touch screen that they can see, telemetry and control the vehicle systems. They're very, very different apps, but, because we use Flutter for all three, we have a shared SDK that's used across the board. We have shared widgets, we have shared data models. So all the foundational pieces. \[00:20:00\] is the same and a small group of engineers can maintain that. And then for the very specific platform things, we can build different things by composing these elements together and it just works. **David SP-VG:** Yeah, it sounds like you guys took what I think is, the holy grail approach to these types of tools, which is not thinking about single code base, same app, multiple platforms, but what if we have an ecosystem of apps that are all architected, built consistently, and in a common, with a bunch of common shared libraries and set that up, so that your team can, your team then becomes really portable as well. Like, are you guys able to just, easily hop across all three of those applications super easily? **David Chen:** Yeah, totally. Nobody is assigned to working on a single app. they work across the apps and I found that to be really great. Really good and allows us to be very efficient. So someone might own the SDK stack across all the apps and if they need to make changes, that's \[00:21:00\] automatically available for all the other platforms. **David SP-VG:** And the, if I'm correct, the one on the, tractor, that's not like an iOS iPad or something, is that, what operating system is that running? **David Chen:** So for that one, it's an embedded Linux system. So that one also is Flutter and it just boots right into a Flutter application. When the system boots, it looks and feels exactly the same as our other apps. But you just never know what's underneath it. **David SP-VG:** Yeah, that's, I never know what's underneath it, I feel like that's the new marketing campaign for Flutter right there. well done. no, I think, cause, one of the things I think we face when we look at some of these really transformative tools is you base your perception of it on prior mental models. And you might look at a thing like React Native, or you might look at some of these other tools, and I think what you guys are doing that I've always been, like, that's really exciting about it is pushing it into these boundaries of no, this is truly, multi platform. This is, we're using multi platform. Linux on a \[00:22:00\] ruggedized device on a tractor. We're not just talking about iOS and Android apps in your pocket. and would that have been a lot more difficult for you guys as a team, staffing, resourcing, hiring, if you had to hire, did it some other way? **David Chen:** Absolutely. I think we would have needed a lot more engineers to actually build out everything that we have built out. And we would have to hire very specialized resources that are experts in that respective platform and language. Which meant,it would be much slower to build an app and we couldn't move people around to work on other parts of the system when there's needs. **David SP-VG:** Yeah, that's great. And so it sounds like in both, both your experiences that you described, defining that 3d, core, core software and making a choice like Flutter, I'm sure they were like, Many, many, many other important technical decisions along the way. But it sounds like one of the things that you, you've prioritized in your roles is like making these like really important, high leverage strategic technology choices, right? Choosing where to invest and what to \[00:23:00\] invest in to give your team The flexibility, portability, is a word we've said a couple of times, and sort of scalability to really be as productive as possible. how do you like, make those decisions? I mean, I guess to some extent the Flutter decision maybe predated you, but even still, as you consider down that path and making some of those choices, or when you're observing your teams making decisions around priority, or decisions around, like, critical technology choices, how do you as a team make those effectively? **David Chen:** I think you have to take a step back and really look at what are the things that you will build or you will need, and really take that into consideration when you're architecting your apps. We're actually going through a pretty big refactor to bring a bunch of things up to speed. There were some tech debt that caused certain apps to lag behind, but being on Flutter, we were able to, build something new and port it over to the new apps or a different app with pretty minimal effort. And \[00:24:00\] now they're all pretty much. Pretty much identical underneath. I think the Flutter ecosystem is moving really quickly and we're always finding new packages and new tools that will help us accelerate our development. So it's, it still feels. Relatively young in terms of Flutter itself. I know it's been around for almost 10 years now, but, it's still growing. I see a lot of excitement around Flutter. I was actually just at a Flutter meetup, last night and met some of the Google team there that works on the Flutter frameworks and they're all super excited and just seeing. them talk about how the adoption of Flutter is really growing and accelerating all over the world is just really good signal that we've made the right choice. **David SP-VG:** Well, I tend to agree with you for obvious reasons, and I think it's, uh, you know, the thing about Flutter is it's a high leverage tool, is like what I like to say about it. It's like all the things you were describing about the impacts \[00:25:00\] on your team. And there's a lot of things that you can do, and you've made choices elsewhere in your technology stack throughout. All of your career experiences where it's like creating these leverage points for your team where you can kind of like unlock additional productivity. and Flutter, because you can, like, you know, anybody can work on any of these apps and the complexity in your organization, if each one of those apps is a different team staff, differently, different product manager, right? Different engineers. And you have to coordinate them and talk through all the requirements. and really like at the end of the day, the user experience, like you said, you want to make the UI really, it's super critical you get that, but still like. There's a lot of work being done, I would think, on the autonomous side, like actually making the tractor actually be autonomous. And in a way that's like the killer feature, but you need this front end and you don't want the front end development to get in the way of that other stuff. or be this extra strain and burden on the business. **David Chen:** Yeah, absolutely. And I think for a lot of these industries, The UX is so important, not to make light of the autonomy side, that is an extremely hard problem and takes a lot of work to get \[00:26:00\] right, but what I found is no matter how good the underlying tech is, if you present the user that's using this as their livelihood and it, something breaks or doesn't work or costs them an extra hour to figure out how to do something, They're just going to throw it away and never use it again. You have to make it easy to use. and that first impression of how well that app works is so important. these jobs, they, you know, every second counts, they can't spend time figuring out an app or if it's buggy or, you know, it's unintuitive. That's the most critical piece. **David SP-VG:** Yeah, makes a lot of sense. these industries you're in, I find it fascinating because you don't really come across too many people that have built tech drones for mining, right? or even ag tech, I mean it's out there, it's a big, big sector, but You know, so many engineers gravitate towards, other types of things, whether it's e commerce or, online tools or whatever. It's interesting to me that you've focused on these \[00:27:00\] connections and there's something about even those industries that there's some stigma to mining and even agriculture, you know, pesticides and things like, how did you get into those places? And how did you like, what's your perspective on getting into these types of industries that maybe an engineer out there might not be prioritizing when they're thinking about their career? **David Chen:** Yeah. In terms of, at Skycatch, when I joined, I didn't know anything about construction or mining. mining, I think, to the outside perspective, it's like, oh, it's, it's dirty, it's hard work, there's toxic chemicals you hear about. You know, tailings dam collapses or failures that just pour toxic sludge down a valley and destroy the environment. That's absolutely true. There's always these very negative aspects about these industries, but also everything depends on it. This is the core resources that Pretty much everything, the computers, the houses that we're in for ag, it's the food that \[00:28:00\] we eat every day. It's an extremely important. It's, it's something that will never go away. And I think it's very important to have people working on improving these processes. How do we make. Mining safer. How do we make the environmental impact minimized by monitoring things more carefully and preventing these catastrophic failures? For ag, how do we minimize the use of chemicals in farming by using more intelligent systems that can do things mechanically instead of chemically? **David SP-VG:** Very, very cool. There's definitely like a really interesting connection, right? Mining and agriculture, because there's like a supply chain that is so crucial to us living our everyday lives. You know, we're using Devices right now, or whether we're on the other end of this listening to it in our car, or on our airpods, that had materials that came out of those mines,  and similarly, like, with food, or the wine we drink, \[00:29:00\] or whatever, that comes out of agriculture, and I just think it's really interesting, those threads of, like, building tech that helps to improve safety and quality. Uh, Efficiency,all those things to build a, build us a better world. I think that's super cool. I'm, what, like, kind of parting thoughts or what, when you meet, an engineer out there or a team that's starting out and they're gonna build some complicated system, right? what advice do you have for those people that, they could follow to be more successful or optimize their chances of finding success with the tools they're building? **David Chen:** I think the first thing is find what you truly love doing. And for me, what really unlocked that was drones. When I realized I had this passion for building software, hardware together into a system, and then once you find that, then I think it's to find how can you use your passion and skills to work on something that makes a big impact, a positive impact on the world. And \[00:30:00\] then if you can do these two things, and I, then I think whatever you're doing, you're going to find fulfillment and happiness every day doing it. I think that's really critical. I know a lot of people who work for large tech that, really hate their jobs, but they, maybe they get paid a lot of money to do it. So they stick with it. But,I much rather know that what I'm working on is helping people on the other end who's using my product and hearing people visiting the customers, hearing them say how great this is and how much it's improved their lives. that's so important and, um, is all that I really need. **David SP-VG:** Do you think that helps you write better code, or make better tech decisions? I'm curious, like, I mean, well, it gets to culture, right, as well. I mean, it's not only your personal passion, but all the things you're saying, like, are you happy, are you fulfilled, are you motivated, all those things, I think, maybe some of the reason those other people are not happy in a big tech job is maybe they're not aligned with, what they're doing, they're not excited about the product, but also sometimes, what Cultures can be not \[00:31:00\] great and it's not a fun environment or it's too much bureaucracy or whatever I'm, just curious if you think that drives better results or anecdotally if you've seen better outcomes on teams Where you have that enthusiasm and passion **David Chen:** I think so. I think when you're passionate about the product you're working on and you care about the users of the product, then you get into that customer mindset of always thinking about how do you make this better for the end user? And I think that definitely drives better products because you're constantly thinking about, okay, when you understand what it takes to do their day to day work, you think about during their day when they're doing this, what is the pain point? How can I make this process better and smoother? So maybe it doesn't help me make better code, but it helps me come up with better products. And ultimately, that's what I do. That's what's important for the users. **David SP-VG:** Well, and it maybe drives you to go visit that mine. It's cold. It's uncomfortable. It's far away \[00:32:00\] But if you're passionate and really interested in solving these problems you're maybe as a team more likely to go out there and really experience it and seek to truly understand what the users are looking for. We had a, we had Patricia Roller on here as an earlier podcast, and she talked a lot about the importance of customer insights, like how customer insights have to drive a lot of your decision making. And maybe if you don't care, you're not going to fight as hard to find those insights, right? exactly.  well, this is great. I don't know if you have any other parting thoughts. covered a lot of ground here. I think just incredible, experience and background. I think just an area and a space where, I think it's novel and super interesting. I mean the whole autonomous space, started with Waymo and, but getting into like where autonomy is going, I think people see, you hear about, you know, full self driving Tesla and you maybe hear about things like Waymo, but the idea that there's autonomous drones out mapping minds is extremely cool. And thank you for sharing your stories today. And really appreciate you, helping us out. **David Chen:** Yeah, absolutely. It's been a pleasure chatting with you. **David SP-VG:** Where can people find out more about \[00:33:00\] Agtonomy? If you guys are hiring, or if, if somebody's out there happens to be looking for autonomous solutions for their tractors? **David Chen:** Yep. We're hiring across the board on all the engineering teams. agtonomy. com. **David SP-VG:** Alright, awesome. Well, thanks so much, David. Can't thank you enough. **David Chen:** All right. Thank you. Take care. ### How AI and Flutter Are Shaping Development Source: https://verygood.ventures/podcasts/teresa-wu-how-ai-and-flutter-are-shaping-the-future-of-development/ Ready to revolutionize your approach to tech? We’re joined by Teresa Wu, VP of Software Engineering at JPMorgan Chase & Co., whose previous roles include Senior Staff Engineer at tide, and Android and Mobile Lead at Ostmodern. Teresa shares invaluable insights on mastering new technologies like AI and Flutter, emphasizing the critical role of community, curiosity and strategic risk management in successful product development. {% audio_embed url="https://player.cohostpodcasting.com/0a6f8233-1031-4790-b816-4a41b2dca396/4979711c-9f75-48b1-a956-2f958f4d7165" /%} ## Transcript **David DeRemer:** All right. Hi, Teresa. Thanks so much for joining me today.  **Teresa Wu:** Hello. Hello. Nice to meet you, David. **David DeRemer:** Yeah.  Nice to see you. Um, so it's been a while since we met in person last, um, we've spoken since then, but almost a year since Flutter Con in Berlin last year. Uh, how, how are things been going? How's your last year been?  **Teresa Wu:** Well, I have to say it's pretty, really, it's really busy. And it's been actually, uh, the year is flying. And I'm going to FlutterCon again this year. I'm really, I'm really excited. It's just like this year was like, yeah, really fulfilled with lots of things, exciting news. And, um, yeah, super busy year. **David DeRemer:** Amazing. Yeah. That's coming up. It's, uh, so there's gonna be one in Berlin and then there's one in New York City as well. You think you'll make it to New York as well?  **Teresa Wu:** Oh, I didn't know that. But I hope so. Someone, if someone invited me, I'll definitely fly there. **David DeRemer:** Alright well I know some people, so I'll, I'll put a good word in. Um, so why don't you, let's take a step back in that. Why don't you introduce yourself and give us a little bit about your background and what you're doing Right. now. **Teresa Wu:** Right. Hello everyone. My name is Teresa Wu. Oh, wow. Um, actually I started, uh, my career, actually, it's not from it. I \[00:01:00\] self-taught it myself. I really enjoyed it. Then I just applied for my, a computer science master master degree course, and then I started my IT career. Since then, I've been mostly in fronted. Try the different frameworks at the moment. I'm in love with Flutter. And then, um, I started to share things. I know I'm excited about in Flutter field. That was many years ago when just Flutter just started. I got noticed by the Google team and the community. And later on, they sent me the invitation to join the GDE, the Google developer expert G roup since then, uh, that was two years ago. Um, Then I had one side of me is to work in a fintech industry as an engineer. The other side of me is joining lots of community events, be a, be a very active speaker, share my knowledge, my experience to the wider group. **David DeRemer:** So you started in Flutter just, uh, um, on your own time as \[00:02:00\] your own personal interest. **Teresa Wu:** So back then Flutter was just like a new framework that came up and we hear different side of stories about product releasing by Google team, as we know, uh, how supportive they are and how positive the community is. That is my personal interests. And I talked to lots of engineer back then in London. Cause Flutter in Europe is, uh, quite, uh, popular. It has a huge community and that's how we all started. I met Simon, I met Remy, I met like lots of very, uh, good engineers here. Sorry if I didn't mention your name, but that's how we all started this together. **David DeRemer:** Um, and you, you, I think Tide, you were at Tide for a bit. They are Flutter. They're using Flutter there as  well.  Right.  **Teresa Wu:** when I was working in tight, we are 100 percent native, right? And there's a start. There was the conversation started with. We should go. We should think about a cross platform tool to accelerate \[00:03:00\] our, uh, development and make have a better dev experience. Our CTO back then, uh, suggested the real native. To begin with, we did some research and then we started the Flutter. I was the first one, uh, not, not the first one, but I was the, the, the key person who is very excited about Flutter, but Flutter hasn't released. At the time, the version one didn't come out, but luckily it does actually released, it was released in London in 2018 and we went to the events and people started to learn, and also we had a conversation with VGV group. You, your team really is really supportive back to them, although we, we didn't actually, um, collaborate, uh, on the, on the financial term, but we had lots of great, um, Conversations and the nice meetings from people in your team and that you, you, you support us through the journey. In the end, we actually decided that Flutter is the framework we're going to use. So now, uh, even though I'm not working for Tide at the moment, but while I was working Tide, we switched from native \[00:04:00\] to Flutter. **David DeRemer:** I love that. I mean, I think that's a good recipe for people getting into new technology. I have someone like yourself, a passionate leader who loves the technology and is the champion within. And then I think just the benefit of a supportive community, right. Which we just consider ourselves to be a part where, um, you know, the rising tide raises all boats and we figure as long as the community helps each other out, you know, um, that's all that really matters is for, for these technology adoption things. So that's a wonderful story. And now you're at a JP Morgan. Is that correct?  **Teresa Wu:** Yeah. Then after that, um, I joined JP Morgan because I also have some other interests about learning more about the fintech industry. Cause this one is for me, it's, It's old and new. There's lots of things involved in this field. I want to learn a bit more and also seeing the new product launching in the UK. That also was a very exciting process as well. **David DeRemer:** Yeah. And so I, you know, as you were telling me a story once about how you're into skiing and I think about some of your career and your interest in kind of pursuing things on the side. Um, and things like Flutter and you're working at a place where, you know, you have to balance these new \[00:05:00\] emerging technologies and things you're interested in with the day to day of the work. Um, and I, I, you had an insight that I thought was really interesting around sort of doing things that you don't have time to be great at, you know? Um, and I think skiing is just a really fantastic example of that. You know, you know, it's not like you can do it every day all year long. Um, unless I guess you're a pro skier that can travel the world. Um, and so I'm curious like how things like that have guided you. Your technical, uh, abilities and skill development over the years. Cause it changes so fast. Right. And how do you, how do you stay up to date with things that it's hard to have the time as a leader and as an engineer and someone with a personal life and doing all these things that you might want to do, how do you stay up to date with what's going on? **Teresa Wu:** Right. Actually, talking about skiing, something I'm quite interested in, but I agree with you. Ski for, you know, For people who have commitment for work and family, and especially I live in London, I don't have a ski place to go, like local. I have to fly or travel 10 hours to France, for example. That's, maybe that's part of my personality, right? I do like to try different things. I \[00:06:00\] started to ski in, uh, How do I say in the common where people always go to those places in France and in, um, in Switzerland. Last year, I actually went to ski in a very unusual place in Hampstead, in Norway. That was negative 20, uh, in terms of the degree. It was really, really cold. I just wanted to try out. Doesn't mean I will keep going back every year to the same, to the same place. I like to say something new. To try out. And one thing I would like to say is, um, just feel curious, always have the chance to try something new, but you don't have to be good at it. I know this is the opposite of what we learn in university, where people tell you just have to be good at something, especially when I come, where I came from China, right? Education system basically says that you have to be the top all the time, but actually you don't have to. **David DeRemer:** I think, Um, skiing is a fantastic metaphor for learning technology. Actually, I never, I'm a big metaphor person. I'm I often \[00:07:00\] speak in examples and metaphors and you're, you're, I think it's just such a good example because skiing is the thing where it's not really that fun the first time. If you're falling down all the time, you're catching edges, you're falling on your butt and your wrists hurt, whatever. Um, but then eventually you kind of figure it out and you, you kind of get the enjoyment and the thrill, um, and you, you do it, but to be super good at it. You have to do it all the time, um, and I think that's just a good metaphor for a lot of things in life and things that we learn, um, no different from technology and as technology leaders. It's so hard because the world around us is changing constantly. And so how do we maintain that expertise that makes us useful leaders or engineers when, you know, every I am flutter now is like getting a thing like back when it started, it was just like brand new thing. It was crazy. No one knew it. Now it's sort of like, yeah, of course we do flutter. Like, why wouldn't we, you know, it's just, so what's next is the question. Um, So are there things right now that are taking some of your attention or your interest, right? Where this, this creativity and this curiosity you have, what, what's peaking your curiosity these days? **Teresa Wu:** Um, one letter, sorry, one \[00:08:00\] word, two letter, AI.  **David DeRemer:** okay.  So, you're in on the A.I as well. Yeah. Yeah.  **Teresa Wu:** I think everyone is right. Uh, not just Flutter engineer, even people are not engineer. I think that's where everyone talk about nowadays, right? People from product, from testing, from software engineer, from marketing, from product design, UI, UX. This year and last year was just the year of AI. Even when I was on the tax, taxi drive to the airport and I was talking to the tax driver saying I'm going to attend the conference and he asked me what kind of topic I'm attending. I just said, I'm going to do a talk about AI, but in front end industry and he was like, wow. Yeah, that's very scary. Also fascinating. So yeah, that's really exciting. It's not just me, but I think it's everyone's common topic as today, but I do have special interests in AI, not just to talk about it. I actually went diving a little bit more to understand this, the, how things works behind the thing.\[00:09:00\]  **David DeRemer:** is this a personal interest you're, you're pursuing cause you were mentioning sort of before you kind of have to balance your professional needs and curiosities with your personal needs and curiosities are, are, is this kind of both? Is it driven by one or the other?  **Teresa Wu:** Um, so at the moment, AI just personal interests. That's why I couldn't go ski every, every, every month. I just have to cut some of my time to do some other things, but which is great. Um, I'm not using. Um, I'm not an AI engineer. I don't have any AI background. I don't use AI in my work. I don't write code to build AI. It doesn't mean me or anyone else couldn't learn AI. It doesn't stop you from actually understanding how AI works, especially as a software engineer. We solve problems every day. And if you actually know something extra, Beyond your current level, it will help you enhance your, um, experience, your expertise. You can actually add something more to your product. If you know how those things works.\[00:10:00\]  **David DeRemer:** That's it. That's fascinating because I think a lot of junior engineers might think, Oh, I need to learn every, you know, every, uh, part of this SDK or every feature of this programming language or framework. Um, but it sounds like what you're saying. It's, it's just as important to have a wide range of experiences and insight to, to inform what you're doing. It's very cool. How do you, how do you learn? Like, how do you, um, what are some strategies you found when you have a busy life, a busy career, a lot of things going on? Um, how do you, uh, what are, what are some effective ways you found to dig into some of these things like AI?  **Teresa Wu:** So I would just share a bit of, of how I actually allocate my time. Hopefully this help others, right? Actually, I did the same, not just for AI. Um, I I'm doing the same all the time through. These years when I know Flutter, uh, self learning, uh, programming in the beginning is you definitely have to have a very tight schedule. Um, I have a list. I have a to do list in the morning, and I look at what I need to do in the day, the next day. \[00:11:00\] And in the morning, I actually handwrite my to do list, so I memorize it again. And this might sound stupid, because we have Google Calendar, we have so many tools, right, out there. But I do handwrite it myself in the morning, and give every task a specific time. Like, for example, nine o'clock, I have my morning meeting and from 10 to 11, this is what I'm supposed to do. This is why I'm actually able to allocate an hour or two each day, maybe three times in a week to learn something new, read a tech book, or study a course. Otherwise, it's just, for me, if I don't have this schedule, it's impossible. **David DeRemer:** It's the chaos of keeping up with that. So, so a rigorous plan, you know, putting down, taking the time to do that. I love the idea of writing. I mean, I'm the same way. Like my, my to do list are these things right here. Some post it notes. Um, you know, it's like, you're right. We have all this technology and AI, um, and tools. And I found that nothing is a better task manager than you. Just sticky notes littering my desk and, um, the urgent ones go right in \[00:12:00\] front, you know, um, no, that's, that's amazing. So, and, and I know you're very active in the community as well. Um, that's been something that you've, uh, seems like it's been an important part of your career and your personal and professional development. Um, do you, like when you embrace a new thing like AI, is that, do you, do you reach out and engage those communities to learn as well and participate in them? Um, what's your approach to community?  **Teresa Wu:** For myself, right, I don't ever have different learning style and study style, I, when I, Put myself as a goal to study AI. I found this one is a bit chaotic because there is a such a board topic. Not like when I was learning Flutter. I know this SDK need to use, but look at AI. It's just so general. So bored. I don't know where to start. This is the time actually, um, Let's I will tell myself I don't get stressed about it. Pick up a easy course, like a beginner course for dummy to actually have a look what AI is all about. And once I understand \[00:13:00\] that the wider topic, then pick something more specific to dive in, because I wouldn't have time to know everything on AI. I have to decide, for example, I'm not a data scientist. Maybe this is not the part I would be interested in. So I will give up the part in AI industry talking about data scientists, talk about data, talk about the feature engineering. I understand them, but I don't have to dive in. Just then from those 10 topics, right? And once you understand all of them, pick one that you're really, really interested in, and then dive in. That's how I started to, um, my journey of learning AI. I'm not an expert. Once I learned those things, I look at them as from a beginner's view, and trying to, um, put myself in, in a, in a position where my audience, It's the same as me, who has lots of, uh, background as engineer, but may not have, uh, AI expertise or studied AI in university. I put myself interview, put \[00:14:00\] starting to write study notes, and put them in the slides, and share the results. Um, share what, uh, honored with with the community. And so far, actually it's been great because I, um, I starting to talk about ai but combine, um, how we use AI technology, especially gene AI in front and project. So I do actually associate with some of my work experience together, and so far those topics are quite well received. So, um,  **David DeRemer:** if I can recap your method here. I mean, first off, you have an innate curiosity. You want to keep up with it in order to kind of round out your expertise. Sounds like we had another conversation the other day with Karsten. Um, and he was saying about atomic habits. I don't know if you read that book and it sounds like you actually, I'm drawing some connections here because you're very methodical about planning out your time and making sure that like, even that, that insight there of you don't have to. Yeah. Jump ahead to being the expert, but find that beginner content, find the bread crumbs to figure it out and get into it. And then lastly, like kind of share it back with people as a, as a teacher and a community member, I think \[00:15:00\] that's a good recipe. Um, uh, that's, that's really awesome. I mean, so what is it with AI right now? Do you think AI is going to stick around? I mean, do you think that, you know, over the last couple of years, there's been metaverse and AR VR and crypto and. NFTs and, and now ai, blockchain, all these things, these like sort of trendy technical things that everyone goes crazy for and runs after really hard. And then like, what happened to Metaverse , you know? Um, where'd that go? Um, what do you think about ai? Is this one a little different? **Teresa Wu:** Um, it's probably not, it's probably be one of those topics that comes in and people just forget about them after one year or two, or it might just stay. Right. Depends on how the community accept AI and how we actually, because we will be the front line of, to push AI into a product. If we accept AI first and this community will grow and we will see the user accepting AI more because right now there are lots of conversation about UI, but when it comes to products. It's still very limited in terms of the variety and how AI can actually be applied to different industry. So \[00:16:00\] as engineer, actually we are quite lucky to be in, in today's technology. We have the tools and platforms to do something about it. It's up to us how we actually want to shaping it. **David DeRemer:** So how, as a product leader, you're VP of engineering at a, at a large company, a large FinTech company, um, when you get these sort of curve balls and all of a sudden the executives and the sort of corporate strategy suddenly pivots to, oh my God, we need to do ai. How do you approach those inputs into your team and your product and your roadmaps? Um, how do you tend to react to those and figure out what to do about  it?  **Teresa Wu:** Uh, personal device, right? Don't quote me on this one because this is two part of me. One part of me is engineer who like to try something new and always be passionate about those new things. And the part of me being an employee of a company have to be responsible for our product for end user. This part of me wouldn't rush into something new. Whenever we, I heard something new or let's put this \[00:17:00\] new SDK, let's put this new language in our consumer product. I actually put myself back and I think it few times before we actually go ahead and using it. Uh, but so when it comes to work and it comes to our end. End to end product, um, definitely do all your, um, MVP, do your, um, POC, test them out, try it out, and slowly introduce them in your product. Because as, uh, when you work for a very mature company, a very mature product, when you have a very good consumer base, you want to continue the same user experience, make your customer happy, but minimize the risk as well. Because you don't want to repeat your work twice or putting something unknown for the future. But on the other side, as a software engineer, I would encourage everyone to be curious about new things in the NUR. At least you understand what they are, so when you want to apply them, you can \[00:18:00\] make good suggestions, good advice, and basically you, you know what you are saying. **David DeRemer:** Mm. And, uh, do we, how, how do we fend off the pressure? You know, I think non-technical people might assume, Hey, I don't get it. Just hook up the ai. Just make it work, right? Like, I don't understand, why can't we just add this to the product? You know? What, what is the, um, how do you see AI reshaping product as you go forward? Um, and you, as you imagine that. You know, you have to be cautious, you have to be safe, but there's certain changes we have to make because if you don't have an AI story, you know, are you getting the valuation if you're a startup or are you getting the enthusiasm of Wall Street or, or investors? Um, like what, what is the, what do you, how do you see AI shaping the product development capability going forward?  **Teresa Wu:** Um, it will definitely change how we work, how we use our product, and how we actually design our product. But you don't have, how do I say this right? You wouldn't have a single answer for this question because it's very wild. We need to look at this from different \[00:19:00\] angle. Um, for example, so let's say right AI, there are three part of it, right? What, how AI can impact our end user, our consumer, our client, this is where we add a lot of fancy functions. Coming from you are coming from a I, for example, um, some a I document the chat bought a smart health care, a smart Jane trainer application. Those are the functions we added to our product to our user. This is for the customer part, and those are very attractive. Even if you are building a very mature product, or if you are startup companies, right? Adding those functions in your product, will be a very attractive shining point in today's market. And if we look at from the enterprise level as from a company's view, how do we build those features in our product? What are the platform, the technology out there that we can use? Because we have ideas, everyone have smart ideas. But how do \[00:20:00\] we make this true? How do we make this end product? What platforms are there? This is where Uh, as engineer, we will make a device to try out, uh, doing some PLC to make some small product, a viable product that's from enterprise level. How do we make those features? And as for engineers, software engineers, we want to use AI to accelerate our work, to make our life easy. How can I make an AI bot that can just code for me? That's for AI dev experience. Depends on the company, right? So like, some companies, they emphasize a bit more on and, uh, and, And the product, some companies, actually, they have analyzed the market. They are know what they want to build and they need a solutions for the enterprise level. They want to understand which platforms I can use, which technology I can use or for like startup or for, um, uh, a community, uh, use. They actually looking for solutions for dev \[00:21:00\] experience. What tools are there, like, for example, uh, code a pilot, um, the IDX project. What are those tools I can use in my developer team to help our engineers code in a better quality. So there's a lot of talk Right. now, or I don't know, a lot of talk, but people say, Oh, you know, AI is going to replace all these workers and it's going to replace developers. Do you buy into that at all? Do you, or like, what's your, what's your perspective on that? Because I think what, what I'm hearing is that actually these tools just add leverage and capability to our engineers. Um, So what is your vision or what do you think these tools are going to, like, if you, if you were to fast forward a few years. How will these be influencing how we develop software? Right.  So that's where I actually have this story of when I go into a taxi, uh, taxi on the way to airport, I chat to the taxi driver and he was saying to me, AI, right here, and then he just posed and say, that's very scary. And the reason people feel scary is because they, they, they are facing something unknown. They actually don't know what AI can do. When you \[00:22:00\] don't know what this tool can do for you, you will feel frustrated, scary, and the, the, the end conversation might be, Oh, AI is here to steal my job. Right, but let's think about whether it will, or how can they, right? There's a study by, uh, by the Google team. They look at the percentage of coding in the AI, uh, in the AI project. The actual lines of code that in the AI project is only 5%. The other 95 percent is on data. Um, clean the data, preparing the data, and, uh, and then train your model, refine your model, and then, uh, build up the whole AI, um, Uh, the, uh, the machine learning pipeline to, to deliver the AI model, build API, monitoring it, and the feedback to your product and then restart the whole process again. So even AI can't replace the, the, the tool to build AI themselves. So I, I mean, I don't think AI will \[00:23:00\] steal our job, but, but if, if, for example, um, if that was five years ago, when we just started to learn Flutter, we spent. Days, weeks, months to analyze how we can use Flutter in our project. Are we able to build MVP? Are we able to build a feature of our existing product in Flutter and all this work, we'll have to do the manually. But if back then I had a tool of IDX project, um, by Google, it's a web browser, smart IDE. What it does is it transfer the language from one language to different language. So if I have a Java project, I want to see how this project will look like in Dart language. I can just convert it. It won't be production ready, but at least I can see how this project will look like in a different language. How many lines of code are there, how many files, what's the \[00:24:00\] processing time, are there any bugs, any libraries I couldn't use. I will have a very quick analysis. This will help me to make decisions of whether this new framework We'll actually improve our product or not. So those are tools there to help you make decisions. But as engineers, we still have to do the work to make the product  ready. So super fast way to do  **David DeRemer:** proof of concept, both to validate maybe an idea, but also I think that's a good insight around as an engineering leader. You want to validate what that code looks like? What is the performance of the runtime? What is the structure? What are the best practices? I mean, I think that's an area where AI probably has some room to grow up a little bit. Some of the hallucinations and, you know, is it giving you a best practice, you know, Flutter dark code base when it's giving you that example, but it certainly is not a thing you had before. Um, you would have had to find a, uh, an open source package or a library or something along those lines, um, or some content out there that helps you illustrate that. And there's a lot of work, so it saves a lot of time. And I think one of the things that I often think about is like, to me, Flutter and AI, they're like the \[00:25:00\] same thing. When you think about the value they provide for an engineer, I mean, technology wise are obviously very, very different, but like, why is Flutter interesting? Cause it's a, it's a way to give leverage to an engineering team. It's makes engineers vastly more productive. Like one engineer can make iOS and Android, you know, at a bare minimum, let alone windows, Mac, and. Linux and all these other things. So it's extremely high leverage for that engineer who learns that tool because they can do all these extra things. And when I think about AI helping an engineer, to me, it's the same thing. It's making an engineer more productive. I don't think it's replacing, you know, I think when, uh, another parallel there is early on in the Flutter ecosystem or react native or any of these cross platform solutions, there's a thing of like, buy one, get one free. Like, Oh, cool. I can just have one engineer. They can do iOS and Android. And I can fire half my engineering team because of my adoption of Flutter. But I think like what actually is really the value is it's not that you don't fire half your engineering team. What you do is you do twice as much with the engineers you have, you know? So you move twice as fast because I think that's a more, uh, at least an equally motivating factor for a \[00:26:00\] lot of companies besides cost is how do I move faster? How do I improve my product better? How do I generate revenue sooner? Things along those lines. So I think AI is doing the same thing. It just helps us move faster. You know, it helps us do more with the resources we have. And I think the narrative about it being replacing people seems, seems wrong to me. Um, I think people are scared, right? I think AI, same thing with metaverse, I think metaverse, everyone got excited about it cause they could see like ready player one and snow crash and all these like sort of, um, you know, pop culture references and they're like, Oh, we're going to all like upload to the metaverse. Um, and then you see things like AI where people are thinking about Terminator, you know, and they're like, Oh, it's just gonna. It's the existential crisis of mankind. Um, and, and I think that fuels a lot of this. Um, but I think people get a little ahead of themselves and, uh, really we got to just focus on how this is going to make, um, I think people far more productive than ever. **Teresa Wu:**  Yeah. I think actually you have a very, very good point. I had never actually thought about a comparing AI with Flutter as in, in terms of from, for the developer, what they actually say those tools and what you were saying just now, it reminded me how DevOps works. This \[00:27:00\] becomes, uh, uh, becomes DevOps. Because back then we have to actually, uh, uh, shipping our product manually. So the product team, the design team and the engineering team, we just write the code and to do the product in three months and the, just dumb this to the, um, to the test engineering team to the release team. Operation team, and they have to manually package these products and testing them and then put them to the, to the, uh, to the industry. That's then after so many years, right? Of being in it, we say, we say the racing of DevOps, the idea of making things automated and we can ship a faster and the build a quicker. The, I think that's how we can use AI ways as for engineers, how, how the, how we, I can actually accelerate our work. Yeah. But we still, we will be the one actually behind the thing, writing those, um, AI, uh, tools, maintain them. **David DeRemer:** I wonder if long run, I mean, maybe one difference between the crypto, um, blockchain trend and metaverse is that AI has sort of a front of house and back of house, um, benefit, right? It's like people \[00:28:00\] want to add it to their product. They want a chat bot to for customer support or something, or, you know, they want to, everyone wants to emulate a chat GPT type experience. But I think it also has a huge productivity, uh, benefit. Um, it's a back of house thing, operations, and sometimes I feel like, uh, at least what I read or see that maybe we're putting a little bit too much emphasis on the user experience side of what AI can do, and, and maybe focus a little bit more on like, hey, let's figure out how to make this more productive so that we can, Figure out how to do cool UX faster, uh, because we can iterate our products quicker because of our increase in productivity. Uh, given that you work at a large company, large enterprise, what are the impacts? Uh, like I think enterprise has a different, it's a different world, right? Um, if we're talking startups, people can be like, whatever, I'll just take the latest API and like, give it a shot and see what happens. Right. And I can fail and risk and who cares? And I don't care about my privacy policy. Like whatever, I'm a small company, but.  **Teresa Wu:** Are we able to say this?  We care. **David DeRemer:** well, I'm just saying like, you know, startups, I think maybe move a little fast and loose, um, enterprise, definitely not, you know, my example as a consultant working with enterprise that I've always said as a, as a metaphor is like, you know, an enterprise is a big tanker \[00:29:00\] ship and we as teams, individual people working for that company or a consultant or anybody. We're like a little. skiff just banging into the side of that thing, trying to get it to shift course a little bit because it's hard to move a big enterprise. Um, so how do you see AI shifting or impacting the enterprise, right? Given that there's a lot more constraints, a lot more, Um, like less tolerance for risk, more things that need to be buttoned up. Um, how is it different inside of a, of a large  enterprise?  **Teresa Wu:** I think you just summarized really, really well. That's what enterprise do, right? Not just for company I work with, but in general, uh, enterprise are slower. Adapting to something new because of we are under lots of regulation, especially for fintech industry, we are being like lots. There are less people watching us of what we do. So because we have to be very, very careful on what we deliver and what we don't deliver and what the risk we are bringing to our product, giving to our user because our user \[00:30:00\] data, how do we protect those data are the key. Of all this, um, of our product, our daily life, that doesn't mean enterprise isn't, uh, innovation or they are not innovative. It just, um, there are different ways, different approaches of how we look at those, uh, new technology. Some companies are trying. New things every day doesn't mean they will actually use the mean are in the end product, but they are getting ready. Warm up getting ready. Understand. Study them. And when actually, when on the day when they have to, when they will use them, they will be super quick, super fast. It will be just a term of a flag. And this product will be just be released, um, by the year. But in terms of dev experience, again, these are different from building consumer products, right? In dev experience, we are definitely even more slower in the game because, um, again, it has actually higher priorities of our consumer data. We \[00:31:00\] need to understand what we are. given away to the public and what we should protect and not give away. So yes, um, there are differences, right? So this is why engineers, uh, actually prefer, they prefer to work for a startup a bit more than joining an enterprise because they have more freedom to try out something super, super new. **David DeRemer:** Yeah, I think the flip side of that though, and this is something that we, we wrestle as, as you know, BGV is a consulting company and what are the types of the shape of client we want to work with? I mean, on the flip side, you get to impact things at such a huge scale, right? The, like when you push a product out, the impact and influence that has, and the number of people you can reach is so fast. You might bang away your whole career at startups and. They all fail right Like, you know, a lot of them fail and, Um, maybe you don't. get to make that impact. So it's a challenge. It's a trade off I think we have to make. Um, and I think it's, um, different type of game. It's very much so. And, and I kind of like the skiing thing, I think, right? It's like, Um, if you're getting out on the bunny hill and you're falling down and you're learning and then you hit some blues and you're like kind of enjoying it, but that's totally different from being a professional skier trying to make the \[00:32:00\] Olympics is doing the moguls like crazy and running through the gates. Like that requires a dedication and a commitment. That's probably a little bit more like getting through the enterprise, right? Like You got to work hard. There's a different set of rules, different commitment you need to do to get through that. It doesn't make it any less interesting than that beginner. You just have to have a different passion, right, to get through it. Um, so, I'm curious, because we talked a lot about Flutter, I know that's an interest of yours, and AI, and it strikes me as that at least, you know, I similarly have interests in these areas, as you know, and, um, these are two very transformational technologies, I think, that we've seen over the last, um, You know, five or six years. And I'm curious, like, is that by accident that you are, are passionate in those? I mean, um, or, or how do you identify a transformational technology, right? Some of these technologies that they emerge and you're, it's just like, wow, this is like flutter became a thing. It went from small little project at Google, like, you know, super popular mobile framework, AI, same deal. It's sort of like a, one of those, uh, 20 year old, um, success overnight successes. Like it's not like AI and ML wasn't happening. Two years ago, it just suddenly became, um, really popular. Um, how do you like pick up those transformation? Like \[00:33:00\] what is a transformational technology and how do you identify them? **Teresa Wu:** Um, you don't. You just have to try it out. I studied, I studied the PHP, I studied C I studied Java. So, you don't know which one will stay. But, pick the framework is actually so, sorry. Pick the community is more important than picking the actual product. Um, I study Flutter. I actually spend a lot of time building, uh, um, learning Flutter because of the community. Actually, I have, I have a, um, I have a high hope in, in Flutter because the technology behind it, I can say it will be successful. There's something actually, uh, really, I really love about Google. Of how they are building their ecosystem of each engineer is also their client is their user. So they're treating engineers as the end user as well. So they have this very healthy community behind the Flutter. This is one of the key reasons that for me, right? Were you thinking about choosing something new? A \[00:34:00\] new framework and industry. Look at how, how the look at what the companies are behind those technology and how much sure the community is or are you able to shaping it? Is this like a very open community or very close community? Um, but it's okay. If you choose something that they goes away next year, it's a learning, learning experience and you, you learn from your own mistake. **David DeRemer:** So I pick up two key lessons there. One is, um, you can't. You can't pick the winner every time. The only way you can do that is by trying a lot of things and giving them a shot. And then the second thing I think is just an incredibly important insight. And I agree with you about the Flutter community. I think picking, uh, the community more so than the product or the tool or the framework. I think that's probably not something that a lot of people really think about as like a key decision choice, but they should, um, without a doubt. I think you're absolutely Right. The flutter community has helped it to get there without the flutter community. I don't think it would have become what it is. Um, there's just no way. I think there's some broken things about our open source world and stuff \[00:35:00\] to where you have all these people that are pushing things forward and maintaining open source frameworks and. Not getting paid for it and having a lot of responsibility and impact on a community, um, where the community grew. I mean, I think Flutter's a little bit in this spot right now where, uh, the Flutter was built on the backs of a lot of people who put a lot of energy, positivity, enthusiasm into the community. And as the rest of the world is like, Oh, yeah, I trust this now. They kind of maybe forget all the people, like some of the people you mentioned at the top of our conversation. That like we got here because of the efforts of those people. Um, and so that's something I'd love to see companies that sponsor some of these tools is how do we make sure that those people thrive and participate? Cause they're so vital. The community is so essential to getting these new tools and tech. Um, Off the ground. Um, that's fascinating. I love it. Um, so, you know, we've, we've got, we've talked about a lot here. Um, uh, this has been super, super awesome. I was just, uh, curious as we kind of wrap up, are, are, are there any sort of parting insights or advice you would give to product leaders or, or engineering leaders like yourself out there? Um, guidance, maybe you would go back five or 10 years and give yourself, you know, advice, um, just curious if there's anything that you \[00:36:00\] would want to say to the world in terms of what we talked about today, **Teresa Wu:** Right. Um, I could be wrong on this one, but it's just myself, uh, experience through my, uh, my learning and my, um, me working in this industry is, um, keep a curiosity about something new. Don't be afraid if a new technology will come here and steal your job. This is what I see this happening when we're adapting to Flutter. Replace native language, and there are engineers who are super experienced in native language. They have these worries, um, just keep learning something new, but at the same time, you don't have to be good at everything. I don't know if they contracted with, with each other. Um, you, you could be like, for example, the, the, the, say of being a T shape engineer, right? P. Be very experienced on one thing and have your board view on other things. You don't, you never know, maybe one day you will actually wanted to switch to different industry. As for myself, I studied language where I'm using today. And also people don't, people don't use them anymore. I did a \[00:37:00\] framework that I don't, I don't use today, but it doesn't matter. I also have a key interest in release. Um, I actually, once I feel interest in Google Cloud platform and in the whole dev, DevOps experience. So I give myself two months to learn Google technology, Cloud technology. And then in the end, I just applied to, to take the certificate course I passed. **David DeRemer:** Awesome. I love it. So the advice is just keep learning, right? Just keep at  **Teresa Wu:** Keep learning and don't give yourself the pressure of you have to be the expert of this new field you are learning. Just be curious about something new and that's it. Don't feel the pressure of I have to be an expert. Also, when actually, um, as today I'm talking a bit more on AI and how we use AI in Flutter in front of the project. I always say in the beginning of my talk, I'm not an expert. I'm just sharing my knowledge as I've \[00:38:00\] been learning so far, but I'm not an expert. And it's okay. **David DeRemer:** I love that. I'm not expert, but it's Okay. Um, got us, you got to start somewhere. You got to keep learning. Well, Teresa, um, this has been fantastic. I really appreciate you taking some time out of your day and your busy career and life and, uh, pursuit of, of passions and interests, um, uh, to take this time to talk with us. And, um, so really appreciate it. Um, and I guess with that, we'll say goodbye. Bye. **Teresa Wu:** Okay. Yeah. Thank you for inviting me and I really appreciate this opportunity to share my, uh, my own experience with everyone else. And, uh, yeah, thank you. And thank you for everyone who is listening to this podcast. **David DeRemer:** I loved it. So hopefully we can do it again sometime soon.  **Teresa Wu:** Thank you. ### Sam Moore, Betterment - Leading Effective Mobile Source: https://verygood.ventures/podcasts/sam-moore-leading-effective-mobile/ With his deep expertise in infrastructure, software architecture, and scaling engineering teams, Sam Moore, VP of Architecture at Betterment, shares a behind-the-scenes look at Betterment’s journey to innovation and growth in fintech. Get comfortable and listen to Sam’s insights on fostering innovation by embracing an iterative approach instead of big-bang rewrites, building a tech investment lifecycle through four key phases, adopting Flutter to accelerate Betterment’s mobile development, and sharing leadership lessons on balancing fatherhood with effective team management. {% audio_embed url="https://player.cohostpodcasting.com/598333db-b5b0-4ea6-ba7d-cd5e1d5c8170/b8e695b0-29db-4cd7-b8b7-7f8a39a3aa56" /%} ## Transcript **David DeRemer:** \[00:00:00\] Alright. Hi, Sam. How's it going? Thanks for joining you today. **Sam Moore:** Hi, David. I'm doing great. How are you? **David DeRemer:** I'm doing terrific. Uh, yeah. Thanks again for, for coming on the show. And, um, I wanted to start with just a general, I, I know you've a recent dad, moved to the suburbs. Um, just curious how that's been going. Uh, how, how's it being a dad and how's it changed things for you? **Sam Moore:** Oh, yeah. Yeah. Uh, it's, uh, it's been great. I mean, it's, it's fun. It's, uh, it's challenging. It puts things in perspective. Um, I, I think, Making it through the early stages of being a parent has definitely put, uh, put like what difficulty is into perspective. Um, which is actually, you know, it's kind of great because we face a lot of challenges in the work that we do and it's, it's, it's easy to get lost and like, Oh my God, how am I ever going to figure this out? And, you know, when you face those challenges with like taking care of a new loved one, it's, uh, it's, uh, it's a totally different ball game. So it's fun though. Love it. **David DeRemer:** Totally agree. I think it definitely, uh, impacts how you are a \[00:01:00\] manager and how you show up at work. Not only cause you're tired, but,  **Sam Moore:** never been more tired in my life. **David DeRemer:** Yeah, yeah, absolutely. Um, has it changed, uh, how you've been a manager, like as a leader? **Sam Moore:** Yeah, I would say it has. I mean, uh, I think You know, once my brain started working again after not getting sleep for, for a long time, as is often people's experience, you know, my, my background processes and, and started, started crunching the information I was collecting and immediately seeing the relationship between, like, just like the psychology of, of children, infants, uh, toddlers, uh, and, and, you know, full grown, uh, you know, adult humans, uh, and just, like, It's so hard not to see the similarities in the work that I'm doing. I think a lot, I've always, I've always had a strong Affinity for kind of coaching and like asking questions and like trying to be a sounding board for people. Um, and I've always appreciated that kind of interaction with \[00:02:00\] other people and it ends up, you know, my, my daughter's two and a half years old now, so she's firmly in the toddler stage and, uh, there is just so much overlap there, right? It's like, you can't just tell them what to do. You have to like, get them to think about what's going on. And kind of guide them through things by asking questions and leading them. There's, you know, things about learning about setting boundaries and like what boundaries actually are. And they're like not things that you tell people not to do, but rather like won't let them do. Um, and so yeah, there's, there's a lot of overlap. There's definitely a lot of overlap to how it, how it comes into the workplace. **David DeRemer:** Totally. In my, in my experience as a parent, it's been one of the cool things is to, um, learn things again, right. To like, um, see, uh, somebody learn something new that I think as we grow, right, our, our. Our minds get these layers of learning and we just kind of jump ahead to all these conclusions. And, um, it's just so fun to watch like a kid growing and learning to like learn something brand new that like literally their brain has never learned before. And \[00:03:00\] then you see it like when you do something cool and new and different, like at work or as an adult, and you're like, Oh, beneath all these layers I built over my life, there's still that like very raw, like, Just learn something new and that like what enthusiasm you get from that is super cool. I think just kids like really distill some of those basic like human mentality, uh, aspects. It's pretty cool. **Sam Moore:** Yeah. Yeah. It definitely just sort of like boils things down to their essence, right? You just see things for like what they are from, from first principles, right? Like as a human learning a thing. Um, it's, it's pretty fascinating. Um, it also, uh, as you know, having, having kids, like, uh, just having a reminder of, You know, how much patience it takes to, to sort of collaborate with somebody, or cooperate, or get them to cooperate, uhm, and, uh, how, having, being a parent, it's like a good reminder of, uh, you know, how to, how to maintain that, and how to know, like, when you're at the extent of your, of your patience, and, and \[00:04:00\] processing your own feelings. Right. I think that's, that's like a reminder that's, that's actually been really rewarding for me is like, it's been easier to think about like, okay, I'm feeling frustrated in work right now. Right. Or I'm feeling really excited about this thing. Um, because there are things I'm trying to teach my kid. Right. And I'm trying to be like, oh yeah, that's what that feeling is. Okay. Let me help myself process that. Um, which is, it's fascinating just like being a, uh, I don't know, a stronger human on like the, on the, you know, emotional and cognitive **David DeRemer:** yeah, that's really true. Like we spent so much time teaching our kids to like be in touch with their feelings, but then as adults, it's like, no, no, no, no. Ignore those, ignore  **Sam Moore:** Right, exactly, exactly. **David DeRemer:** Yeah. Well, I mean, we've established that you're a parent and it sounds like a pretty good one. Um, so why don't we take a step back and do you want to just introduce yourself and tell us a little bit about, um, who you are and your, your experience and, uh, kind of kick us off maybe why we're here today. **Sam Moore:** Yeah, yeah. So I'm Sam Moore, VP of Architecture at Betterment. I've worked at Betterment for a little over 11 years at this point. I work \[00:05:00\] as an engineer. I, I spent most of my career and most of my time at Betterment as an individual contributor, working on Primarily sort of full stack, sort of front end to back end to the database, um, uh, product functionality. Uh, I've worked sort of in the background on our trading systems, uh, our, our, uh, our, you know, front end, uh, infrastructure, our, uh, AWS cloud infrastructure, um, and, you know, the way that I came to meet you, uh, was a, was a jaunt that I did into working on our mobile, uh, platform. But before Betterment, I worked a few different places doing similar work in engineering, web development, I kind of came up into software through the, the, you know, web development age. And so I lived through a lot of the bad old days of web development, saw the early emergence of mobile development, and have, you know, tried to \[00:06:00\] find interesting ways to bring that experience and that knowledge to bear on the problems that we're trying to solve in our business. **David DeRemer:** That's awesome. And sorry if you saw that weird facial expression. It's the first time I've ever had to like sneeze, uh, in the middle of the podcast. And I was like, ah, and then it didn't even happen. It was very weird, but at any  **Sam Moore:** was thinking about that before. I was like, I need to figure out how to **David DeRemer:** know. Like a dump button, you know? Yeah. Um, well that's what I was doing. It didn't work. And then like, I was so busy messing with the mic mute that the sneeze didn't **Sam Moore:** You scared the sneeze away. You're like, get out of  **David DeRemer:** Yeah. My brain muted it automatically. Um, yeah, no. And, and you're right. Like, I think, um, you mentioned that how we got to know each other was the, the mobile project and replatforming to Flutter and. Um, that was a big undertaking and also quite early in that. And I think you, you specifically and leading this for Betterment, Betterment, of course, uh, as part of it was kind of pioneers, I think, of, of Flutter in the ecosystem. Uh, you guys got into it really, really early and embarked on a very large undertaking, I think. Um, so I was just curious if maybe like, you know, \[00:07:00\] tell us some of that, like, how did that happen and, you know, what have we learned? And I think that's going to be the bulk of our conversation today is like, How do we, how do we effectively transform technology? But to take us back, do you want to kind of over give it over you maybe of Betterment's technology stack or like what kind of led you to that moment? Hmm. **Sam Moore:** Like I'll say a quick thing about Betterment. So Betterment is the largest independent digital financial advisor. Um, we operate in the United States, uh, exclusively. Um, Our product is sort of an advice, an advice and investing platform that's offered in three different flavors. Uh, we do like a direct to consumer thing, that's kind of what OG, original Betterment was, sort of on Betterment. com, uh, and our mobile app. Uh, the second piece of it is, uh, What's called Betterment at Work. It's a product that we sell to employers to provide benefits to their employees. So it's like a 401k and other sort of suite of benefits that they can use through our same sort of great mobile experience \[00:08:00\] that the consumer product delivers. And the last is this like advisor platform that we offer to financial advisors to be able to manage their clients using our software. And it's like a full custodial platform for them. So, you know, that's, that's kind of how Betterment exists in the world. It's a, like a, you know, U. S. centered, uh, financial services company. Uh, and as I said, we have, uh, a web platform and a mobile platform and multiple different web presences. Uh, so that has informed, you know, a lot of our technology strategy, how we pick technology, how we. You know, scale our team, uh, and we've always put a heavy focus on, uh, I guess I'd call it like consensus, but also like alignment and trying to sort of, uh, leveraged shared technology in all the things that we build, uh, to the extent that it makes sense. So having like fewer different ways to do the same thing is like a, is a value or a principle that \[00:09:00\] we have. Um, and so, you know, when I started at Betterment, our technology stack was, well, we didn't have a mobile app at the time, and our technology stack was Java, uh, written mostly by our CEO, who wasn't an engineer, uh, and, uh, a bunch of JavaScript for our web experience. Uh, and, and, you know, in the time that I've been here, we've then evolved that and grown, grown the team from, you know, five engineers to 150 engineers. And that's sort of necessitated, uh, taking new approaches to the different technology and has really demanded that we figure out how to adopt technology that works. Well for us and the needs of our company as those needs have changed. One of the biggest ones that we undertook was actually transitioning from our monolithic java backend system to a Ruby on Rails set of applications. Which is like, sometimes we joke about it's like the the anti Twitter like Twitter's infamously moved from Ruby on Rails to like Java and Scala. And we kind of like, did the reverse. \[00:10:00\] Um, we might actually be moving in the other direction again now, but, you know, years ago the priorities were very much clear that, like, we need to be able to build stuff and throw it away, right? And, like, doing that with, like, rigid strategies and, like, kind of crusty old techniques wasn't working well for us. So we, uh, You know, pioneered an adoption strategy there. We also got rid of our cruddy JavaScript strategy and, you know, eventually started embracing a new one. So we have this, like, uh, in our DNA, right? Like, finding the way to adopt technology that's sort of, like, the minimum amount of, of technology to, to be able to deliver great products for our customers. Um, and transitioning from one technology strategy to another, **David DeRemer:** And I think crucially for you guys specifically, I mean, I think what you're just describing there in that counterpoint to Java to Ruby or back or whatever, you know, different things. There's no one way different companies have different, uh, uh, benefits, requirements, things that they want to do. And I think that's just an important part of it. And I think that \[00:11:00\] as you and I've talked and learned about your approach, it's not just about which technology is better. It's really like for our culture, for our team, for our unique objectives, not like what I read on Reddit as the top post of the thing I should be using. Um, it's really thoughtful, mindful approach to what works for you and your team. **Sam Moore:** Yeah, absolutely. Like decisions are made in context and like, uh, we, we can't pretend like Facebook, Meta, uh, Google, Amazon. VGV, Betterment, like we're all the same context, right? Like we're not, we, we have different needs, right? Like I was at a conference recently talking to somebody and they were like, how do I get my team to work on more platform investments or something? I was like, I can't answer that question abstractly, right? Like what is your business? What stage is your business at? What's your current technology strategy? Like, how does it work, right? Those are all the things that factor in. Cause yeah, the right answer for one person or one group of people is not going to be the same for everyone else. **David DeRemer:** Totally. **Sam Moore:** That doesn't mean we can't learn from each \[00:12:00\] other. Uh, it just means that like, learning isn't about just reapplying the same strategy, right? It's about like, well, what, why did it work and why didn't it work? **David DeRemer:** right, right. And, and part of it is evolving. You know, I think most of the product stories that you read on the internet or that people celebrate, they tend to be like some bright, new, shiny company or thing that was built. But the real situation for most engineering teams out there, especially if a company has been around for a reasonable amount of time, is you're maintaining a lot of stuff that have been built over time, and you're trying to pivot your technology strategy and update things as you go. And I think that that's a lot of the stories that you and I have talked about over the years are really interesting, right? Um, so many companies have these like impediments to their organization. And technology gets in the way. Technology debt is a very common phrase for that. Um, and rebuilding or adjusting or adapting is really, really hard. It's really hard to get like the executive buy in, the motivation. Um, and one of the things that I think has just been really cool about the Betterment story \[00:13:00\] and the things that you've told me about Is that you guys have been pretty successful, I would say, at pulling off these migrations, like moving from one thing to another and just this really mindful approach. And the one that I know best is the migration from two native apps, iOS and Android to a Flutter app and all of the complexity around that. And how do you do that without pausing your business for a year or two while you're doing that right. . Um, so how, how did you do that? You know, getting into that, like, like maybe, maybe double clicking on that one specifically and then I know we can expand that to some o of the other projects you guys have been working on. You know, where was Betterment at the time? What problems were you facing and what led you to sort of embark on that path and feel like we have to do something to change something for our team. **Sam Moore:** Yeah. Uh, I love the setup. I mean, I think it's, it's really, uh, the context for us is like, we, it was like late 2019 and we were thinking about 2020 strategy. Uh, if only we knew there was a pandemic coming. That's like a fun thing that we could also talk \[00:14:00\] about what we did when that happened. Because it was also right when we were really getting into it with you guys. **David DeRemer:** Right. Right. **Sam Moore:** But it was late 2019 and we were thinking about 2020 strategy and we really wanted to figure out how to make like mobile first feature development and product delivery like our focus for our consumer product line. And the reason for that was that. We, uh, to that, to that point, we had mobile apps, native Android and iOS apps. They were 20, let's say 2020, like, I think we built them in 2013 originally, so they were like seven year old codebases, um, which, you know, not that that means that they're bad, but they had seen a lot of, uh, they'd seen a lot of years and had a bunch of road miles on them. Um, they weren't the best, in the best state, um, and even so, the, the applications themselves on mobile weren't doing as much as we wanted them to do. They were, we kind of categorized them as like a companion app. Like, we never really wanted it to be that way, but that was really what \[00:15:00\] materialized in terms of like how much we ended up investing in them. They could do the basics of what our product wanted to do. They could sign people up and, and you know, they could do what they needed to do for the most part. But every time we built new, really cool, groundbreaking features in our space, that was like mostly on web. Right. Um, and, uh, a lot of that. Kind of, it was, it was almost like a self fulfilling, like we weren't investing in it as much, uh, on mobile, uh, and then when we wanted to, it was like, well, You're going to need to invest a lot to get a little because the codebases had kind of developed these warts, right? We kind of had, somebody introduced me on the team to this concept they'd coined for it or heard called lava layers. Where like, you kind of have this eruption of an interesting idea that you kind of like start to push through the codebase to how to do things differently and presumably better in the way that you build your features. And like, but you don't quite get there. And so it just kind of like, pardons on top \[00:16:00\] of the other ways that you were doing things previously. And you have like several of these eruptions over time and now your codebase is this like smattering of layers of different strategies of doing things that like no consistency exists really between building features and it makes everything go slower. So anyway, that's more of where we were and where we wanted to be, right? Uh, and so we looked at the code bases and we kind of asked ourselves, like, how will we move as fast as our business wants us to move? Uh, Next year, uh, if we don't do something drastic. And so, like, naturally we have a bunch, uh, uh, a decent, you know, sized team of, of mobile people doing native Android and native iOS, uh, and they all have super strong opinions about, like, how they would do it differently now if they could start over. And then we started to grapple with, like, well, big bang rewrites are bad. Um, There's a lot of, like, business cases and technical cases for where they usually don't pan out. Um, And usually the better \[00:17:00\] strategy that, that, that we've also learned internally from our transformations has been one of, like, an iterative, sort of, like, wrap the thing that you've got in something new. And, like, gradually transition your functionality so that you can, like, new stuff is, you know, is new, and old stuff is old, and like, you can, you can find the transitionary path there. Uh, and, uh, that then had us say, like, well, what about cross platform? Like, that's a thing that's growing. Like, React Native had been through, like, many hype cycles at the time, uh, and, and was sort of on an upswing. Flutter had, had been emerging as, like, This isn't like a toy idea. This is like a real thing that people are using to do stuff. BMW had started using it. There were tons of international companies that were using it extensively to build like their entire mobile strategy. So, in wanting to meet customers where they're at and recognizing our technology was, was, uh, Behind where we wanted it to be. We opened the door and said like, okay, what \[00:18:00\] are the cross platform options? If we're going to do an iterative rebuild, like, can we do an iterative rebuild that doesn't require us to build two new things? Can we build one new thing? Uh, and you know, that's, that's what opened the door. We looked at Kotlin multi platform. We looked at React Native. Um, we looked at Flutter, uh, and. We decided we needed, we wanted to do one of them. Uh, and we ended up picking Flutter for a lot of reasons. Uh, we had kind of put together some evaluation criteria, Um, really early, rough, like kind of exploratory stuff. Uh, and, and one of the key things that made Flutter stand out was that it, had the ability, I think it was like, other than React Native, a Kotlin multi platform didn't do this at the time. It had the ability to be like an integrated thing. So you were going to build not just like the business logic of your user experience and, uh, you know, the core interesting stuff, but also the UI. And that was all going to be done like once for both or multiple platforms. Whereas like Kotlin multi platform, it was like, hey, share, \[00:19:00\] Share the business logic, but like build the UIs yourselves, which isn't, isn't a bad strategy for some. It was just not a good fit for us. And, you know, we had tried React Native a few years before and kind of had a bad taste in our mouths. And so we were apprehensive about going in that direction again. And then we started using Flutter a little bit more and like the Bye. Bye. IDE experience was really good. It worked with like tools people are already familiar with. Um, it was easy to install and get started. It's like hard to under value how important those things are when you're making like a monumental decision to just like change your engines out of your plane while you're flying. Uh, like the, the foundational bits need to be really good and they, they were with Flutter. So that's a lot of our stuff. And I, you know, I think, uh, we'll talk about more of it in detail. **David DeRemer:** yeah. I remember early on, um, you guys were just very methodical around evaluating the things that would matter to your team. You know, and I think a \[00:20:00\] lot of these new technologies, there's a lot of hype around them and there's plenty of people that will like, tell you they're awesome. Oh, you're going to love it. And then there's plenty of people that are like, no serious engineering team would ever use that, you know? Um, and so that sort of like discussion that's out there in the world is like, you, you have to really evaluate it yourself and really define the criteria, um, and the process for how you're going to go about doing that. And I just thought that. I remember observing that in real time as, as you guys were thinking it through. It wasn't like, this is like, Nope, we're gonna do this and we're gonna go to trial. I was like, what is a sequential process we're gonna go through to really clarify what we wanna learn and validate that? Like, is that hype true? Like, okay, like, like, um, hot re hot reload for flutter. Like, is that actually really a big, like productivity boost or is it just something that's cool that everyone talks about, you know? Yeah. So **Sam Moore:** Because if I was going to sell it, I would definitely talk about that. Uh, but if I'm using it, is that a thing that actually makes a difference to me? And like, yeah, in reality, that was the, those were the \[00:21:00\] things that stood out to us. It's like, wow, that works really well. That does make a big difference in our development experience. **David DeRemer:** In terms of, like, I know it was a long time ago now, it's kind of wild to think about, right? Um, you're more than halfway from how old that app was when you decided to redo the native ones. Um, were there some, just, you know, kind of getting back to that moment, were there some things you saw in that codebase, uh, that were, like, really clear signs to you? Or, like, what were the symptoms for you, besides the sort of strategy of becoming more mobile first? That like something was just like, not something was wrong that merits this sort of investment. Right. Cause I think you guys saw that. You're like, okay, we need to be more mobile first. We're lagging behind on features, but, you know, doing this is not a, it's a non trivial investment and it requires a lot of alignment where there are like some really obvious, like things that were just like failing the smell test that you guys were like, Oh, this is just not going to be scalable long run. Like, how do you make that call? Hmm. **Sam Moore:** well, it's really interesting. I wasn't \[00:22:00\] working on mobile at the time when I was asked to sort of get involved in this. I think my role was like principal engineer. I was maybe like one of two principal engineers at the company and having specialized a lot in like front end and full stack development. I had a lot of familiarity with our mobile stuff, but hadn't really worked in it. And. So when I was getting involved in, in kind of the, the, from the business and technology angle at like wanting to do more mobile stuff, I needed to close my knowledge gaps as best I could. And so I went and interviewed like the, there were like 20 engineers on our team doing mobile stuff at the time. I think I went in like one on one for like 45 minutes with every single one of them and just kind of talked about like, Hey, we want to do this. Like, here's what the business wants to do. Like, How? Like, what do you think? Can we do that? Like, what's gonna slow us down? Like, what do we need to fix? Um, how long do you think that's gonna take? Right? And like, just asking those kind of questions and like, engaging people in that process of recognizing that like, \[00:23:00\] People outside of just, like, the grunts on the ground doing the work recognize that, like, things aren't the way that they need to be for people to be successful, um, and we will need to fix some of that, um, and, like, that, those conversations produced, like, so much information, right? It was just, like, going to the people and being, like, what would you fix if you could do it, right? What would you not do the same way? And they were just, like Laundry lists of things. Um, that's where I got, I learned about that concept of lava layers and that, that kind of impression of the code base. Uh, so things that stood out, like on Android, we were using like the MV, uh, VM pattern, the MVP pattern, uh, two different versions of a different MVP pattern and, you know, those like architecture strategies and those are like, pretty clear signs that like the application is not holistically designed, right? And like, it doesn't have to be. You could be a successful company with a successful product that's like a mess on the inside of the software. Um, \[00:24:00\] but like, you certainly don't want to be that way. Uh, I mean, you want to be successful, uh, but, but you don't want your code base to be in that state because it, it does slow you down. It keeps you from, from onboarding new people because they don't know which way to do a thing. They gotta ask somebody, right? So there are a lot of those things. There was a lot of, like, tribal knowledge where, like, you would have to ask somebody to say, like, I'm building a new feature. Which of these four different strategies should I be using to do that? So, you know, things like that that really immediately are, like, red flags to me with when I think about architecture and, like, an architecture of software is, like, ability to keep you at pace, right? Um, They don't always have to be like bending curves as much as that just needs to be like this architecture is like ready to evolve. It has space for new functionality to be introduced. It is like tolerant to change that needs to be made to the, to the code base, to the product. Um, and there were just a lot of those flags that were, that were not, uh, not in a good \[00:25:00\] spot. **David DeRemer:** Yeah, it almost sounds like, um, there's more efforts to figure out how to architect and implement a feature than to actually do it, than like, what does the feature do, you know? **Sam Moore:** exactly. Yeah. And, you know, you contrast that to, to good things that you have experience with and it's like very obvious, right. But what we're saying. **David DeRemer:** No, I was just gonna say, I think someone on the team told me that like one of the apps had a local database, one of them didn't, something like that. You were doing like, so  **Sam Moore:** yeah, Android had a local database. iOS didn't. Yeah. **David DeRemer:** Yeah, and any new thing, you gotta like, not only figure out how does the feature work and define it, but you then have to like, put it through its paces of understanding like, okay, how do we actually like, make it work for this one? And, you know, mobile in particular, I think why there's always been this push for cross or multi platform solutions is because, you know, You know, you're trying to get the same product into your user's pocket. It's just that there's two of them. There's Android and there's iOS. And you gotta be on both, you know? Thank God Windows Phone failed, because imagine if we had three, you know? **Sam Moore:** Yeah. Yeah. Yeah, totally. Uh, Windows Phone and, and Palm \[00:26:00\] WebOS and all the other ones that have floated out there for, for the years. But yeah, it's, it's been. It's been great. It's been wild to see those things come and go and see what's stuck around. And yeah, then having to build and support them, it's tough because usually it's not the same people either, right? People specialize across the industry. There's, you know, there are plenty of people who like can hack it on multiple mobile development platforms, but certainly most people don't make their bread and butter on both platforms. They work on one. Um, and they, they have, you know, those platforms, even when they're done well, don't, always agree on the way to build things architecturally. They, the platforms don't even agree in their, their concepts or semantics on exactly how things should work, which makes cross platform things challenging too sometimes. Uh, but certainly like when you're doing native, you got two lead engineers on, on the project, right? Because you got to figure out how you're going to build it on Android and then you can build it on iOS and they got to talk to each other and as. Divergent as those architectures are, um, the strategies look different. The \[00:27:00\] asks look different. Like, oh, well, we want this A. P. I. We want that A. P. I. It's like, well, it starts to multiply the amount of effort. Um, however, you need like extra specialized people. Like you need to hire people who know how to do both, which is even harder. **David DeRemer:** And it's not just limited to the engineering team. It's the product management team, the marketing team. Cause things are different. The QA team, the build trains, like everything that like all that complexity scales, like I've often thought about there's this, um, graphic, right. Of like the complexity of communication chains. Like if you have two nodes, it's one line. If you have three, it's a triangle. If you had four, you know, all of a sudden it's a box with an X in the middle and like when it's just those lines just get out of control. And I often think about that as like. Team as you add more people to your team. But then what happens when every single one of those dots on that thing is actually a, a team of people as well. Right. And now you've got like 64 teams. Hey, um, it's just, uh, it gets real. So I, I have often thought like part of this is just reduction of complexity of people collaborating \[00:28:00\] is a huge benefit, you know, of, of all  **Sam Moore:** Yeah, And speak in the same language, right? It's the same I was saying earlier, right? Like we think about how do we have fewer technology stacks to build our stuff, right? Uh, if we had Java and Scala and NET and Ruby and Python, like we got to be good at all those things. And then those people need to work with each other and they don't necessarily use the same terms, right? So the, the, the fewer of those things you can have, It's the same thing. The communication overhead goes down. Um, The more transferable people are, the more they can like move from one space to the other. The bigger business impact an individual or a team can have because they can maybe move around a little bit more through the, through the things that they're working on. **David DeRemer:** Yeah. **Sam Moore:** So there's a lot of advantages. **David DeRemer:** So you identify these opportunities and, um, just want to highlight what you said before, too, I think, which is just super brilliant that some people should take away is, is when you identify these things that are kind of smelling funny and you're heading these impediments and you're frustrated, why don't we ship faster? Or, you know, why does it take six \[00:29:00\] months to change a logo or something like that? To actually go down and like, do the, do the research to like, talk to the team, like just go down and do that to generate insight. Um, I think that's probably something that a lot of people higher up in chains probably aren't doing as much, like going down and actually talking to the people who are living it every day, cause that's where you're getting those insights, those level layer problems. And you might see the symptoms like, or the problems in the organization, but getting lower level. I mean, insight and doing that, uh, sort of user research of the team is super, super valuable. So how, how did you then, like once you sort of identified that there was this problem and maybe this opportunity, how did you get organizational buy in? Right. I mean, obviously it's, you can point to these problems, but I think sometimes they're very, um, vague, right. In scope or scale, it's hard to quantify like, okay, how much productivity are we losing every year? To teams just coordinating, right? Or, um, those sorts of impacts, but what probably you can look at very closely \[00:30:00\] is, well, we're going to need. To train the team. We're going to need to invest in this new thing. We're going to have to like hire new engineers. Like those are things that I think most businesses are much better at planning is like into the future. Like, what is this going to cost in terms of headcount time and everything else? Um, so how did you like go about getting the organizational buy in to embark on something as significant of this as **Sam Moore:** Yeah, well, it certainly helped that the business saw this sort of, this desire to be building features mobile first, like meeting our customers where they were, right? They had, I don't have the exact numbers off on the top of my head, but, um, we, we found that like the vast majority of our customers were using our mobile product for their, their majority of their interactions with us. And it was like a vastly you know, narrower experience than what you could do on web. And, you know, many things in our mobile product would sort of say like, hey, go do this on web. And that worked for people, right? I mean, like, I think \[00:31:00\] finance was always kind of this thing that people were, um, more inclined to do on a, on a computer, a full computer, you know, big screens for big problems or something like that. Uh, but, um, but because so many more people were using it on mobile, like the business had an appetite for a solution. That was going to be better than when they could, we could look backward, right? And say like, well, how long did it take us to do feature X on mobile? And like, that was a good, we, that was, that's when we said it was going really well, right? And like, that took, three months. And like, we want to do like 10 of those features next year, and there aren't 30 months in a year, so we're going to have a bad time. Uh, how can we do something differently? And like, what would it cost us? Right? And so then it becomes like a, a, a, you know, a kind of classic business case of like return on investment. Like, what's the investment? Uh, how quickly will we get the return? Um, and you know, \[00:32:00\] will, you know, how risky is this, right? Are we going to be successful or is this going to be, uh, an investment that flops or something like that? Uh, so we were able to draw on like past experiences. So I, I think I mentioned we, we had kind of a heritage of doing some transformational changes on our technology and, and kind of seeing the benefits, um, and had like some You know, rough guidance for how long various things would take. Um, and we had started to develop, uh, at the time, like, uh, a framework for thinking about these things. Um, now we kind of refer to it as like the life cycle of a tech investment. We got to come up with like a cute name for it. Um, but, you know, maybe expect that if I ever write a blog post about it. Um, but, you know, we say like the life cycle of tech investment or of a tech investment and sort of framing it up to the business, uh, And the people working on it as like, there's four phases. There's like exploration, uh, and between each phase there's like a decision, right? So there's like exploration, and then you decide, are we going to go to the next phase, or are we done? \[00:33:00\] Uh, then there's like a pilot, uh, then there's an adoption phase, and then there's like a cleanup phase. And sort of along the way, you're, you're like getting more information. Um, I have this like poorly formed expression that I, or adage that I've, I've coined and people I work with of like, well, you, you never know less than when you start your project. Like, as soon as you work on anything on a project, like whatever the first step is, you learn something new. Uh, sometimes it's like, oh. Oh, well, this project is not, not going to be successful, right? Or sometimes it's like this project is going to take longer than we thought. Sometimes it's going to take less time. Uh, it's going to have different steps, whatever it is. Um, and so that's why exploration is at the front of the, or the top of the funnel. It's kind of like, we're always looking for challenges. Uh, we're always looking for, uh, opportunities to, to solve for the, the pains of those challenges. And looking at, you know, the big challenge, like, how can we do mobile faster was out there, and cross \[00:34:00\] platform was an opportunity, and we said, how would we know if we should do that? And we went and did what I referred to before, like, looking at the technologies that were out there, seeing what their adoption looked like, talking to people in our network, and all that was in the interest of, like, is there a there there? And, uh, mitigating the risk, reducing the risk before we make a case for the business to say, like, let's do this thing. Uh, and we got to the point where we were like, look, we think, based on these companies, uh, that are using this, these network, you know, stories that we have about people's success, that, like, we should try putting, A team of people, maybe four people, and some, and you know, some consulting resources on piloting, building a feature a different way for mobile. Uh, we would like to give it this sort of time, you know, box. Uh, you know, the resources I mentioned, and like this is the thing that we want to deliver. If we can deliver it in that time and we like what we got, we'll learn something about whether we should go to the \[00:35:00\] next phase of an investment. Um, and so that sort of strategy is really often appealing. You know, to the business minded people because it gives them the opportunity to sort of think about, like, you know, funding something like, you know, like, okay, I'll put this much in. And if I like where it's headed, I can decide to make further investment or I can not, right? Like, even if we loved it, piloting Flutter, the business could still choose to say, like, that's great. Maybe we'll invest in it later. Um, and it kind of gives you this opportunity to kind of like stage and like mitigate the risk, uh, incrementally. And that was like, I would say that's sort of like the biggest trick to it, right? Is just figuring out how to make space to do the exploration and how to advocate enough for a legitimate resource pilot, um, to determine whether you should go further. **David DeRemer:** Those stages also help to minimize the, uh, perceived investment in cost, right? So, cause if you come to them and you're like, okay, there's going to be a multi year project for this many millions of dollars. It's very easy for \[00:36:00\] finance or, or anyone, an executive to be like, eh, I don't, I don't know about that right now, but if you prove it along in like bite sized chunks that you can kind of start to get a taste for it and evaluate it, uh, the sequential investments are more palatable and you can invest in that and decide and then move forward. And I think sometimes, um, I know we see this, right, companies wait until it's a major burning serious issue. And then they go through a complex process because it's so hard to get buy in and decision making where you kind of have to paint this giant picture and it's this huge multi year transformation project. Um, and those tend also not to go super great sometimes. So, **Sam Moore:** And sometimes even if you get them done, they, you don't feel great afterward, right? There's such a slog, such a journey that like, You know, you just, you just wonder what you could have spent the time on otherwise, right? Like, was it actually worth it in the end? **David DeRemer:** And if you're not validating along the way, I think one of the fears, even in like a, uh, uh, you know, especially in a Flutter migration or something like this, it's like, well, how do I know? \[00:37:00\] I'm not going to end up with a new thing that has the same problems, you know? Um, but this gives you a way to sort of validate that too. Like, are we actually like resolving some of the problems that we identified in our evaluation criteria and our exploration and validating that we're continuing to improve on those problems along the way? **Sam Moore:** Yeah. Yeah. Yeah. That's a big piece of it. Yeah. So that's a, that's a lot of it. And then, and then once you get past, once you go to a pilot, right, you need to go into that pilot with like structure, right? Um, so that you can come out of it with something to say other than like, my gut says do it. I mean, sometimes that's all you've got, but like, That's actually less often the case than people would like to pretend it is. Uhm, so. **David DeRemer:** Well, and with that pilot, I think, uh, if I recall correctly, you guys wanted to build out like a checking feature. Is that right? Was that the first pilot or was that not really the pilot? No, the pilot was **Sam Moore:** We, We had just finished building a bunch of stuff. Uh, we, so 2019 we built like a new line of business for like a checking product. Uh, \[00:38:00\] and, uh, we We wanted to do, we had built like individual account setup, so you could sign up for a checking account as a person, like one person, and we had the plan to do joint checking sign up, so multiple people could sign up for an account together, um, and that was like the big, one of the big features we wanted to build in the, the first half of, of the, the, Of 2020, um, to kind of like continue to build on the success of that product line, uh, and be able to target more, more customer use cases. Um, but in order to do that, we also like were looking at our signup experience and saying like, okay, so like we should rebuild that in order to be more presentable, correct? Modular was like kind of like a long like 20 step sign up process, um, and it needed to change. Um, and so what we decided to do a pilot in, uh, well, we decided to do a pilot with Flutter, and we decided to say like, well, we're not going to do the new stuff, but what we will build is a, modernized version that's modular of our \[00:39:00\] signup experience, and we'll build it with Flutter, and we'll use their add to app functionality, um, to integrate it into our existing app. And like, we'll give ourselves, I think we said like 10 weeks, like just less than the first quarter of the year, um, to build the pilot, uh, and get it released to our customers. Um, which was ambitious, because there was like a lot of stuff to figure out. Enough stuff to figure out, we learned just building a Flutter app. From, from scratch, but then using add to app and then also like, uh, integrating it into our app, which was like a non trivial app with like a lot of weird stuff going on. **David DeRemer:** Yeah. **Sam Moore:** Um, but yeah, that was the first app and that was a lot of the strategy. **David DeRemer:** That side, the add to app side. I mean, that's definitely, um, uh, a key feature I think of, of Flutter. And it's an interesting dynamic. Like I remember these conversations around BlueSky, Greenfield, Brownfield, like, how do you go about doing this? I think most engineers want to be like, yeah, let's start fresh, you know, brand new, and that's just not really the reality \[00:40:00\] for most businesses to be able to do that. And also like, you might make a lot of mistakes. There's a lot of things built into the existing app. That are really hard to make sure you get brand new in the new one. Um, you know, and, uh, but that ad app experience, I know that it's not necessarily the cleanest way to get an exposure to Flutter. So how did you guys manage that? Given that, like, now you're kind of introducing this like new technology into these two apps that we just established had kind of these architectural problems and some issues, like, how did you kind of like insulate the team around that and make sure that, um, people still saw the, the value, not added complexity of what we were doing. **Sam Moore:** Yeah, it was really interesting. So the pilot process involved like building evaluation criteria and like the fundamental thing we wanted to say was like, is building Flutter as good as, or better on average than building with. both or either of our existing strategies. Because like, if it's as good as, and it's, uh, and it's like, less total work that needs to be done, \[00:41:00\] and like, fewer different things that need to be thought through how to build, like, that's actually better intrinsically, right? Um, it's a better state to be in. It would be a better set of trade offs to take on. Um, so then that was like an important part that we had to establish sort of early with our team too, right? It's like, we want to keep the people here. We don't want to lose all of our engineers because they don't want to do a cross platform or like they feel like they're, this is forced on them or something like that. So we had to bring everybody into the process and that was kind of like how, what's important? Let's talk about what the criteria are that we care about that define what goodness looks like for building the software. Because it's, it's bigger than like a language or a platform, right? It's, these are qualities of the software that like exist in multiple platforms. And you know, and back in the day, you didn't have any choice and now you have some choice. So let's see if this one's worth picking. Um, so that was like sort of key for both knowing that we were building something that was good and \[00:42:00\] being able to get people to be aligned with the decision to adopt it if we wanted to go forward. Um, and then add to app was, as you kind of mentioned, like it was a very interesting challenge. We wanted to get like that really great, what does it actually feel like to do Flutter, the Flutter way experience. And so we took this approach where we basically built a Flutter experience, like Flutter create with the add to app strategy, but like in a in a separate thing and like a with like a dummy runner flutter app and just built the flutter experience like built the signup did all the software development so it was like really fast development pace like exactly what like the demos of building a brand new flutter app are and that was what our development experience was building that feature along the way so we could kind of know like this is what it's going to really feel like when we get to the to the promised land of this thing. And then as we got nearer to the end of the project, like maybe sort of the midway, we \[00:43:00\] started actually doing the work to say like, well, how will we integrate this add to app thing into our real app? And then, you know, that was where we were, we were very strategic in picking like an experience in our product that where you weren't like authenticated yet. So you don't have to like deal with like secure storage and like being logged in and all this stuff. And like proxying that through all the craziness of our, of our existing stack. Uh, and so really what we needed to do is be like, how do we get like a view controller or a presenter or something that can like launch the Flutter experience at the right time based on a button click and like make sure it all works integrated and that the right thing happens when you're done. Uh, and, and that was like all we had to do in terms of like the complexity of managing add to app in the pilot. And it was just enough to know like. Is this possible? And how hard will it be to do more? And as well as, what does it look like when you're like really just doing Flutter development? And, and you know, both of those things was like, it's not that bad on the ad app side, like it's complicated, but like it's doable. And we can \[00:44:00\] like, maybe framework away where the pain existed. And like the Flutter experience was like, top notch. It was great. It was better than our current native development experiences because we weren't able to use like the latest and greatest experiences at the time and SwiftUI wasn't even really a thing much yet and all that. So **David DeRemer:** Nice. And you mentioned the team in there a little bit, right? Um, that's a big aspect of these types of changes, especially when you're changing a stack or adding some new technology. Um, what did you do? Uh, you mentioned you kind of brought them into the story, but like, how did you bring them along for the ride? Like, how did you get them to, uh, to, uh, skill up, but also wrangle some of the feelings around identity, right? That comes along with some of this stuff. Um, because obviously you need the team on board. Like you said, you don't want to lose people because of this choice, but at the same time, their lives are probably a little painful dealing with these things. Um, so how did you balance them and bring them along?  **Sam Moore:** it's really interesting to navigate these things because you kind of need, like, \[00:45:00\] we go back to the business case part of the discussion. Like when you want to do something like this. You need to provide like enough of like a glimpse of what the future looks like and what the migration path is. Even though at the same time, you know, holding your head like this is like a hypothesis, right? It might never come to pass, but you really need to think out into the future in order to be able to talk to like the non technical, non nerd people about this, or at least the people that nerd out on maybe the finance or the business cases and not this stuff. Um, but, but it turns out that like, doing that thinking is also really great for talking to the nerds when they're nervous about what it means for them, right? Like, am I going to get, are we going to fire everybody? And it's like, well, Well, no, like we put together like an FAQ, right? For people of like, what does this mean? Like, what will, if we do the pilot, what is that going to look like? And like, what happens if the pilot is like green lit to go, you know, to adoption, right? And like, that means, like, we're going to do \[00:46:00\] training. So we actually had to factor that into, like, the pitch and, like, the, the plan before we even started a pilot. So we had, like, it also creates this tension of sort of, like, it's like fait accompli, right? It's like, it's, it's, it's, uh, destined to happen. Like, you did all this work. We're definitely going to do it. And you spend a decent amount of time reminding people that, like, This could not be what we want to do. Um, but if it is, we need to be ready. Um, and I think that kind of just like preparedness and carrying that the right way in your team and your conversations about it help a lot. Like you have to believe that like this is a pilot and we will explore it and we know where we go next, uh, at least approximately and we're going to go together and that's the journey that we're on. And like being able to talk to the team about that and like. Communicate what we're learning as we go, um, and, and reassure them that like part of this is like there's a training plan. Um, so I mentioned that before and I think you maybe referred to it as like we said like, well, in going into a pilot, let me rewind. \[00:47:00\] We said we don't know Flutter, we could figure this out on our own, um, but like that's yet another risk and another layer of stuff that we have to do. What would we do to, to mitigate that? And we connected with you at VGV to say like, Hey, you're experts in Flutter. You probably haven't really done much with add to app. You fortunately had. Just enough experience in it to be extra helpful on that front too, but we wanted to bring you all in as experts to say, like, we're also going to know that, like, for the time investment that we get in this, that we're spending it focused on our problems and not, like, Flutter problems or, like, generic problems that, somebody solves and we just have to find them on the internet or figure out for ourselves. Like, let's talk about the betterment business problems that we're trying to solve with this software. Um, so like you're going to kind of layer these things together, right? You say like, okay, we're going to do a pilot. We're going to bring some expertise in to help us to like, move fast and avoid the pitfalls that are obvious to somebody who's used this before. We're going to \[00:48:00\] put together evaluation criteria. We're going to provide updates to the team as we go of what we're learning, what we like, what we don't like. We're going to make a final decision once we get to the end. If we are able to actually deliver the thing, because there's a risk, maybe we might fail, might fully fail and just like not get it done on time. What are we going to do if we're not done? Are we going to keep going? Um, so, you know, having answers to those questions are really important. And then it's like, okay, so when we're done, we're putting it in production. We also need to be like, What do we do if we put it in production? It's fine, but we decide we don't want to keep it. Now it's like another lava layer, right? Um, so like we had to have some commitment to like, we're going to unwind it then, right? Um, and, and we'll have to go forward with a new strategy and like figure out what that investment looks like. Um, you know, so anyway, that, those are like a bunch of the pieces that fit into it. And then like somewhere along the way it was like, okay, well then once we pick it, we need to say like, when will we agree to like pause for like two weeks? Train the team, build something together, useful, uh, get everybody to learn how the fundamentals and get like some \[00:49:00\] experience. And then how will we like do adoption? Formulating like an iterative adoption plan was like a big piece of success for this project, as well as other adoptions we've done, which is, uh, which is like a huge piece of just like, you have a bunch of experts that, that worked on this thing or like piloted the technology. It's a bad move to say, okay, now everyone use it for everything new. You have to sort of train everybody. Sure. You can do that up front and say like, okay, now sit on everything you learned. There's four teams. We're going to stack them up. The first team we're going to bring the experts in and we're going to work with you to build a thing. the new way. And like, we're going to learn stuff. We're going to make improvements to the technology stack that everybody else will subsequently benefit from. And like, you know, it's not entirely different from like consulting, right? We were doing sort of like internal consulting and contract work to like help them build the thing, get the skill, get the affinity for it, and then move on to the next project. Because we didn't have the capacity to \[00:50:00\] help six people or four teams at the same time. Uh, and then, you know, that team is now living the future, and the rest of the teams keep doing it the old way. And, uh, That tension is also a fun one to observe. It's like, Oh, wait, but like, we want to do a new thing. But for some period of time, we're still going to like build new features because we can't stop building features and we're going to do it the old crusty way. Right. And like, you have to live in that duality. Like, like that's how the business continues to be successful. And, and you don't like stop all the things, uh, and, and, you know, Ultimately, often, I think if you do try to do that, you usually fail because you can't support everybody. You can't do it all at the same time. **David DeRemer:** I remember those trainings too. I remember, um, You guys explaining that or, or kind of having this vibe that it was this kind of cool cultural moment for everybody to, cause it's not that often in our careers where we as a team take like a break, do something all new, learn something new together. Maybe going back to what we were talking about kids, like, like sort of learning something new and how exciting it is to learn something new for the first time. And I remember \[00:51:00\] after that, that training, there was this like, kind of interesting, like, um, at least my recollection of it, there was a positive culture bump of people being like, Oh, cool. Like. We did this together. We had this almost team building exercise where we're learning some new technology and wrapping our heads around what our future will look like together. Um, and I thought that was just a, a really interesting strategy. Of course you have to do the training, but also to kind of like really embrace it as like, this is kind of like a good moment to rally the team around what we're doing, if we're going to go forward with this, **Sam Moore:** Yeah, yeah, exactly. It's like that. It's like, yeah, classic sort of team building stuff, but like If, you know, in your hands on and like really feel like you're producing something and we kind of found like a hack, which was like, we did like a week of like workshops and like throwaway training material content. And then a second week where we're like, now we're going to take everything we learned and we're going to everybody together that is going to build components for our design system. Cause we tried to follow the design system \[00:52:00\] and like, it was like, great. It's for people to build something real. That we're gonna use in Flutter, and they all get a taste, and then they all go back to working on whatever they were working on before. And like, eventually they'll all come back to Flutter. Uh, and, and it, yeah, it just made such a huge difference of people being like, like almost it created the demand, right? They were like, it was able to dispel people's concerns. Validate, you know, concerns that were founded and, like, figure out how we would mitigate those and, like, create a desire to, like, there's somewhere better than, like, I'm going to go back to some of the stuff that's crusty, but, like, the future is coming and it's good, actually. And that psychology is really important to consider, right? **David DeRemer:** totally. Yeah. No one wants to work on the old and crusty. You want to work on the new and hot, **Sam Moore:** yeah, yeah, like it, you just learn over time that people like, you know, they want to be upskilled, right? They want to learn new things, like there's this fear that people are curmudgeonly and just stuck in their ways and like, I always felt like that was very strongly pronounced in the \[00:53:00\] The mobile world, just like everybody was in their camps, like nativist camps, uh, uh, but then like, you know, I, I talked to people and like the reality was that people are very open minded, um, they just held strong opinions, right? And like, they had fear for change in the ways that everybody does, like change management is like a skill, uh, that, you know, Individuals and organizations, both giving and receiving and making change, all need to practice to get good at. Um, and so, yeah, you, you, it's hard to, uh, it's hard to over invest in, in that kind of stuff. **David DeRemer:** Totally. Yeah, there's so much identity trapped in that, and I think it's really dangerous, right? And I think you see the natural progression of this, where I think Flutter engineers can really just be considered as, like, product engineers, because it's like, oh, you want me to build for, you know, the web now, or Linux, or whatever else, and you have this, like, power in that technology, and I think it's even a danger to call it technology. A Flutter engineer. Um, cause also like with all the talk about A. I. and what happens when one day you just go in and you enter a prompt, you know, and it's like spits out a code or \[00:54:00\] something, right? Um, or an app. Um, I see Flutter as like a natural progression of just like how do we make it easier for engineers to accomplish their objectives, right? And not be so dogmatic about the tool, right? Um, so, um, just what about the sort of like, so fast forward, you know, and then I know there was a big process to kind of You know, continue to implement and follow through and do cleanup and stuff. But like, like as you went through that process and now you've got, you know, is the app fully flutter now at this point? Or, or where did you get to? **Sam Moore:** Yeah, so I think that's the other fun part, right, is an crucial piece of this like tech investment life cycle is that it doesn't stop once you pilot and it definitely doesn't stop once you start adoption. You need to actually get everybody adopting it, sure. That's the easy part. The hard part is the cleanup. The hard part is like, uh, you know, we have this framework, the, the, the life cycle of tech investment that, that kind of talks about the. the return on investment at each stage, right? \[00:55:00\] And we talked about mitigating risk and like, the return on the investment is, is nothing until you get to like the end of a pilot at which like maybe you did something quickly or something, but even then it's usually negative, right? You're still just investing. And then when adoption starts, you start to get some benefits, but, uh, With these sort of like, you know, uh, incremental rewrite things, you're not getting the full benefit. You know, we call it like, until when we're full flutter, like this will be better, that will be better, this other thing will be better. You get the isolated benefits, but like certain things still have a cost that's painful. You know, we still had to test things in add to app mode where everything was integrated. When we, you know, we would build new features, we, we devised like a strategy for being able to build like in a standalone app that was like a Flutter dummy app that we would like launch and log into with like a dummy login feature because we hadn't migrated our login experience yet. And so you'd like login, and it would get you an auth token. And then we'd like launch features with like a little buttons that we \[00:56:00\] built. And then you'd build all the things out, Flutter. First, and then you'd integrate them in the add to app experience and like test it and like that was painful and stinky. It was not a good experience. It worked. It was totally doable. Um, but it was like the contrast was constantly there, which was also a good reminder and incentive to like, keep going and do the cleanup work. But you really do need to prioritize the cleanup work because it's not until you get to like, you don't do it two ways anymore. You just do it one way, the new way that you fully manifest all the benefits. So cleanup is a huge part of it. It's the easiest part to give up and not, not like power through. And we've learned those lessons too of like spending years, not, not being done. Now we are full flutter with the exception of, uh, one feature or something. That's like a web view based experience or one or two features in a handful of features. Um, but just like, don't, they're not frequently used. \[00:57:00\] Uh, and so. We're getting all the benefits, right? Like even that WebView feature, like it's not foundational infrastructural pieces that are in the way anymore. It's just like, now it's like the legacy thing, but it's like nicely isolated and nobody, it doesn't, doesn't get in anybody's way. Um, and the benefits are there. Like we get to do all sorts of interesting things. Like we recently built, uh, a new fancier test runner for doing like parallel test execution in Dart. And like, we just need to run Dart tests now. We don't need to run iOS tests and Android tests and Dart tests. And so it streamlines so many things for you where, like, you don't need to support multiple platforms. You can make optimizations. You can embrace the choices that you've made. So, you know, on the impact of the engineering team, it's really great. But, uh, You know, there's, there was, that was a journey. I think it took us like, until like 2022 or something, like, you know, a full, like, almost two full years from like, Idea to being full Flutter, right? And you can accelerate that if you can choose to \[00:58:00\] invest more. Um, but, uh, You kind of continuously get more benefit as you go. **David DeRemer:** So there's all these benefits on the engineering team. Um, now that you've got that process behind you and a couple of years now of like living in the future, um, what have been the impacts on the overall like business of Betterment beyond the engineering team? **Sam Moore:** I mean, there's tons of customer win to it, right? Like the customer value is like, we got it. We, we, you know, it's like, it's hard to, uh, it's, I mean, it's, it's easy to sound like it's hyperbolic, but like, it takes less time to build stuff, right? Like we, we love that. Like we did a hackathon last year. We do like an annual hackathon. And, uh, you know, one of the teams was like a mobile parody team. And they like, they just wanted to see if they could build like a bunch of missing features into the mobile app. They're still on web, that we just haven't gotten to building on mobile yet. They built like five features in like, like a week. You know, like, like, and then, you know, they're, they're not like, the most complicated features, \[00:59:00\] but they're more, you know, it's not just like adding a button to a screen or a, uh, it's like a screen. You click a button, you open it, it goes through a flow and talks to some APIs. Like they were able to do that with like three, four people, uh, in, in a week, right? And that's like, it used to take us multiple weeks just to build one feature. Um, and so the, the, the massive improvement to, to that is, is great for. Everybody. It's great for customers. It's more stuff we can deliver. More features can be built on mobile. The quality of the user experience is higher. It's more consistency across Android and iOS. You know, there were always these, we would have to design for two platforms. And usually, like, the designer would build iOS or something because they used iOS. And then we would just be like, what, what should Android do here or something? And like, now it's mostly the same. We have a design system. It doesn't take very long to just, like, add ideas and prototypes. Which are again, all really good things for the customer. It's not really a companion app anymore. \[01:00:00\] It's not at full parity with our web experience, but that's, that's just like a matter of time. Whereas before it was like, We might never get there. Um, and that, that really speaks to it. And we do deliver features on mobile first. Uh, you know, not every one, um, but, but tons of them, right? Uh, which is, which is really cool. And then, yeah, the business has been able to target the customers where they are. Um, we have the ability to say, we want to build this mobile, uh, First, or even only mobile, like a feature just for mobile. We have like remote check deposit for our checking thing, uh, checking product. And that's like a mobile only feature. That stuff is really cool. **David DeRemer:** That's awesome. Um, so many good, good benefits, you know, and I think it's, uh, you so many focus on the buy one, get one free of flutter sometimes and sort of the cost savings, but it's all these other organizational benefits. And I think it's important. We don't lose sight of the goal of what we're trying to accomplish with these products. Like what we're not trying to do is like save money on engineering resources or time. That's \[01:01:00\] not the goal. The goal is to like deliver a better product to our customers faster. You know, like that's the goal. Um, it's just that technology is what we need. And sometimes managing our technology gets in the way of that. And sometimes it's like so much emphasis focused on how do we do it? Or the, um, barriers and obstacles to getting things done. We lose sight of like why we're doing it in the first place, you know, which is making our customers happy. **Sam Moore:** Yeah, yeah. And, and, you know, we, as a business, we also got the experience of like how to do this, right? Like, again, like this was maybe one of the, another one of the really big transformations we've done, but like now you, we, we have, we're doing more transformational stuff now. We're adopting React and like rich single page app experiences for web. Uh, we've been adopting GraphQL for our, for API services and like. I get product and design people talking to other people, but when we did Flutter, we kind of did this and that worked really well. Right? And so like, you know, there's a, there's a part of our business like \[01:02:00\] fabric where like we, we can refer back to these experiences, um, you know, for people who are around for them, as well as people who kind of, kind of came along as they were happening, uh, to, to kind of create these like, uh, for them. There's these cultural elements of like how to think about stuff, um, and, and so it's, it's sort of like an accelerant in that front too. **David DeRemer:** Yeah, it gives you confidence, right? I mean, I think confidence is such an important attribute of just an individual, but also a company, right? And so if you start getting into the situation where you're like, Eh, it's too hard. It's gonna take too long. I don't think we can actually do that. That's like. The death of, of like your progress of companies. Right. So, I mean, it sounds like not only do you have confidence now in your technology that you built, um, both Flutter and other stacks you've built and maintained, but the organization has confidence to like navigate through those changes and those transformations. Right. Which I think is probably the most, even the stronger value in the \[01:03:00\] long run, because things are going to keep changing. You're going to have to keep evolving and migrating and moving forward. So that's great that it's kind of giving you that organizational strength. **Sam Moore:** Yeah. You gotta be willing to take risks, right? Like, like, and you gotta be able to sort of see like how big a risk really is, right? one of the engineers I worked with He, he has sort of a phrase of like, he'll just say like, well, how hard could it be? Right. Um, it's, and it's just a reminder that like, you gotta take that first step. Um, you gotta, you gotta trust that like, maybe it's not as hard as it seems like it could be. And, and doing it is one of the best, you know, recipes for, uh, for being able to know that you could do another thing or a bigger thing. So, **David DeRemer:** So take us home. What are some sort of key advice or kind of, do you have summary learnings that when you talk about this and these things that you've done, the framework you've come up with, your life cycle of, of, of what was it? Technology, uh, tech investment, life cycle of a \[01:04:00\] tech investment. What are some of the key takeaways or things you guys talk about internally, um, that you could share? **Sam Moore:** Yeah, well, I mean, I think there's a whole bunch that I could rattle off. I mean, I think, like, having alignment between the technology part of the org and the business part of the org is really important, and that, like, uh, you want to focus on doing right for the customer. Right. Um, and if you, if you center the customer along the way, you kind of have this product mindset. Um, then it, it gives you this like shared space to talk about, uh, you know, well, how is, how is doing this or not doing this going to make a difference in that area? And it helps you kind of understand that like, that's, that's where your shared values are. Um, like it turns out that like the organizational parts of this are harder than the technical parts. Um, the technical parts are hard, but like, They're also, like, structured. They're, like, tangible, right? You, you, they, they hit you in the \[01:05:00\] face, whereas the organizational parts, like, They, they sneak up behind you, right, if you're not, if you're not looking out for them. So it's like really important to come in with a plan, right. You got to get your commitment from the business that you're going to make this kind of investment. You can't begin exploring things that you're not going to be able to afford to resource. And you don't want to pilot a thing that you're not going to follow through on. Like all this sort of things that are in this life cycle of a tech investment, right? Exploration, piloting, adoption and cleanup. Like you don't want to adopt a thing if you can't see a path to, to investing and cleaning it up. And they sort of just all stack together. Um, those are, those are big things for me. Um, you know, on the cleanup front, uh, Somebody I work with likes the, the, you know, everyone wants to go to the party, but nobody wants to stay and clean up, and I think that's real, like, that's a cultural thing. that exists in our, in \[01:06:00\] our world that's part of our collective psyche. Uh, and so you, you really have to like formulate this into the plan. Like we gotta do it. It's gonna happen. It's gonna be annoying, but like, you don't get to have the party if you don't clean up the, the space, right? Um, and I think the other big piece of it that's, that was a learning for us is like, the work is never done. Uh, so like, as you're going through it, like, you gotta really find a way to embrace the journey of it and recognize that like, once you Climb this hill or realize, you know, this mountain is actually just a hill and you see the next peaks like You got to go there too, right? Like for us, adopting Flutter helped shed light on the other problems that we had, the other challenges that we needed to solve, the next layer of, of, of, uh, things that were going to slow us down. You know, for us, it was like web versus mobile dichotomy of how we built software. Um, and not building APIs meant it didn't matter if we had the best front end, uh, mobile tech stack, if we couldn't get the \[01:07:00\] data or, or, you know, create data, then like we were going nowhere. Um, and so. You know, those are other learnings to just recognize these are all steps in the journey, and it's all part of some, uh, some,  **David DeRemer:** So like, clearing, cleaning up sort of the, the mobile process helps to clear the, clear the view for getting after the other problems that are deeper in the stack or other things inhibiting the growth of the, of the  **Sam Moore:** Right. Yeah.  Yeah. And from a technical perspective, like, I like to start with the customer, and I also like to start with. the thing the customer sees, which is the U. I., and just like work toward the back of the system from there. Um, because sometimes, you know, the other way is, is fraught. Both ways are fraught, but the other way is often more fraught in that, like, you go and build, like, the best A. P. I. and, like, there's no one there to consume it, right? Like, you can't rely on, like, if you build it, they will come, right? Like, the field of dreams is not how these things work, right? Like, you need to, like, create the adoption. You need to recruit the people to do the work. You need to, like, Sell them what they're actually looking for. Um, \[01:08:00\] So, it's all really important. And, and starting with the customer is, is usually the best way to, uh, to kind of center that, that right part of the narrative. And, and focus on, like, the right things first. Um, It doesn't mean that you, you have to ignore that other problems exist. Um, But certainly it helps to be like a guiding, guiding strategy for, for, you know, how to pick where to start. **David DeRemer:** Very insightful. Well, Sam, um, this has been awesome. I think your story and not only the Flutter experience, but what you guys have done as a businessman, managing all these tech transformations. It's, um, I've always been very keen to help you and collaborate with you to get these stories out. Because when I, when I get to hear about them and talk to you about the things you're doing, I'm like, Whoa, like. More people need to be aware of this because so many companies are facing these technical challenges and they blame the tools or the technology or the market or whatever. And sometimes it's like, well, no, we just need a mindful process to go through this, to continually improve and get better. And you've been a great collaborator of us, so thank you for having this conversation. I know we've worked on, there's \[01:09:00\] a webinar people can find that we've done together. Um, we've created some, some guides around migrating to Flutter in the past and collaborated on that. And, um, just really appreciate you continuing to share these thoughts. And I, I personally love to hear that sort of, that next step. That it's like this Flutter migration you did, this process you went through. Like, that process becomes repeatable and you're doing it for other things. So it's not just this like one time skill. That you like, it's like you're training to do the Olympics one time and then like it's over and you're ever going to do it again. It's like, no, actually like going through that process. Yes, you're creating an improvement to your mobile stack, but also you're creating this organizational capability and comfort and confidence to do more of it on other stacks and other parts of the business. And that's just really cool. So, um, yeah, just wanted to for coming and telling the story and hopefully we get a chance to do more of it. **Sam Moore:** Yeah, I mean, I want to thank you for, for, you know, inviting me on to, to tell the story, uh, and, and being such a, a wonderful host, uh, asking great questions and, uh, and giving me a lot to think about. So, \[01:10:00\] uh, appreciate the, uh, the opportunity. **David DeRemer:** Awesome. Well, thanks, Sam. Appreciate it very much. Talk to you,  soon. Cool. Alright, we'll just hang out here and Luke, you out there? Hey Luke. Alright, so.  **Sam Moore:** Okay. **David DeRemer:** some of those cards, like Sam you and I can follow up on this around sort of like for, cause that was about an hour ten or more. So FlutterCon thing, but. So I think that's good because we can find some things just like we can really contain it, I think, to the Flutter stuff. Um, cause that's what the dev crowd will really be into. And, but I think that was awesome. I think you have so many like snackable quotes to pull out of there and stuff. So it's going to be **Sam Moore:** That's great. That's great. Yeah. I mean, I like, I mean, you know this, but it's easy to have these conversations with you, right? You, you've got a, you've got a good, uh, uh, you know, an active mind asking really great questions and it's so easy to pull on threads and, you know, throw things out there \[01:11:00\] for you to, to, to sort of gnaw on. It's, it's, it's good. **David DeRemer:** Well, don't give me too much credit. You're the, you're the star of this. I mean, I've always felt, just for what it's worth, I mean, you're, you're, um, you should be doing more talking and stuff, I think, like, as you grow your career, because I think your insights and your ability to deliver the message, just for what it's worth, is, like, really, really on point and solid, and, uh, I've always found you to be, like, really, the way you communicate and deliver the insight is just, like, something I think people can really connect with, um, and it's genuine. I think so many people are out there giving this advice. Thanks. Thanks. And it's like, you know, uh, cause you want to be a LinkedIn influence or something, but like everything you're describing is just like lived experience, which is really  **Sam Moore:** yeah. Well, happy to do it. I appreciate the, uh, the vote of confidence. I think that's the thing that I'm, I'm aiming to spend more time doing, which was sort of why I wanted to, you know, do this with you and, and also, um, to, you know, figure out that talk and, uh, even on short notice, **David DeRemer:** yeah, well, we'll follow up on that. I think, um, my goal is if we can get like three, four or five slides, you \[01:12:00\] know, and just keep us oriented, you know, and just get us on point. Um, so even, even just those header discussions. ### Shaping the Future of Cross-Platform Development Source: https://verygood.ventures/podcasts/harnessing-innovation-to-shape-the-future-of-cross-platform-development/ In the first episode of "Build To Succeed," [Eric Seidel](https://www.linkedin.com/in/ericseidel/), Founder of [Shorebird](https://www.linkedin.com/company/shorebirddev/), joins us to discuss his career journey and share insights on building impactful products, particularly focusing on his work with Flutter and Shorebird. {% audio_embed url="https://player.cohostpodcasting.com/256c3794-0d37-4b86-b99d-b3104f08836e/16bb1c57-624b-4eb1-b3d3-d733c0d0a6eb" /%} ## Transcript **David DeRemer:** Hi Eric, how's it going? I'm uh, Really happy to have you here. I am frankly honored really, um, to have you here, you know, you and I've known each other. I was thinking about this before today, like seven years, over seven years now, you know, from back there pre, before most people knew about Flutter and stuff way back then, I think our fates have kind of been intertwined over those years. You know, obviously mine very, very much so based on your creation with Flutter. And so I just wanted to start by saying, thanks. It's a, it's a privilege to be able to get a chance to chat with you and just to know you over the last seven years. So, um, Um, really appreciate that. So one thing, um, as we were prepping for this, one thing I learned about you that I didn't know was that you've kind of been like everywhere, you know, something like 10 different States and, and, uh, lived in Austria at one point. And I was just wondering how that travel, that background maybe influenced you over the years. **Eric Seidel:** Yeah, I, my parents moved around a lot, um, and yeah, I spent a couple years in Austria and, uh, I don't know how it influenced me. I, uh, I guess I got to see, you know, a little bit of, of America at least. Um, I get to learn how different, uh, other parts of the world are from, you know, California where I live now. Um, but yeah, I, uh, it, it's, you know, it just, it's been a part of me for forever. **David DeRemer:** Well, and on the theme of everywhere, I also learned something I didn't know about you, which was, um, you literally, your name is everywhere. Um, probably far more than people think. Right. You want to tell us a little bit about that? **Eric Seidel:** Yeah, there are quirks about things like the BSD license. Um, and there was a time when I was working on WebKit, when I was not working for Apple and not working for Google, and so my name, as an individual, uh, is in every install of WebKit everywhere, and WebKit is basically on every digital device, including those that go into space these days. Um, and so, yeah, my, my name and, you know, obviously hundreds and thousands of others, uh, is, is everywhere. **David DeRemer:** That's, that's pretty awesome. Name in space and name in every smartphone everywhere. Um, and probably, uh, more and more these days in terms of apps. Right. Cause I don't know if the, you know, obviously you had a big, big, uh, big influence, uh, at Google and with Flutter and a lot of the things you worked on there. So, and you were at Google for what, over 14 years, is that right? Something like that. **Eric Seidel:** Yeah, something like that. I, uh, I had a company a long time ago, and then I, um, After that dissolved, I went to Google and I worked on Chrome, the original Chrome team. And then when we shipped Chrome, I helped fork WebKit to make Google's engine Blink, which now runs Chrome. And then we forked Blink again to make Flutter, which is what you and I, you know, have interacted with over the years. **David DeRemer:** Yeah, I wanted to go back to that, um, company you mentioned before all that, because I think, uh, you know, someone who, who just met you and you worked at Google for 14 years, you might think, oh, here's a, you know, big tech guy, but I think you're an entrepreneur at heart. Right. Cause going way back, I mean, what was it? 2006 you were in Y Combinator. Is that right? Something like that. **Eric Seidel:** I think that's right, summer 2006. **David DeRemer:** Yeah, I **Eric Seidel:** I had a **David DeRemer:** pretty early. That's wild. You know, **Eric Seidel:** Yeah, we were the second or third class, something like that. I, uh Yeah, I had a, you know, a little no name company in high school, um, you know, doing sort of consulting y stuff. And then I, I just, I long thought I wanted to be an entrepreneur and I stumbled into Y Combinator. Um, what, what ended up being early for Y Combinator's journey. And then after that failed, uh, you know, after that startup failed, as most do, um, you know, I took some time off. I hiked the Appalachian Trail as part of my continuing traveling the world, and then ended up at Google. **David DeRemer:** nice. Did you, did you learn anything from as an early, early person in the tech community there? Like, did you learn anything from that YC experience? **Eric Seidel:** It's interesting looking back to see how much I feel like both we learned from Y Combinator and how much Y Combinator learned from us. Um, it's been interesting to watch Y Combinator refine their pitch, you know, over time. Uh, and obviously, as I said, my, my company failed. Uh, and I exited. Uh, you know, sort of divorced from my co founder and it wasn't the cleanest in the world. Um, and I think that Y Combinator learned a lot from that experience, uh, of, you know, several others in our class, you know, breaking up in that same way. And, um, yeah, it, it, it's, uh, it's hard, it turns out to start things and you, uh, you got to learn a lot. **David DeRemer:** A hundred percent. And that's, that's what this is all about. You know, it's a, it is really hard. It's really hard not only to start things, but then it's hard through every single part of the phase, right? So it's hard to start it. It's hard to get something that has value to begin with. And then you don't even get me started on scaling. Right. And like getting to something big. And so I think that's another thing, like you've obviously built some products in your career. Um, you're probably best known as the creator of. Uh, of Flutter, which of course, I just want to pause here again to say, thank you, um, you know, uh, for doing that. And I think not only VGV wouldn't be here where we are today without Flutter, um, but probably a lot of other companies and a lot of other teams out there. So pretty, pretty incredible impact there. And so, you know, um, you kind of caught us up on that story a little bit before and WebKit and Chrome and then into Flutter. Um, and I was wondering if you could maybe just fill in the rest of the gaps of your experience there. Like, what did you learn along the way and maybe get us up to where you are now. **Eric Seidel:** Yeah. Okay. So, so filling in some of the gaps. So I got into this journey. Oh my goodness, probably at least 15, almost 20 years ago, as part of getting onto the web. And I think I got addicted pretty early on to the problem of helping the world stop writing everything twice. And I tried really hard with the web. I spent a decade of my life trying to make the web good enough to do that. One of the very interesting learning experiences after we had forked Blink, we felt free. So Blink was Google's fork of WebKit. We were suddenly free to fix all the bugs that Google needed fixed, or we felt some of that at least. And we worked really closely with a bunch of teams. Uh, including Gmail and search and all just like try and finally make these, uh, apps really good on the web. Uh, and we found that we just couldn't, uh, at least couldn't to the level of standards that we wanted. We actually had an effort where we took, um, the Android calculator and the Android calc uh, calendar and we tried to replicate them perfectly on the web. And you couldn't do it. You couldn't get the drawer animation exactly right. You couldn't get the, you know, the rounded corners exactly perfect. And just, uh, that taught us and that, that is why we ended up, you know, creating Flutter. Um, and so, yeah, that journey for me has just long been, I look out into the world and there's this crazy thing that all businesses. Write things twice or three times or four times and that's just silly like that just shouldn't happen We should spend more time with our families and our kids and go home earlier because we didn't have to write the the same bug twice and so that has been this journey and I See things like flutter as being inevitable not because flutter is specifically the implementation that will win, but the Idea that we should be able to write a product for our customers once and have that meet the customer wherever they choose to consume it is I think a very just basic idea And the only reason we're not there is because of the incentives created in the industry That uh pull us towards single platform development rather than you know customer development **David DeRemer:** Yeah, and it's interesting how you're positioning that too, where I think a lot of people think about it as a cost savings, right? Like, oh, if we can just write it one time, we can have cost savings. But I love that point. It's also about time. You know, it's like, get, hey, I can spend more time with my family or more, more time on a feature that matters. Like, not writing that same bug twice. It's, it's not just about sort of the. Economic capitalist side of things of like, how do we do, you know, more with less? It's also just like how do we do more with what we've got? You know, so love that. Um And tell us about what you're doing now **Eric Seidel:** Yeah, so I, uh, left Google a couple years ago, um, to start a company around Flutter. It's called Shorebird, and we're solving problems for businesses using Flutter, and the product we've developed first is CodePush for Flutter. Uh, so CodePush is this idea that all, honestly, the big apps use today, uh, that you should be able to update your app immediately in production. And this is how all the big apps operate. So the YouTubes, the TikToks, the Facebooks, etc. of the world, when they have a critical bug, they push a critical fix right now. And all users get it right now, um, instead of waiting for, you know, any update trains or, you know, people to click install, etc. And this problem had been solved at Google for some of Google's Flutter apps, but there wasn't a public solution for it. And so we developed a new novel public solution. for doing CodePush, uh, that I think works great. And we're about a year and a half into the journey now and have a whole bunch of customers. And, uh, yeah, we're mostly just trying to keep up with them. **David DeRemer:** Yeah, it's awesome. I know it's a big need and people surfaced a lot and compared a lot to other solutions out there is needing this. And I think it ties into what you're saying to about time, you know, um, being able to not be beholden to some release train or some approval process or something like that, that just why wait for two weeks to get that fixed to your users or that new feature. Uh, because you're just waiting for an Apple or, or some approver to like tell you, it's okay to release it, you know, just push it, push your fix, you know, get it out there. Um, so it's awesome. I'm really excited to see what you guys do. I know you've got a roadmap and if people are interested in, maybe we'll drop a link here and, um, you know, see what you guys are up to. Um, I also wanted to, you know, I want to really dig into like just the overall experience you've had in your career, because like we said, look, Y Combinator, I know you started your career at, at Apple. Um, you worked at Google for all this time and you're back to doing a startup now. And so one of the things I really wanted to just try to unpack here is like with all that experience and the impact and the things that you've seen and touched the impact you've had on the, you know, sending your name into space because of the things you worked on and, um, and helping to create flutter and making an impact on, on the web with web kit and other things. You've been exposed to big companies, small companies, you know, Apple, long time ago, Y's combinator, long time ago, Google, wrong timeline. What are some of the things you've learned? You know, like when you contrast your startup experience to your experience at Apple or Google, what are some of the big things that you've kind of learned along the way that you think influence how you approach product development now? **Eric Seidel:** Yeah. I think each of these, I think of sort of as separate phases. Um, and I learned different things. Like, I think one of the biggest takeaways that I learned from the Y Combinator experience was a belief in. As they put it, and I think they're correct, you know, making something people want. I think that this is a step that, uh, honestly, most teams skip over. Uh, we skipped over in my company when I was doing a Y, a Y Combinator. We built a thing that, um, nobody ever wanted because we just never talked to our customers. And, um, Yeah, so I learned a lot about building something people want from the Y combinator phase, and it wasn't even the the during, it was the after. It was the that we had failed to do that. I think I learned a lot about focus from the Apple phase. I actually worked on a few different products at Apple, but I remember one of my earliest experiences at Apple was I was an intern. That was how I came. So I came as an intern, decided I liked the place, converted in the fall. Um, But as part of the intern series, the executives would come and talk to us. And I had come late because my school let out late and I only showed up for the last executive conversation, which was Steve Jobs. And Jobs came and talked to all the interns and his whole point the entire time was that life is all about saying no. And I'm sure he's talked about this before. He said there's, there's plenty of, plenty of things. It's always easy to say yes to more things. It's all about saying no. It's all about focus. And you saw that embodied in Apple. That Apple was very good at focus. And I, again, I didn't really learn it while I was at Apple. I learned it while I was at Google. Um, Google is good at many, many, many things. I would not list focus on that list. Um, and. I think that I learned that in contrast in the same way that I learned, you know, watching the companies that were successful with Y Combinator versus ours in that they had talked to their customers relentlessly and just focused on trying to build a thing that people want. And it's funny actually feeling it this time around. So I feel like we've done a pretty good job of building something people want. And it just the experience feels completely different. The experience I see many. So one of the problems that we see companies having and many startups having, and we had before, was one of constantly going to find customers. And obviously I have a bunch of privileges and past successes that make some of that easy, but I think also some of it is that we've built a thing that people want. And so finding customers has never been our problem. Customers have been beating down our door since we started this venture, you know, 15 months ago. And our problem is all about how to deliver on the really difficult technical things that we're doing. **David DeRemer:** I think a lot of early, especially early stage companies, or, you know, if you're just want to start a company or something, you feel like you've got this entrepreneurial drive. I think some people just look, where's their business opportunity, you know, where's there a market to exploit or something, or how do I get on this gold rush, you know, of AI or whatever it is, you know, and it's sort of like ideas with some pie in the sky idea of success without a real motivation. And I think also people like think about product market fit. Right. And it's like what, like, I think some people think about that as, okay, we got to go and just keep refining until we find the thing that someone wants. But yeah, like, I think what you're saying is, isn't it easier just to start by really identifying what people actually want, you know, um, and, uh, and, and maybe start there. So I think people start with the product and try to find the fit as opposed to try to. Work through the fit and figure out what the product might be. **Eric Seidel:** I think there's so many of these platitudes that, you know, smarter people than I have come up with. Um, you know, but one of them is that it's, it's, it's about, you know, falling in love with the problem rather than the solution. And I think that, you know, in many of my past. phases, I fall in love with the solution rather than the problem. And I feel like with this, you know, with Flutter and with CodePush, it's all about falling in love with the problem. Like I want to solve this problem. It just, it offends me that the world still writes everything twice or three times or four times. And, you know, again, back to your comment about, uh, you know, it's more than just saving money. We all work on a budget. Right. And whether that budget is your years on the earth, or the time that you're going to put on this, on this, this project that you're on, or, or the money in your bank account, right? But we all work on a budget. And when you find ways to do things better and more efficiently, you can do, you can, you can stretch your budget, you can, you can go so much further. Um, and that's what I think we're trying to do.  **David DeRemer:** makes sense. That makes sense. I, and I think also like one of the challenges we might face with tools like flutter and shorebird and things where we can move faster is that I think then that creates more people still need to have that focus on their idea, like to your point of build something that people want. If the tools become easy where it's faster to iterate, faster to get something out, that doesn't mean you can just skip that step, you know, you have to make sure you're still thinking about what the product is and does it have a reason to exist just because you can make it go faster or, or put something out quicker. I think, um, you know, there's, we see this all the time, like in our work, it's sometimes there's a business who there's, it's almost driven by a business need or like, we have to have this not. Really going that next step of like, well, what do our customers actually want? Um, uh, another thing I've seen that's kind of interesting where Flutter creates this replatforming opportunity for people. And so I've had some people say, Hey, this is actually an interesting time to rethink, like, what is our business today? Not let's not build something based on the premise of the business we started 10 years ago, but you know, let's go back and like, let's rethink who is our customer. Like, what do they need? If we were going to start today, what would we do differently? And now we have this tool that allows us to move a lot faster to get there. Um, so it's really cool. Um, the focus thing is really interesting to me too. You know, um, I think so many companies are really not great at focus. Is there something that you guys or that you've, you've done or been seen successful to help a team narrow down some of that focus? Um, it's really hard, right? Especially in a big company, I would imagine to navigate the politics and figure out like, what is the thing that we should do and how do I say no to somebody if it's not the right priority? Just curious if you've developed any strategies over the years. **Eric Seidel:** I don't know that I have like scalable strategies, uh, other than, you know, through cultural immersion, but I, but I do have like patterns that I, that tricks that I play for myself. Uh, so one of them is, is I, I often question when offered a problem, is this a problem of success, right? Is this a problem that we only get. After some other unlock, right? And problems of success are good problems to have, but they're not good problems to solve before you have them. Right? So if it's a problem of success, like great, let's have that problem. Let's get there. You know, let's have the servers fall over because we have a million customers hitting them. Then we have a million customers. That's a great problem to have, right? You know, I think another trick that I use, um, was a habit that I started when I, when I worked with my, uh, I have a very close collaborator. We started Flutter together. We worked on WebKit together. Um, And, uh, you know, when, when we've worked in the past together, we're not working on this company together. But when we worked in the past together, one of the habits we've had is that we have the same conversation every day. Which is, what is the most important thing to be working on? And it's a little silly to have it every day, but I think it's actually been really interesting. important and impactful to me, because it can change. And I think just that general review of like, are we actually working on the most important thing has been important. And maybe it's mostly important in like doing new things rather than doing a thing you've been doing for 30 years or whatever. Um, but I tend to do a lot of, of new things. Um, but I think it's really easy to get distracted by the, the third most important problem or the 18th most important problem. And if we aren't working on the. First most important problem? Like, what are we doing? Like, we're, we're, we're just wasting time. **David DeRemer:** And some of those, I think part of the challenge is sometimes those first most important problems, they're hard. They're, they're unclear, right? And so I think. Um, especially in today's world where we have all these tools, like, you know, to, to manage your productivity and task lists and all these things. And a lot of people buying, it's very easy to go down to the third or fourth thing because you can see the solution kind of easier and you can like, oh, okay, I can fix that today. And I can push that code and fix that bug or whatever, and get a little dopamine hit because I accomplished something. And if you just keep doing those, it feels like you're making progress. But if you keep like that first, most important problem. Hangs out forever. Well, may maybe you've like stunted your business by five years by not like doing that for three months, you know? **Eric Seidel:** so those dopamine hits are important, and doing the little cleanup stuff is important. And those are the things that I often do as warmups. Like I, I, I do a lot of mentorship of, of, of engineers, but like, one of the things that I do when I'm coming into a new problem space or a new, uh, code base is to do the little cleanup stuff to do those dopamine hits, right? Because then you build momentum and stuff. But the, you know, going back to tricks, right? So another trick that I have is that I keep a log, sort of an accountability log. It's like a journal, effectively, of like the stuff that I do. It's my to do list slash journal, right? And so the, but the advantage of that is that I'm like constantly writing at the top of it, okay, what are the most important things to be working on? And I can see how those change over time. I can, I can watch them drop off. And like, it's okay if I go work on the dopamine hits. That's good. But like, at least I know, like, once I'm revved up, like, what is the, the most important thing? And again, and I'm reviewing it and Yeah, but to your point, right? There aren't 8,000 things to do. There are five things to do. And figuring out what those five are, and going and doing them, that's the important part. **David DeRemer:** Yeah. But it's not easy. And I think that's, I I have a quick question. Is that a, uh, is that a physical notebook? Like do you write it down on, on paper or is it a, a digital notebook? **Eric Seidel:** You could not read my handwriting, you know, with a handwriting expert. Like, I have an impossible chicken scratch, so no. Um, I actually use a, uh, a digital Word document or a Google Docs document. **David DeRemer:** Yeah. No, I was just curious like, um, I, I'm kind of interested in how people kind of keep track 'cause it's so, like these days you got. Slack, and Notion, and Notes, and, and all these like, you know, methodologies and stuff, like Pomodoro and things, and all these, and, you know, it's funny, I personally have gone back to Sticky Notes, like, if you could see my desk right now, it's just literally glittered with Sticky Notes, and that's how I kind of track stuff, but, just curious, it's kind of, kind of an interesting thing, but, I mean, those are extremely, you know, fascinating insights, and thanks for sharing those, um, I really, really love that thinking, and, um, Uh, I think you're right. A lot of people, I love that idea, especially about starting off with some of those small things. Don't get, don't get, um, too worried about the fact that maybe you haven't tackled that hard problem, as long as you are mindful, like you said, to use it as a warmup and then get you to a point where you can tackle sort of a trickier thing. I like  **Eric Seidel:** We're human, right? Like this is a thing that I think I forgot when I was in my 20s doing my, you know, first startup, right? Is that I think I forgot how human we all are. And like, you need to play the dopamine hits and you need to go take a walk and you need to like, go get exercise and take care of yourself. I think trying to just grind it out. That's what we did in, you know, in my first startup. And I just burned out after, you know, 15 months of it. And, uh, you know, working hard is important, but, uh, you also have to remember you're a human and take care of yourself and including letting yourself do the little things. **David DeRemer:** Well, that kind of dovetails into culture a bit, right? Because, um, you know, a lot of companies out there have this like grind mentality, like, Hey, where you have to just work at an absolutely insane pace. We have crazy deadlines to hit. Um, if we don't get this out, somebody is going to beat us to it. Um, but I think culture, as we think about it within a team. Is super vital. And I think I've heard, you know, you talk about this before. And I certainly see important to me and our team at VGV as well around, you know, the culture of how you approach a problem is also important, like spending time and actually being mindful and letting people have some of that space. Sometimes you get more speed, uh, in the long run by letting people be slower in the near term, you know, um, there's that phrase I like, like, Slow is smooth and smooth is fast. I don't know if you ever heard that one. It's like a, like a special ops kind of phrase. And I've always liked that where it's like sometimes being like thoughtful and methodical, you kind of work through stuff. Um, but I think that comes to culture and, um, you know, when you think about building products and what makes teams successful doing that, what, what, what kind of influences culture have in your mind and what are some hallmarks of. successful cultures  **Eric Seidel:** I think culture is very important. Um, I don't know that I could define it super well. I guess I think of culture as being sort of shared values. Um, I think about it with the word incentives much more than culture. So I'm very driven by understanding the incentives in the world around me. Um, I think this is another thing that I learned early on. So actually, uh, much of my learning is not, uh, formal learning like from books or it's, it's from books vicariously. Uh, so like my father was learning to be a manager when I was a teenager. And so he listened to. countless, you know, self help manager books in the car where we would be driving around. And so I don't, I have not read them, but I've listened to some of them when I was a teenager. And, uh, one of the things that I remember him talking about is incentives and incentives are, uh, are important because you're not always in the room. And I have come to learn, and so you want to understand, like, what's going to happen when you're not in the room, right? What, what, what incentives are, what are you encouraging people to do, discouraging people to do? Um, I've learned you're basically never in the room, and so incentives govern the whole thing, and culture, I think, is like at the, at the base level, like, what do we value, um, what do we, but anyway, so how does that impact on a team? Um, I think that the, the, The biggest impact I have seen is less from sort of the positive side of culture and more from the negative side, and that the, the, uh, gardening of culture is important. It's much more important in my view to make sure you aren't doing bad things in your culture than worry about doing great, amazing things. And some of that comes in terms of making sure that folks who, you know, aren't working out, you know, help them find their, their next place to be, uh, help make sure that like, again, you talked about the grind question, like make sure people aren't killing themselves, you know, in a way that, uh, it is going to just burn people out. Um, yeah, I don't know that I answered your question, but, but that's, that's where my thoughts run. **David DeRemer:** no, it's, it's, it's, it's there and I think one of the things you, uh, have mentioned to me in the past is the concept of like culture debt as well. Um, and I think people hear the phrase technical debt and in the engineering space, at least we kind of have some idea of what that means, like the choices we've made are going to create like issues and slowness and things we're going to have to Payback with interest later. Um, and you, you, I, I hadn't heard anyone really phrase that before, like this notion of culture debt, that some of the choices you make on your team, like, what does that do in the long run? Um, so yeah, I think maybe that's a little bit of where you're getting at here too. It's like the choices you make and some of those negatives that you can instill in a company, they can be really costly if you're not careful, if you, yeah. **Eric Seidel:** Oh, I think this is a particular with people, right? I think one of the things that I have learned through managing. relatively large teams, um, is that the cost of having someone who's not working out is not the individual person. Right? Even if you're getting 1 percent of what you should out of that person, the much bigger cost is on the team. Because it's on the changing of expectations for those around it, it's on the cost of people having to help them, it's on the cost of people wondering, oh, you know, Joe isn't, isn't doing anything. Why am I doing something? Or, oh, I gotta go clean up after Joe again. You know, it's just like, the costs are all, uh, externalized. Um, and I think that was a, that was a big learning from, from having to manage a large team. **David DeRemer:** And that's incredibly difficult to fix too. If you one day wake up as a manager and you're like, Oh, we're doing this. Like we, we have this problem around our team. Now you're kind of trapped as well too. If you don't have processes in place early on to make sure that you, uh. people to achieve if they can get some assistance or coaching or guidance or maybe just coaching out, you know, or whatever it is. Um, uh, by the time you realize it's a problem, it's, it's probably too late and even more expensive to fix. Um, and reset that culture. So I wanted to ask you, uh, another flutter question if you don't mind. Um, because I think, obviously I think it's such a, Successful framework, you know, um, uh, the man, by all accounts, I think at least the most popular multi platform framework out there these days. Um, and, uh, had a huge impact on the industry and the community. And so I think we have a tendency to talk about all the great things and all the benefits. And I was just wondering, what are some of the mistakes, you know, if you could go back and look at that journey. What were some of the things you and the team did over time that maybe if you could go back and talk to yourself five years ago or, or however many years ago and say, Hey, maybe do this a little bit different. Was there, was there anything else that comes to mind in terms of how you could have, uh, or, or how to help drive a product team, um, drive to that success? A little better. **Eric Seidel:** I think one of the mistakes that we specifically made with Flutter, was that Flutter focused too long on Greenfield. And by Greenfield, I mean new applications. Um, and I think that if you look at it today, 90 percent of the top applications using Flutter are all Brownfield. Despite here we are 10 years in, and we've known about this problem for at least five years. Uh, Flutter doesn't. make it easy, at least in terms of docs and examples, et cetera, for you to add a little bit of Flutter, sprinkle a little bit of Flutter into your otherwise large app that you wrote 10 years ago. Um, or at least we were too late to do that. Uh, or we could have been earlier about that. Um, so I, I don't know that I have a lot of, uh, regrets. I think another mistake that I made, um, was doing impeller too late. Um, so impeller is Flutter's, uh, rewrite of the graphics stack. And so. Flutter started as Chrome. We took Chrome and we, we, we initially did not plan to build Flutter. We didn't want to build Flutter. We were trying to build a fast version of the web. Imagine if you could just have like a duct type fast at the top and then like turned off 90 percent of the web but the remaining 10 percent was super fast. That was what we were trying to do. Uh, and we did that very successfully I think for a while. We made it really fast. Um, like 20 times faster on benchmarks. Um, The problem came was that eventually we had to add something. And the moment you add something, you are not the web, or at least you're three years away from being the web. And the reality is that it won't be exactly what you added, you know, once it gets through standard processor. Yeah, so we started from Chrome, right? And we stripped it all the way down. But one of the few things that we kept and shared with Chrome was the Graphics Library Skia. And Skia's great. But it's designed for long running processes that are going to render arbitrary content, which is not what mobile apps do. Mobile app processes need to start up quick, and often get shut down quick, and they don't have very much memory to run in, and they're only going to render the five circles that are in that app, or the ten buttons in that app, right? And so, Impeller was moving in the same way that we moved, so, you may not know this, but the original three versions of Flutter were all in JavaScript. We eventually rewrote, uh, we eventually rewrote Flutter in Dart when we moved to Dart. It wasn't called Flutter then, it was called  Sky,  **David DeRemer:** You could, hear the collective shrieking of every Dart Flutter engineer there for a second, right? **Eric Seidel:** Well, we moved off of JavaScript because it took 12 seconds to start an app on iOS. 12 seconds. Like obviously we couldn't ship that. And that was because there is no, or I don't know if this is still true today, but there wasn't at the time a JIT for, uh, JavaScript on iOS. Um, and even if there was a JIT, you'd still be compiling the app while you launched it, which is just silly. Anyway, so we moved to Dart, which had an AOT, an ahead of time compiled mode. And Impeller is the same idea. Impeller is moving from just in time compiled graphics code to ahead of time compiled graphics code, which is exactly what you want for a mobile app. You want to be able to precompile your 12 circles you're going to draw in your app into super fast graphics code and just include it in your app. That's it. You're done. Anyways, we should have done that two years earlier. The learning, the leadership learning, is that I was presented with a menu of options of the small, medium, and large. And I chose the medium. And one of the learnings that I've had over years is that often when you're presented with an option set of options like that, they all cost the same because the cost is not in the complexity of building the thing. It's the, it's the team change and it's the process change and the, and so we should just, should just should have started impeller two years earlier and you know, the world would have been two years further along. **David DeRemer:** point. I mean, you have the team, you have the time. And, uh, and so it's going to cost time and team no matter what. So it's prioritizing which things you're going to get to. And this tracks with a lot of the things you've been saying about sort of prior, you know, prioritizing focus, choosing the, the tier one problem, that first order problem. Right. Um, You know, and, but there's so many incentives in our lives and in our workplaces to make other choices, make that cheap choice or make that middle tier choice and leave the big thorny one. Right. And, you know, we got a product manager who wants to ship a bunch of features and, but we got this one thing and that's going to consume the entire team for a long time or something. And it's really hard to fend off against that. But having these stories, having these, this insight, right. Helps you as a leader, as a manager to, um. To tell that story and say, maybe next time around, no, no, no. We gotta, we gotta get on this faster because long run, this is going to help our, our exponential growth curve way faster than these smaller things. So, yeah, I mean, you know, in terms of success, right. In general, as, as you think about your. on in the past and now where you are today, where you're building a new company and having a lot of customers and, and, um, great team. And, um, how has like your sort of definition of success changed over the years? Um, and now as you're, uh, you know, uh, kind of, a founder again, um, but with this huge body of work and experience behind you, you know, how do you think you're, how has that shifted for you from early Eric to 2024 Eric? **Eric Seidel:** Yeah. I think early on it was all about money. It was all about, uh, you know, feeling financially secure. I grew up, uh, again, my father earned significantly more than his, his parents had because he was a doctor. And I looked at, you know, how we lived growing up in Kansas, you know, whatever. And I was like, how the hell am I going to ever afford this this, right? And so I think for a long time, success was money for me. Um, and I think I definitely went through a phase where success was, was, was time was like freedom to do family and such. And I think that was in part of my Google phase. Um, and then I think over time, as I've gotten back into building bigger and things was, success is defined by impact. How many people am I touching and reaching? You know, in my budget on this Earth, you know, in my time on this Earth, um, how much good can I leave? **David DeRemer:** And when you, when you, how do you evaluate that? Like the people that you can impact, right? Like how do you kind of, Cause I mean, I think a lot of people who are trying to figure out something to do or an entrepreneurial venture, they might be driven by things they read in social or in a book or like, Oh, you got to identify some huge addressable market. And I think the risk there is someone might end up doing something that doesn't play to their strengths or it's not something they're passionate about. You know, how do you, um, zero in on. I don't know, like where, what, what, what impact is for you? You know, what is the right impact to pursue given your experiences and your, your sort of advantages? **Eric Seidel:** Yeah. I mean, this was actually another lesson that I recently learned, um, was in leaving Google, I thought I was walking away from Flutter, because it was, it was a long process, honestly, to walk away from your baby in that way, um, as I did, um, but I um, I Was talking with a bunch of venture capitalists, and one of the lessons that I learned from them was the importance of founder market fit, you know, founder product fit, and I did a little bit of sort of dipping my toes back into the Flutter world, and it was just obvious how much leverage I had, and so I think that was a lesson in leverage, and I think this this journey has been a lesson of leverage, and so you were asking, you know, how do you choose what to work on? Well, again, how do you It needs to matter to someone. It needs to be an important problem, you know, make something that people want and that those people can be you. And then also work on something that like you have some prayer of working on, right? Like don't, you know, probably pick something that was completely outside of your experience or if you do expect to spend a year learning it. **David DeRemer:** Yeah, that's very, uh, wise advice. And I think something that you learn over there over a career like yours, and it'd be very easy for you to get pulled into a lot of different directions and to have that kind of clarity, um, founder market fit is something that's, or, or, you know, that's, that's pretty cool. That's a, an interesting way to think about it and even high leverage, right? I think. What can we do to be more successful, whether it's with our own careers or the tools we choose or the things we go after that idea of leverage, I think is something that's really important. Um, I think for instance, flutter is a high leverage tool, for example, like any engineer who picks it up, they have so much more leverage than somebody else who's just building for one of those experiences like you were talking about. And so. You know, tying into the, some of those things, um, well, this is awesome. Um, I really appreciate this. Um, uh, we can kind of wrap up here. I wanted to see like any other, any other words of wisdom or, or anything you'd kind of impart on all the founders and chief product officers and, you know, engineering managers and stuff out there, anything else that you kind of drop on them as, as key learnings. You've got a ton of them here, so we don't need any more. I think I've, uh, we've got like 20, 30 awesome things. We could have like, uh, had to done a whole, whole other episode just on like any number of the insights that we've talked about here. Um, there was one thing I wanted to ask you about if, uh, if you don't mind is we kind of, um, you, you mentioned something, um, uh, in one of our earlier conversations about, uh, this idea of a challenge network. And I wanted to just like unpack that a little bit more and kind of what is that? I mean, it's not something I'd heard of. I hear about, you know, executive coaches and getting a peer group and stuff like that, but a challenge network seems like an interesting perspective. **Eric Seidel:** So this is from my wife, from one of the books that she's reading. Um, my wife is a voracious reader and is currently reading through all the business books she can find. Um, and I don't know which one it was from. Uh, but I really, I liked the idea. Um, because I think otherwise the term, uh, you know, like peer coaching or mentor, they, they, they, they have this hierarchy. involved in them, which that's not what mentorship is about for me. I have learned so much from other people who I think of as mentors and often those mentors are even my reports, right? Like, which is, I just, I feel like there's so many people to learn from who have different life experiences than you have. Uh, and so I think I'd like the idea of a challenge network, which is just a network of people who you are seeking out, you know, challenge from. You are seeking them to to learn things from them. And I, I guess I sort of have that and I seek people who will challenge me and that I can learn from. **David DeRemer:** Well, and in this ecosystem, there's that notion of like go out and fail, right? Like go out and like, like through failure is how you learn. Um, and I think in a lot of our day to day interactions with our peers or coworkers, our boss, our direct reports, you know, people are, I think, I believe at least that humans are generally nice, you know, despite what you read on the news, I think generally like most people are good people and they want to help each other. And they also may sometimes maybe don't want to like, Say that truth telling that is maybe a little bit critical or, or challenging, right? Um, but I think it's so important because if we're saying on one hand, Hey, you got to put a product out there and see if it, if it works and, you know, fail and move on. But in our day to day interactions with our peers and our trusted advisors, if we're also not setting, like creating situations where our ideas can fail, right. Or like, you can ask me a question. I can fail to answer it because I don't know the answer yet. Um, that's the, that's kind of what stuck with me is the relationship there of like that idea of challenging with someone is like a nice way of saying, I'm going to encourage you to fail with your ideas kind of right. But in a way that's like friendly, not confrontational or something. Um, and I think that's kind of interesting as a, as a interpersonal way to learnings from failure into your day to day life without feeling like a failure all the time. So. Well, this is **Eric Seidel:** I could probably spend another hour talking about failure, because I think failure is super important. And I think it's a thing that we forget how to do as adults. If you ever watched a small child learn to walk, you know, or learn to do anything, right? They just go out and fail constantly. And they fail so many, many, many, many, many, many times. And I think we adults are trained by whatever societal forces, et cetera, to, you know, try something once and then avoid the failures that need to happen in order for us to learn. **David DeRemer:** Totally. My example of this is, uh, Super Mario Bros. The fir very first one. You you put that controller in the hands I did this with my kids when I think my my daughter was five, and the youngest one was like three, and I got the little NES Classic, and I I booted up Super Mario Bros., and what's the thing that most people do? The first time they ever play the original Nintendo Entertainment System Super Mario, they run straight into that goomba. You know? They just like run straight into it, and within like two seconds, they're dead. But you know what? My three year old? My first one did it ran right in the Goomba. Second one, pick up the controller, jumped and squashed the Goomba, you know? And so it's just like, if you don't have that failure, you're not going to learn. You're not going to learn how to squash the Goomba. Um, so you need that. Um, well, Eric, this is awesome. I really appreciate your time. I definitely agree. We could have a whole thing about just failure or about any number of topics in this conversation. So I definitely think I'm going to take some notes and go back and maybe zero in Maybe, uh, ask you to do this again sometime. Um, but, uh, can't thank you enough. And, uh, again, just can't thank you seriously from bottom of my heart, from my team, my family, everybody, you know, obviously, uh, I think you've had a huge impact on the world and certainly on, on BGV and, and on me personally with your, your work and your effort, and it's just a pleasure to know you and, uh, thanks so much for doing this. **Eric Seidel:** for having me. **David DeRemer:** All right. So we'll just hang on for a sec. Aiden, you out there. ### Solving Complex Problems at Scale in Tech Source: https://verygood.ventures/podcasts/jorge-coca-very-good-ventures-simple-solutions-for-solving-complex-problems-at-scale/ With over a decade of experience, Jorge dives into the evolution of scalable engineering practices, the balance between complexity and simplicity, and how technology should align with business goals. He also explores the role of Flutter in transforming mobile app development and the future of augmented reality and wearable tech. {% audio_embed url="https://player.cohostpodcasting.com/b7a41c68-faf2-4a80-9419-1ba6f20ed4d9/ed1243e1-df71-4cfc-9ee4-2cfe5bb8cc54" /%} ## Transcript **Jorge:** If you get that sweet spot that with a very few but critical ideas, everyone gets like that aha moment, then it's how you can get 300 people to get on board and drive value for the organization. **David:** Jorge, welcome to the podcast. great to have you here. given your leadership and, uh, at VGB and in the Flutter community and just as a good friend of mine. So, welcome. **Jorge:** Thank you, thank you for having me. **David:** Let's get us started. I know some things about you that maybe some people don't know. such as, in the background here you have some guitars. I know you're an avid musician. I've been in a band and also are a big time chef. I love making paella and other \[00:01:00\] things. And so to get us started I was asked, I was wondering, do you find any parallels between cooking and coding or, or music and engineering? **Jorge:** Yeah, actually, I think that, have to understand that, like, it's very, I mean, you can be very precise, right? And if you want to do those things right, you can understand kind of, like, the foundations, but then once you do, there's a lot of freedom to go and explore, right? So I kind of, find coding, the same thing. It's once you understand the foundations, you can all of a sudden, like, Go and explore uncharted territories and try to innovate and all those things so in a way like those activities that can be very stressful sometimes once you feel comfortable in that space actually can be very very relaxing right so you know it's something that like there's a lot of similarities there **David:** Yeah, very cool. I mean, cooking, you have recipes, music, you got your sheet music, but both of them, you can do a lot of improvisation. And same with **Jorge:** absolutely yeah yeah like that's why you \[00:02:00\] can think almost like You know, like a jazz improv session, like a hackathon, right? yeah, there's a lot of parallels over there. **David:** Very cool. So you've had a very impressive career journey, to your current position at BGB. I was wondering if you could kick us off by giving us a story of how you got here and maybe, highlight some highlights, from your career along the way. **Jorge:** Oof, how I got here. First of all, like it's almost a coincidence, right? Because I'm living in the United States and I'm Spanish. I'm from Spain. So, That's like the first thing, like how did I jump, like the ocean? I was studying in, in Spain, engineering, and like in Spain or in Europe, we have this thing called the Erasmus program. In the last year of college, like they give us like the opportunity of studying abroad and get still your degree from your home school. So I was exploring that opportunity. But actually,the person that I was dating back then, instead of like, wanting to, go to Europe, she \[00:03:00\] was like, what if we go to the United States? That was a big leap for me, not only because the education in the United States, it's quite expensive, it's because like, I'm the first person in my family going to college. It was like something totally, you know, pushing me out of my comfort zone. but actually my family was very supportive. Uh, this was out of, right after the recession, 2008. Money was short, but they were like, Hey, this is your education. This is your future. You got to fight for it. I thought that I was going to be here in the United States just for one year, building my resume, just like, Getting my degree, but turns out that I got lucky. I found, after finishing my education in engineering, like a job in an advertising agency. And that's how everything started. I was, like, very into Ruby on Rails back in the day, even though, like, actually I didn't study computer science. But anything, my background \[00:04:00\] is in telecommunications engineering. So, in theory, I learned how to put rockets, in orbit. Don't trust me to do that, by the way. I think that they would not,they would not land.but, in that journey, I took a couple of programming classes. And, fun fact, I actually failed my programming classes, too, at the first attempt. **David:** We'll cut that part out. We'll edit that out. **Jorge:** Yeah. But, um, it's because, to me, they were very abstract, those classes. But, once I started learning about user experiences, Like how to build websites and all that stuff. There was something that clicked on me. Like, all of a sudden I understand, what a link does, or like what a button does. And everything, like, everything clicked. And so I started working for this advertising agency, and that's how I got even introduced to this idea of, like, how to work for clients. How to be part of, a creative studio. And that was super interesting to me, because, you are exposed to a blank canvas. And you need either to design \[00:05:00\] like a campaign or try to solve like a problem that doesn't exist. So it wasn't very engineering focused. It was a little bit more creative, but that already started giving me hints of what being a consultant means. From there I jump into a company called SPR here in Chicago that is like a very well known like local Kind of like consulting company, and that is kind of like where like the first place where I started being a little bit more like hardcore into engineering consulting. I started working with Expedia. Before it was Expedia, actually they were Orbitz, the kind of like online travel agency. And I got super lucky because I started working with the Android team, and this is one of those in which like even though I was the consultant, I feel like I learned from them more than anything. Actually, I was able to help them, and I can say that with the past of the years, but that's the reality. They were a super talented team, very talented individuals. Many of them are now like in Meta, Google, like all of these \[00:06:00\] places, right? And that's like my first exposure to mobile development. I spent there like about a year and a half, almost two years. Then I moved to a couple of fintech projects around Chicago, you know, so like high scale apps. So that was super fun to me and that's where I started doing like, okay, engineering work at scale. But not only like at scale, many of those projects, not at Orbitz, but like many of those companies, they had rescue projects. They were like companies that they were trying to move really fast. They were scaling fast. Many of them they were about to go like public IPOs and they were in distress because sometimes like maybe Those projects they were not going as smooth as they were to actually one of them was a big Financial risk because they were reporting data that was not accurate and if they wanted to go public Everything needed to be really accurate, right? So that gave me a perspective into like how to solve a problem that is really \[00:07:00\] like Like there's a lot of pressure, but there's also a lot of ambiguity. So how do you use all your talent, all the information that you can have and all the levers that you can pull to solve that as fast as possible, but also providing a solution that it's gonna work for years long, right? I love that, but then one day I learned that BMW was in Chicago. What BMW was doing in Chicago? I didn't know at that time. I learned about it later. Turns out that When Microsoft bought Nokia, Nokia had a small group here in Chicago, and as part of that acquisition, Microsoft said, okay, we don't need this tech team in Chicago, and like one of the executives from Nokia that moved to BMW was like, Well, we're expanding our technology team. We need footprint in the United States. So we're going to expand our technology team and kind of like transfer this technology \[00:08:00\] team to V& I. They started hiding. They were already like working on an IOS program for about like. A year and a half, almost two years, but they didn't have any Android talent. They were about to put the iOS team, the iOS project on the market, but they had no Android app. So they started hiring and that's how I got introduced to BMW.so I was the first hire for Android my mission was like, we need to release in six months and you need to build a team. Wow, big shoes to fill. especially because, I started, like, hiding the team, but I also need to, part of the mission was, like, you need to upskill the team that we have already internally, so many of them, they didn't know Android, but we needed to, to deliver. And just to give you a little bit of the perspective, they were already working on iOS for, almost two years. It was about a hundred developers just in the U. S. Once they started expanding internationally, it was almost like three hundred. And our Android \[00:09:00\] team was like six, seven people. And they wanted us to deliver the same value. **David:** Yeah. Classic iOS, Android, right? **Jorge:** Kinda. But you know, we pretty much did it. Granted, like a lot of the things were already in place. Like all the backend services and APIs were there. We already knew the product definitions and we were piggyback, piggybacking on a lot of those requirements.so we were already able to make a lot of assumptions, but it is impossible to catch up. That's the reality. And what we realized after two years of development is okay, we have the privilege of knowing what works and what doesn't work from iOS, but we're letting down the Android customers. It is true that like, if you look at the numbers, There's 70 percent of BMW, Mini, and Rolls Royce drivers use iOS, and that other 3 percent they use Android. But if you look at the feature set, it's \[00:10:00\] impossible to match up your investment like it's like 80 20%, right? I was very vocal about like, hey, this is not just a matter of putting more engineers or anything like that. Like our customers are complaining, but also like it's a problem for the company. Once we realized that, this application was, like, a source of revenue for BMW, MINI, Rolls Royce, but not only even for the brands, for the car dealers, for a lot of people, in that ecosystem, right? And you're, like, missing out on 30 percent of that revenue. There's a lot of people saying Hey, I want that piece of that pipe. We even had our own internal efforts to do something custom with web frameworks and all the stuff, trying to see if we can help iOS and Android to ship at the same time. That experiment did not work that much. And that's how we start exploring what can we do.\[00:11:00\]  **David:** And  **Jorge:** to tell the story further? **David:** Yeah, let's do it. Let's get into it. Cause that's why we're here. I mean, ultimately that, that, uh, is the critical, part of the story that, that ultimately leads you to VGV and the time that we've spent together and so, yeah, I think BMW is a extremely early adopter of Flutter. And I think that's something that a lot of people don't. remember sometimes, or it's not as celebrated as much as it should be. There's a handful of firms out there, like BMW, Better, and a few of these that really took a risk on it very early. And so I think finding the people who took those risks, it's not only interesting around Flutter and like what is good about Flutter, but I think that there's some insight to be drawn from what was that situation like and how do you, Help a gigantic organization with the complexity and the scale of a company like BMW to embrace something that is Very transformative very risky at the time and we can look back now and be like well Yeah, now tons of companies use flutter. It's a very it's a very safe choice now, but at the time It was not a \[00:12:00\] safe choice. I think there's something really important to unpack there Around how do you, that's innovation at its core. it's, innovation is not following because every, all of your competitors are doing it, right? Innovation is taking that bold risk when no one else. So what was that situation like, and how did you get that through? **Jorge:** Yeah, so let me set the context. This is 2017. So, almost 8 years ago.When we talk about BMW, as a technology company, or actually, like, what I was responsible for, is not just BMW. BMW owns BMW, Mini, Rolls Royce, Mercedes Benz. But also, sometimes these, car manufacturers, they do collaborations between brands. So, for example, when I was working there, there was this collaboration with Toyota, that was the Toyota Supra. So, actually, the Toyota Supra, it's almost like a BMW C4. with a different body, right? So that was \[00:13:00\] another app that looks like a BMW app, but it's branded for Toyota. So why do I say this? It's because when we talk about the code base, it's not just one single code base. It's actually a wide label structure that serves all these brands. But then on top of all those brands, we have to introduce regional variants. North America, Japan, Russia, China, Mexico, rest of the world. The applications in those countries, they look very different. Like here in the U. S., we can use Google services and you can partner with Amazon to deliver to your trunk. But in China, there's no Google services. You have to partner with AutoNavi for navigation and you partner with Alibaba for like deliveries and all that stuff, right? So there's a lot of complexities, both in the backend and in the frontend. So when we talk about complexity, I can tell you that \[00:14:00\] with one single code base, we were actually creating, I think, 49 different applications . So let's put that in context. 49 different variants of the app with different themes, different brands, different regions in 2017, 2018. **David:** then, because you gotta hit iOS and Android. **Jorge:** There you go. Flutter in 2017 2018 wasn't even on beta.there was no null safety. There was no support for Google Maps or no maps at all. There was no state management. There was no Flutter blog. There was no RiverPod. There was no provider. Nothing like that. So, what was the challenge there? Like, we knew that the organization not only wasn't shipping features like fast enough. We knew that we were not like servicing our customers. We were not only like servicing like even these platforms enough to make the revenue that we wanted to make. So our CTO at that \[00:15:00\] point said like, hey, we have a problem that not only our customers are realizing, even us internally, we realize that this is not happening. So he appointed a few people from different disciplines, almost to start like a research and task force team with the mission of like, we need to figure out What's the next phase of digital development for BMW? And these are the constraints, or these are the goals. We need to ship iOS and Android both at the same time. We want to ship always at the same time. We want to be able to ship in every region, almost whenever we want, for every brand. And we don't want to lose that luxurious element that comes with the BMW, Mini, Rolls Royce brands. microsoft. com And you have between four to six months to determine what that \[00:16:00\] feature looks like. It was an open canvas. So within that team, like we had people from a web background. We had people in my case that was coming from the Android team, but like I also had iOS experience. And then we have folks from iOS and then some other, even from the backend teams as well. So we're like a multidisciplinary team. And we just started like. Testing out everything. We wanted to be super open minded and just, be very data driven. We started collecting and testing out, every framework. Ionic, Cordova, Funga. Even, we challenged our own native strategy and tried to figure out what is the best way to ship and deliver. And after all that research, we found our golden solution. And it wasn't flattered. **David:** Cliffhanger. **Jorge:** There you go. We actually did it. And I think it was out of fear.we found this framework \[00:17:00\] for Uber called Uber RIPs.Uber in 2016 did a huge rewrite of their application. like back in the day, like they did a big refactor, like also the UI UX experience was very different and they use these RIPs framework that is a cross architecture solution. So they still build their, applications in, Swift and, Kotlin. But the architecture across platforms looks identical. But they still have their native talent build their applications with those languages. What happens is that, we knew that, from an engineering point of view, we wanted to avoid the mistakes that we made in the past with iOS and Android. We used, heavily React, Reactive Programming, and that introduced a lot of problems. Because we knew that it was a very powerful tool, but it was very difficult for many junior engineers to use. And even for experienced engineers, not only to use it, even to teach it. It's okay, you can do a lot of \[00:18:00\] cool things, but it can be very difficult. damaging for the code base. It was hard to scale. So when I look like, okay, we want to state management that looks like simple to use, small API footprint and things like that. And like Uber Rips gave us that ability. So we started like experimenting and we're like, okay, we can build like clean code. We can do all those things. And we had a POC, it worked wonders and all the stuff. And when we were about to show that solution to our CTO, we were like, but wait a second, we're actually going to be in the same problem where we are today. That is like the business organization is going to invest more on iOS. Then Android. So it's not a problem of code. It's a problem of investment. We cannot give them, even like the minimum option to invest more on one platform than the other. And at the same time, \[00:19:00\] I think that us, out of fear, we were native developers. Part of our identity and our success was attached to that platform. I was going around the world doing talks like Android Cons and Android Summits and all the stuff. I didn't want to leave. That brand, to be honest, like I was almost famous. Like I was going to, someone asked me once to sign an autograph and I don't have a big ego, but that's pretty cool. **David:** Totally, **Jorge:** but like, we challenged that assumption and we went back to the board and we said okay, no, let's go back to the data because the reality is like with this solution, we're not going to ship faster and probably we're not going to ship better. It's just like a refactor of what we have. We can re challenge the technology mistakes that we made, but we're not gonna fix the business mistakes that we made. And that's ultimately what got us here. This was not a technology problem. \[00:20:00\] It was a business and technology combined problem. **David:** I think, if I can, that's a really insightful way to think about it, because I think when people talk about the challenges they face with their products, it's easy to look at the architecture, how they did it. And when you think about rebuilding, you know, people even like looking at a cross platform framework or some, whatever the newest, latest thing. You can look at the technology and does the technology save the day? And the truth is, the technology is just a tool. You can get the benefits that you can get. By two native codebases, if you just do it right, and if you solve the business system around it, right, the communication structures, how you plan, how you collaborate. I think it's just, generally speaking, when you move to these tools that are cross platform, it reduces a lot of the complexity just in terms of the communication pathways. But I do think people put a lot of pressure on the technology to solve the problem, when really it's the people and the business problem. So that's really insightful. Sorry, keep going. **Jorge:** Like, I know absolutely like \[00:21:00\] 100 percent and as part of VGB that sometimes We had to play that role of like how can we convince people that this technology versus the other might help them to achieve their goals. The biggest factor that we have to introduce is not like whether the technology is going to help or not. It's like the fear that individuals might have to lose that identity, right? So that is something that you have to consider. But yeah, pretty much like we went back to the whiteboard and what we realized is okay, we need to fight a stigma that exists against cross platform. Because the reality is like cross platform solutions, PhoneGap, Cordova, Ionic, they were not great. We, my, BMW was a Microsoft shop,we're known for, being like a big, Azure, company. We did many workshops with Microsoft to explore, Xamarin. And \[00:22:00\] even they were, like, the ones telling us, Xamarin is not gonna scale with you. Which I really appreciate the honesty. **David:** Mm. **Jorge:** that was probably the best thing that they did for us. Because if they hadn't told us that directly. We would be like, B& W would be overriding today a Xamarin app.and that's kind of like when we were like deciding between Flutter and React Native, we were like, actually, they're both really good contendants. What's going to give us confidence here. And that's when we started like measuring developer experience, like the capabilities of the framework. Will it go beyond mobile? Because for BMW, actually the biggest amount of screens, they're not on your phone. They're on the vehicles. so the biggest UI development is for the vehicle, not for the phone. And there was something about Flutter that just gave us, like, maybe we need to reevaluate how we were thinking about Flutter, \[00:23:00\] because all the things that we loved about Uber Eats. It's like that ability to separate your UI code into smaller components, like the structure that it gave you as a tree, the way you perform dependency injection, all the stuff is something that actually Flutter and Dart were giving you. And that's something that like with React, which has a similar approach, JavaScript, TypeScript, even though it's probably like more widespread as a community. It leads to like darker, more obscure patterns that it's probably like a little bit harder to just scale. Like we thought that Dart was very, very, very simple. Like we always say that if you know how to code congratulations, you'd know how to use Dart because it's that familiar already. So we expanded actually the research team. that's how we actually integrated Felix on our team.Felix, who we actually tried to recruit for my Android team, joined us at that \[00:24:00\] point when we were already kind of like almost set with Flutter. And there was this time where the organization was like, Hey, you guys have been working on this for six months. We need a go, no go. And we had compiled all this data that point after point said that Flutter is the right choice, but there was a lot of fear because still, wasn't on beta, there was no support for Maps and what if, this POC says that is right, but then three weeks from now, once we start scaling a little bit, let's not forget that we were talking that we're going to offer this to 300 plus developers, A. What if at that point, it doesn't work? You don't want to have your name associated to that, right? Like when you're like six, yeah, it's a controlled environment. But once you open like the gates, it's a different story. That's the only time that I've been in a meeting with someone yelled. \[00:25:00\] And I was like, thank you. **David:** hmm. **Jorge:** it was a go, no go meeting. And of course, like developers, we have a lot of opinions. And we were incapable of just kind of like, Figure it out how to break that loop and one of our senior architects hit the table with his fist He said we are Technologists and if the data says that Flutter is the right choice, we gotta try and since that day including myself, we're Flutter engineers, we're Flutter advocates, including Felix, which he didn't want to do Flutter at the beginning. He was like not really convinced that was the right choice, but that's what we needed. Like we needed a little bit like that. Let's give it a try. And actually our CTO was very supportive. He was like, Hey, you guys, let's go sprint by sprint. If after one sprint, this doesn't work, we'll figure it out. If after the second sprint, this doesn't \[00:26:00\] work, we'll figure it out. Turns out that after the first release, It worked. Second release, it worked. Scaling to 2, 3, 20 countries, China, 7 years later, BMW has never had better reviews for their mobile apps than what they do now. **David:** Yeah, I was just looking at it. on iOS alone, two hundred, about two hundred and eighty one thousand ratings, four point nine out of five. Not **Jorge:** Exactly. And, I can say with confidence that making that decision, being part of that team, is one of the highlights of my career. **David:** Well, you've highlighted a few things there that I think are important. one is you took a methodical approach to get data. Right, so to even be able to have that point where they can bang the fist on the table and say if the data supports this, we should make that call. So you guys were able to get the space to collect that data and have that data driven mindset to do it. So I think that's maybe point number one for somebody looking to do something innovative or risky, is like, how do you collect the data? So that you can make the choice based on data and not just who likes what. \[00:27:00\] A lot of technology choices, whether we like it or not, I think are made by the senior people on the team or whatever's cool and new and whatever they like right now. You know? so that's number one, data. I think the second thing is, like, firm decision making. Right? Like that, to get all in the room and have that go no go moment where you have to make a call. Yeah, otherwise it can just languish and you never take that next step. I think the other thing that I've heard from other people that we've talked to that, that you're describing there is the The space and the belief from the leadership. to let you explore and try it and have the, uh, the, the room to let it materialize and really prove its concept. I think, you know, sometimes people set really aggressive goals or deadlines or outrageous sort of objectives that are hard to know. And, and like that risk of failure, that sort of, the safety of, Hey, we're going to explore this. And if it doesn't, Go right. That's okay. That's part of it. You know, fail and learn and, and to some extent with the Uber solution you guys had, you kind of had already gone through that process and that one didn't quite work out, you know, but you had, clearly, uh, \[00:28:00\] a leadership and team structure there and culture of experimentation. And I think that's like really important that engineering leaders out there really evaluate their own culture. Like, do they have that culture of where failure is acceptable and because you want to learn from it, you know, and you have to take those risks and explore.  **Jorge:** And then I think that though, that was a journey that to me, like those six months, they transformed me. I think that to me was so transformative because it's what made me almost start growing for the first time as just thinking from, oh, it's the code what's important, and start thinking more as this is how technology relates to the growth of a business, or this is how technology impacts. like the opportunity for our business. And I think that moment for me was crucial just to connect the dots, right? Like, I think that level of maturity is something that was a game changer for me because leadership was talking to us straight with clarity. \[00:29:00\] And I think that it also set up a place in which like everyone felt that had a level of ownership and agency that was incredible. We felt like we were, like, we had a. a say in that part of the process, right? But at the same time, we felt like, and probably they were feeling a lot of pressure too, but they were doing a great job, not hiding that pressure, but like, they were never pointing fingers or putting that pressure on top of us. And I think that is something that I also take with me, hopefully, as a leader, **David:** hmm, yeah, I mean, we gotta keep in mind, we gotta reiterate what you're saying. That was eight years ago, at the time, that was a very risky choice, and so first of all, if anybody out there is considering Flutter, you know, you got, BMW had this incredible outcome in a moment when, you know, You know, you were doing your own data. You didn't have market data you could rely on, so the question would be, like, if you're a company out there doing, like, well, what are you waiting for, \[00:30:00\] you know? You got eight years of data that says this is a good thing to do. so, lots there to unpack, but. Since then, eight years have gone by. You know, you continue to grow and scale that team at BMW. and, had the success that came out of the outputs of you guys going through that process of selecting it, setting it up, building it, deploying it, getting it all out there, and then ultimately landing on BGV and being here now for four years or so. And, when, I'm curious, when you look back from like where you are today, It sounds, you know, you had some kind of key takeaways, like those formative years or that experience you went through, if you were to distill out how it influenced your engineering approach or your leadership approach, are there like some key things that when you reflect back, like that, those formative moments that change really, like what was the key pivot? **Jorge:** Yeah, I think that, to me, there's two things where, BMW in those years, shaped, the way you think about these things, right? I think that the scale of BMW is something that not that many people get to experience. \[00:31:00\] And sometimes we think of companies like. Meta, Netflix, Uber, those companies that are like massive and complex. Actually, those companies, in a way, they might have it easier because they're technology focused, they're modern, and they've been born out of like a technology core. BMW is like more than 100 years old. And they're a hardware company, they don't know to build software as well as they do. Build vehicles. So you're trying to transform a company that is not moving at the speed of like those companies and you're still trying to innovate to transform something, right? So to me, what I learned from that is you're going to work on something highly complex. So if you still provide a very complex solution, that's not going to scale. So you need to figure out how to provide a very, very simple solution. that is going to scale to a very \[00:32:00\] complex problem. If you get that sweet spot that with a very few but critical ideas, everyone gets like that aha moment, then it's how you can get 300 people to get on board and drive value for the organization. Now, if you get something very complicated for something that is already very complicated as a solution, Then it's when two years from whatever you decide to start that project, you're going to be already working on an application that is like 2. 5 stars, that doesn't scale, that your product organization is going to be like, hey, that rewrite, that refactor, that new product was a failure. **David:** I've heard you talk about that a lot. So much of what we do is this core of scalability, right? And how do you, and simplicity. How would you define scale when it comes to a codebase? In terms, what does it mean for something to really be scalable? Because people \[00:33:00\] might think about code and it's like, well, I deploy this code and I can, you know, using cloud computing, scale up a bunch of instances. And so I can get more users and concurrency and stuff like that. But I don't think that's how you tend to describe scalable code. And it's not like horizontal scale of more people using it. How would you describe it? **Jorge:** I mean, you can think of it as like the scalability of the system. And that might have to be more with like how you are setting up the infrastructure. But when we're talking about the context of a mobile app, that actually doesn't apply. Because you're not going to be talking about how many cores, how many instances you need to deploy in order to, just respond to the request that you're going to be receiving, right? a mobile application is a different beast. You're going to be working under one single executable. So to me, it's like, how can you help one \[00:34:00\] or a n number of developers? And that n number of developers is unknown. For a startup that is just about to start, it might be just one, it might be two, it might be three. But for a company like, I don't know, American Airlines, depending on the product, it may be 25, 50, 100. So how can you build a foundation that without pain can help you to achieve those goals along the way? And I do think that there's a series of like steps and rules that without over engineering can help you to be prepared. Because I think that's when like people get oh, you're over engineering for something that is not going to happen. And I do agree there's like over engineering steps that like, it's too early. That wasn't necessary. But there's some other stuff that is you're almost getting the, like, if you apply this change, that is almost for free. You're just preparing your case in case of whether that's going to happen \[00:35:00\] or not. But like, it's not introducing either like pain points. It's not introducing like anything that is harm to your code base. And still, it's going to set you up for success. Whether the day you have one more developer to talk to. One less developer is going to be contributing your code base, right? So to me, scalability, depending on the context is something different, but in the context of like user experiences is organizing your code base in such a way that everyone that is going to contribute looks at the code base in a relatively short amount of time. And if you like, Oh yeah, my, they asked me to contribute in this particular feature and I don't need to have the whole context of the code base to be. Targeting to where I need to contribute. **David:** Yeah, I read something recently. I don't know the actual number, so I'm not going to put it out there. Or certainly won't be held accountable to this number. But it was like, an engineer at a big company ends up spending like 10 20 percent of their time \[00:36:00\] actually writing code. And the rest of the time is like, understanding. what to do and what's going on. So the more you can In your engineering, make it so that understanding is easier and more consistent. just even a 5 percent increase in developer productivity time. Even just a small percentage across a very large team. That's like real business impact. so yeah. so given the name of this podcast is Build to Succeed and obviously you're an engineer and you've built a lot of things, both through the experience here at BGV and in your history, what do you think, what does success look like when you're building something? as an engineer and as a leader of an engineering team, what is success really? **Jorge:** It depends on the context. And, for example, for us as consultants, it depends on the context of the client. Right? And that is something that we have to understand. if we are working, let's say, with a \[00:37:00\] startup that is trying to make a dent in the world, It might be how to find market feed as soon as possible because they have runway and they don't want to have like infinite like expenditure or something like that. But you might work with a different partner that they're in rescue mode. It's like, okay, how can we stop bleeding as soon as possible? Because we need to turn this around and start adding value for the business, right? So to me, like in the context of consulting is like, How can we help a client to identify not the symptoms but the true pain points and then orient, in this case, the technology group or product teams or whatever to truly solve that, right? Now, if you're building, in that case, say, like a different product or something like that, it's trying to identify where do we hit that, that sweet spot. But, like, I think for technology teams, the way I try to coach our teams, what do we do with technology? that is going to help us to \[00:38:00\] ship faster and ship safer, right? Ship faster because the sooner we can just go to the market, especially we can say with confidence in partnership with our product teams that we're adding value. Let's be honest, the more revenue, the more profit we can bring to our business. And that idea of like ship safer is that what can we add to the process without losing velocity. At least maintaining velocity that is going to help us to release every single time with more confidence, right? Because what we don't want to do is to just put a release that may be detrimental to the business, to our customers, that all of a sudden gets us farther away for the particular goals that we might set up for that business. So to me, that's what success looks like. Yeah. **David:** You could look at that and say, well, we, one way we could ship faster is by adding more developers, right? Or we could ship safer by \[00:39:00\] having more testers or something like that, right? And we can solve it in a different, a bunch of different ways. I think that was the way that a lot of people solved problems for, For a lot of the last decade, especially with like, you know, more offshore developers and you can just, Oh, I'll just hire a hundred more developers and, you know, we'll just go faster that way. And I'll hire a massive QA team that will feedback that way. But those are not sustainable solutions, right? Because they don't scale, they're expensive, people are almost always the most expensive part of a technology solution. So that's where the tools come in. Come in and making sure that the culture, the teams, the dynamics, and that's a theme sort of throughout your, your story today is, is not just the technology. It's like how you use it, the teams around it, how you think about it, how you communicate this safe space you create for risk taking and for experimentation and use the technology. Not just for the outcome, but to make your team itself also better. **Jorge:** Yeah, it's that how to balance that strike between. The talent, the tools that you use, like \[00:40:00\] using Flutter has been a game changer because all of the sudden you can, if you're building iOS and Android, you can target two products at once. **David:** But the wrong talent might actually help you ruin two products at once, right? **Jorge:** So it's a very twisted way of looking at the same thing, but that's the reality. But if you have talent that can help you even to move faster, the gains that might not be 50%, maybe even more 60, 70%, because you can move faster in targeting like a larger surface of platforms, right? Then like the tools that you incorporate to the process, right? Like even with A. I. now we have assisted, coding tools, assisted like feedback on pull requests and like CI, CD and automation and all those things, right? So there's always like gains that we can add to the system that can help. engineers to focus on like how to solve those complex problems where like the technology itself in isolation doesn't \[00:41:00\] add that element of creativity and only like a human brain can add, right? To me, I think that we're going in that direction, which like that's where let's solve the big, hairy, scary problem. That's where we're going. And I think that is not something that sometimes you just need like more talent. That's another variable that you have to scale. But if you always pull that lever, it breaks at some point. **David:** Yeah. Let's wrap up on that note then and say, you know, you were on a team and you were a key contributor to a very risky choice, a bold choice, an innovative choice back in the day. And then that unlocked eight years of career growth and development and riding the wave. One of the, one of the early people to put a surfboard on the wave of Flutter and ride that. And, you know, I think when you look back, Flutter definitely has disrupted how we do user experiences and what teams can do and, and, you know, yes, there's many ways to do it, but it's certainly a great way. And there's a huge amount of advantages to it that have really benefited both of us personally and the teams that we work with and the companies we've \[00:42:00\] consulted to and every, all the teams using Flutter. When you think about now where we are, do you see any other Flutter like revolutions happening out there? like waiting in the wings that are gonna, you know, that is sort of a bold choice, maybe right now, that a company could be looking to do with their software and just generally are, is the world ready for these things? Like,what's next and, and how do we use it? Yeah. **Jorge:** it's time to dream, huh? I think that we're in a very interesting point, when it comes to, experiences. because I think that the two dimensional world, it's very mature at this point. everything that we see through a screen, like web experiences, mobile phones, they're quite mature at this point and actually that's where you see probably less innovation and that's why tools like Flutter can exist because now they can solve the productivity problem. But now that's where you see like experiences like glasses, augmented reality popping up and \[00:43:00\] it's still not clear in which direction they're going. And that's why people are taking different alternatives, different risks. And we still don't know which one is gonna truly land. You see like Megra trying like their glasses and Snapchat trying something else. Back in the day Google Glasses, right? And Apple Vision that it's like a complete different headset, right? So to me there's gonna be something about that direction, in which like that idea of like wearables might introduce like a new platform. That maybe Flutter can even complement or something Flutter like, but I think for now to the experiences like Flutter is checking the boxes. If there's a competitor like React Native for like even something new, probably like it's going to be more like how to build on another abstraction layer, like better APIs, but as a concept I think that we're already kind of \[00:44:00\] like in that space where It's pretty strong. Now, to me, there's a space in here, especially with A. I. that, I still think that is quite complicated, that it's cloud. if you look at, like, the different cloud providers and their catalog of services, you need almost a PhD to understand there. And maybe this is my own personal battle, but, the promise of cloud was, like, this is going to be easier than, owning your own servers. I don't know if it's easier anymore.just like, there's 20 different types of databases, and 20 different types of storage, and Kubernetes, and managed deployments, and all that stuff, I think that it's a beast that we're not, moving into a simpler problem, even though it's a complex problem, but I don't think we're offering necessarily a simpler solution. So to me,  **David:** are, those are not driving towards simplicity at all. They're \[00:45:00\] driving towards more options. **Jorge:** and there are tools like Firebase, for example, like that, they offer like a lot of simplicity in that space, but it's not solving the complex problem. I think it's just because they're reducing just to a limited set of options. Oh, if you are like a, especially like they work really well in Hey, you're building your Greenfield project for the first time. And. If you're this type of kind of like project, startup, and all that stuff, right? So to me, that is an area in which like I would love to see way more innovation. Just it feels like you can scale and do something that is still sophisticated without needing to deploy an army of tools and engineers and all that. **David:** Well, that's awesome. thank you, Jorge. This has been such a insightful conversation. I think hearing the story, your experience at BMW is like an OG Flutter experience getting or very early on. And I think you and the team who did that are unsung heroes of unlocking, uh, this new technology for thousands, hundreds of \[00:46:00\] thousands of developers and teams all over the world. Really appreciate you sharing that. I think a lot of people who are either invested in the Flutter community already or are thinking about it have a lot to learn from your story and your experience, and so that's what this is all about, is getting that out there and unpacking your insights. I know we didn't even talk about the last five years of your experience at VGV, so I think we're going to definitely have to do that again. But I know people are going to take a lot away from this experience, and that's why I think it's really important we highlight it. Those early moments because so much of your philosophies, I think, and your approach that you've driven into the BGV team, really, the seeds and kernels of those were back in that experience and it really helped unlock it for a lot of people. So thanks so much for spending a little bit of time and telling the story. **Jorge:** Sounds great. Thank you for having me. ### Innovation vs Operations in Modern Tech Source: https://verygood.ventures/podcasts/innovation-vs-operation-striking-the-balance/ On this episode, we’re joined by Carsten Wierwille, former Executive at ustwo, Method, GlobalLogic and frog, and current Board Director at Very Good Ventures. Carsten shares his insights on building sustainable habits, transforming company culture and balancing purpose with profit. {% audio_embed url="https://player.cohostpodcasting.com/107fd92c-5576-4eb7-b6e7-cb160dc4da66/5bd74071-9d3f-4042-b05f-1f8a59d7deef" /%} ## Transcript **David DeRemer:**  Yeah. All right. Cool. Um, well, hi Carson. How's it **Carsten Wierwille:** Hey David, how are you? **David DeRemer:** I'm doing, doing well, you know, it's a pretty hot here. Are you in a, where are you these days **Carsten Wierwille:** I'm currently in Delaware. **David DeRemer:** currently in Delaware? Oh, so hitting the beach later. **Carsten Wierwille:** I hope so. **David DeRemer:** Yeah. Nice. Cool. Um, well, thanks so much for joining. Um, you know, as you know, this is our, our new podcast we're doing here and, um, known you for a really long time. So it's a honor for me to be able to have you as one of our, Um, so to get us going, um, I just wanted to ask you a kind of more general question of like, what have you been thinking about lately? Like what's been on your mind? What are some things you've been kind of getting into or kind of really catching your attention these days? **Carsten Wierwille:** Yeah, I've been thinking a lot about habits lately. Um, in the context of, you know, me realizing that my to do lists get longer every day, and just like many people, I start every year with New Year's resolutions. You know, I want to lose 10 pounds. You want to call your mom more often than you realize by January 15.  It's all a lost cause, right? And so, and I've been thinking about more, how do I get more into the habits to do the things that I want to be doing on a regular basis that are important to me. And you and I have been talking about this as well. And so I sort of a little bit later discovered James Clear's work. He wrote a book, I think in 90, in 2018 called Atomic Habits, where he basically says, if you want to make big changes, you have to focus on incremental improvements. And rather than focusing on the output, lose 10 pounds. You focus on the process. So what are the things you're doing every day? If only for two minutes and maybe also the underlying identity, because often we think, well, I can't be going to the gym because I'm not an athlete or I can't be more organized because that's just not who I am. I'm a creative person. And if you just start changing this on a, on a small, in a small way, by just changing things you do for two minutes, you know, maybe you just spent two minutes every day to become more organized, then you will see over time how that affects the outcome. And so I think that's relevant for my personal life, but it's also  relevant for what I'm doing with a lot of founders, because I'm realizing that a lot of founders have gotten into habits. Maybe they were good habits initially, but then as the business changes, maybe that it's not such a great habit anymore. **David DeRemer:** Yeah, it's um, it's a very difficult thing to do, um, uh, I don't think, uh, naturally humans are in some ways like conditioned this way. I think there are people who are, who maybe are, who are very rigorous in their approach. Sometimes it can be too much, right? Um, but there's definitely the, Oh, well, I don't need to go to the gym. I'll just take a pill, uh, or kind of the quick things or the influencer culture of just like, yada, yada, yada, you'll get to the big result,  you  **Carsten Wierwille:** Yeah, and I think, I think what Claire would say to you is just, you just have to make it easy. You have to make it fun. So I have, I had various attempts to learn a foreign language and I've tossed around, played around with, with Duolingo and other things over the years. But I never, it never really stuck with me. And so what I'm doing now is I get up, I have a cup of coffee, and I just try to do this for three or four minutes. And then, of course, they're very good at gamifying it,  you don't want to lose your streak and all of that, but the core is to basically say, I'm going to do this every day when I have my coffee. So it's pleasurable because I'm enjoying my coffee. And by the way, I'm doing some Spanish on the side. I think you can apply that to many other things. You could apply that to more mundane things like doing your expense report. Or other boring things that you don't want to be doing, but you know, you have to do them. **David DeRemer:** maybe this is one of those things when you hear those people say, like, oh, it's a 10 year overnight success or one of those types of stories where it's like, it might look like something went fast or someone had some big break, but really it's a long time of sustained ongoing effort, you know, to be focused on these things.  **Carsten Wierwille:** That's right. And I think it's easy if you happen to be a disciplined person to begin with, but if you're more, more interested in ideas and doing something new, then it's much harder. **David DeRemer:** Yeah, no, I can relate. Um, I have a bit of a problem with prioritization myself. So, but, so let's, uh, let's get into that. So you're saying it's, um, in your, uh, this relevant to your current work. So why don't you give us a, an intro? Like what is the current  work and how did you get to where you are today? **Carsten Wierwille:** Yeah, so I've spent my career at the intersection of design technology business. I've Worked in digital product companies pretty much since the Internet came about. Um, and I sort of stumbled into that. I was actually trained as a political scientist. I think I was destined to be a frustrated bureaucrat in a public administration somewhere in Germany where I grew up. Or maybe with some luck, a high school civics teacher, um, but I sort of wasn't quite fulfilled with that. And then the Internet came about and I realized, oh, there was a whole new line of work. And so I've been working in that line of work for, for all of my career initially as a project manager, then as a general manager, ultimately as a CEO. And so what I'm doing now is I'm currently focusing on advising. And, and consulting founders and leaders in digital product companies. And my sort of ambition is to, my goal is to be the kind of advisor that I  always wish I had. So I, I'm not an artist. I don't have a personal mission to express. I get a lot of my energy vicariously from helping others. And so I'm trying to help others to realize their creative ambition. The best way I can do that is to help them work through the various questions that they have from, um, what should I be doing every day to are we going to sell the company? Uh, et cetera, et cetera. **David DeRemer:** Yeah, because I mean, pretty much your whole career has been with consulting businesses. I mean, I know you and I even have a shared experience at Accenture back in the day. Um, so, and worked at a lot of different places. I wonder if, um, yeah, I kind of relate to you in a lot of ways there, having been a kind of career consultant myself with a couple of detours here and there, but. Uh, you, you do kind of learn the, the, you become an expert at helping other people with their problems. Um, as opposed to being like super focused on a particular product vision or a particular service vision. Um, and I guess that sort of helped you to get to be where you are today to really do  that for yourself, for these **Carsten Wierwille:** Well, you have to enjoy that. I mean, and you have to be able to sort of take yourself out of it. If you, if you want to create something and you're very kind of focused on how it's done and then very fixed in your ways, you're not going to be happy in a consultative way. And I know, I know a lot of people that might start more in a consultative environment than ultimately go create their own thing because they would rather be their boss and do things their way. **David DeRemer:** Yeah, it also leads to something that I affectionately call professional ADD, which is you get the ability to, um, move around. You can kind of focus on different things and see so many different problems, right? So for me personally, it's very hard for, for me to focus on only one product, right? Or one particular market opportunity. And I think consulting gives you a lot of perspective. So that's why I'm excited to have you here as well. Cause I think having somebody like yourself who has had such a career and observing so many projects, So many different types of leaders, companies, themes, trends, uh, going all the way back to early days of the internet to where we are today  with AI and other things. Um, I think that you can see a lot of perspective, uh, when you get to see so many different things. But if I, if I, if I recall it, you also have a background in political science. Is that correct? Going all the way back to your, **Carsten Wierwille:** Yes. Yes. That's what I studied. **David DeRemer:** yeah. And so is there anything relevant from that experience to your work? **Carsten Wierwille:** I used to think that there wasn't really. Um, but I think lately I've been thinking about that some more in my, in my last role as a CEO for us two studios, a digital product studio, I helped to convert the company from founder led and founder owned to employee owned. And it actually did bring me back to things that I used to study. So it's, it sounds a bit obscure, but I, I studied with a woman named Lynn Ostrom, who was the first woman who ever won the Nobel prize in economics. Even though she was actually technically a political scientist, and she was very interested in what, uh, without getting too nerdy here, what economists call common pool resources. So things like fisheries or  farmland that is shared by farmers. So this is the idea of a finite resource. And then you have a bunch of people that have access to it. You can't really control their access. And so economic theory, traditional economic theory will tell you that's not going to work. It inevitably leads to overfishing and overuse and ultimately the destruction of the asset. And she firmly believed that, that it was possible and has been possible for a long time. If you combine the right kind of economic incentives with the right kind of cultures and norms. And so it was relevant for us to employ ownership because the company was converted into a trust. So, in the US, typically, the model that we have for employee ownership is shares, right? So you have a certain number of Facebook shares or whatever it may be, but in this case, everybody's an owner in a trust and it creates exactly those common pool problems, common pool resource problems. And so it was actually in a sort of abstract way, quite relevant for, um, for what we were doing. **David DeRemer:** Interesting. So it's that combination of incentives plus culture.  That creates that sort of  **Carsten Wierwille:** Exactly. Exactly. Exactly. You want to create incentives and disincentives, right? So you want to create, for example, disincentives for overfishing, but you also want to give the people the sense that they're working towards a shared goal rather than just their own goal. **David DeRemer:** Right. I think a lot these days, especially, um, a lot of companies have are very good at incentives. You know, they're very good at compensation structures, goals, OKRs, you know, bonuses, things like that. I think, uh, it's that combination of culture that sounds really interesting there. Um, something we believe a lot in here at VGV, but, uh, you know, I know a lot of companies are talking more and more about this these days, sort of the impact of culture on the success of a team. Um, was there something unique about the culture of, of us two that helped you to kind of navigate that?  **Carsten Wierwille:** I struggle to answer this because I think when you're inside a company, it always feels like that culture is unique. And then over time, you realize that they're probably more shared cultures. I think there's probably a lot of companies like that.  That, um, I mean, I would say even, even VGV, I think, has some of those cultural elements where you, you sort of give teams the, the power to self organize and the teams demand that kind of authority as well. And, and where people try to do a good job for, for their own right, rather than just for, for a reward. And I think that's something that I've, I've always, um, believed in that was, it's always been important to me. **David DeRemer:** Amazing. So, so now you've, um, left us to, um, your CEO there and you had a long career and variety of well known creative agencies, and now you're advising, uh, founders and leaders. Is it just creative agencies you're advising, uh, leaders of those or product companies as well, **Carsten Wierwille:** I would say it's more broadly, um, digital product companies, and the majority of those are providing services, whether they're tech services or design or strategy services. Some of them are also early, uh, ventures, early product companies, and it's really more about questions of product market fit or, or any of  the other thousand questions that come up when you start a business. **David DeRemer:** Right. And so when you look at that product market fit, are there some thematic learnings you've already been observing, uh, in today's world around, um, how leaders are addressing product development or, or how they're developing their own company cultures? **Carsten Wierwille:** I mean, it's, it's, it's a broad question. I'm not sure I have a general answer. Um, particularly when I think when you're an early startup, I think that's probably the, the, the, the simplest observation that I can share is that virtually every founder, every startup tries to do too much. So, so that's, that's almost always a good thing to ask is how can you do more with less? **David DeRemer:** Right. That gets back to the atomic habits, I guess. Right. It's like, how do you have a roadmap there and not bite off too much and just jump ahead to the glory? Yeah. Well, it's, it's not easy. I can, I can attest to that firsthand. It's,  um, you got a lot of competing priorities, so how can you prioritize? Like when you're thinking about, uh, uh, your team or your, your company growth or, or your product, you know, what would you recommend people to kind of help figure out how to prioritize what matters? **Carsten Wierwille:** mean, I think the, um, the way I used to think about it, um, during my time at us, too, was I used to think about it in terms of profit, people and purpose. Um, and that was just an easy way to remember, because I had 3P's. The profit, of course, if you're a service provider runs through, um, also if you're a product company, it runs through, um, the customers that you have. It just doesn't start with the P, but it starts with, are you doing something that is worth doing that anybody's willing to pay for? Um, and so you want to get that right and that's obviously what all the businesses are focusing on. Um, particularly when you, when you start a business and even more so when you start a  business that's based on talent. More than anything, you have to think about what kind of people do, do I want to hire and nurture and how do I want these people to interact with one another, particularly when I'm not in the room. And then I think the last one, I think is becoming more and more, more and more important for the world at large, but also for the people that are working in companies, which is what is the larger purpose other than making money. **David DeRemer:** Hmm. That's a tough one. And right. Cause the, uh, have you seen more and more companies really embracing that purpose side of it? Or do you, and do you think it's more of the smaller entrepreneurial companies doing that? Or do you see it on the enterprise  scale?  **Carsten Wierwille:** I think I think you see it on the enterprise scale. Um, it, the urgency of the topic sort of comes and goes a little bit with the economic cycle. So it's, it's easier to talk about purpose when you make a lot of money when the shareholders are unhappy, and then the pendulum tends to swing back to the profit a bit more. But I think, I think there's more and more frameworks for that now. And it's more rigorous as  well. And it doesn't have to be. I think the, the old notion is sort of you, you make money and then you do a little bit of corporate social Uh, purpose sort of sees our work to comment to compliment that. And I think I think increasingly company trying to, uh, provide more holistic approach. So, for example, um, a lot of businesses have become a B Corp, which means that you actually legally commit to doing more than just, um, making money. We did this at us too as well. And, um, it's essentially a certification that comes with a set of questions and a really long spreadsheet. And you only, you have to do this once. It's pretty tough in order to get the certification. And, um, then you will have to be recertified every few years and you will only be recertified if you get better and better and it keeps you honest. It keeps you honest across. It's, it's not just fluffy. So there's a bunch of like very specific things that are being looked at from, you know, the governance of your business, which is important, but  also probably boring to, you know, where you source the, um, lunch that you buy for your cafeteria and so on and so forth. And it allows you to provide a framework that is not unlike a, a, um, a set of KPIs that you might use on the operational financial level for the company that you can work against. So  **David DeRemer:** think.  **Carsten Wierwille:** see over time. **David DeRemer:** Yeah, yeah, sorry. No, I was just going to say that, um, I think it's fascinating to hear that direction and see things emerge like that. I also think, um, or I wonder if the proliferation of more remote work where people kind of have a lot more choice of where they could maybe work and their opportunities, um, whether or not this will make some of these choices of companies to invest in things like purpose, um, strategically very relevant, you know, cause if you're competing, if I have a lot more job options because I can work remotely. And I'm competing between a couple of firms that have, uh, similar compensation. You know, maybe I'm going to go after the one that's more  aligned with the purpose. I believe in, um, or at least a company that is more, uh, interested in pursuing purpose. So I wonder if that will continue to push in those areas. **Carsten Wierwille:** It's also a potential overlay for the types of business that you pursue, whether you think that the things that you are making money with are in line with your values and the things that are meaningful to you. **David DeRemer:** yeah, well, and as leaders to take stands right on sometime, especially in consulting business. I remember a very specific, specific example with the two of us, um, a long time ago and it was a client that, uh, where we had to fire a client if you, or you fired the client actually. Um, and it was, and yeah, it was, it was an industry that was, uh, you know, it was, it was a compromised industry, you know, so sometimes I think leadership has to make that choice as well to really show what you stand for, um, and not make the choice of profit, um, or, or dollars. Yeah. **Carsten Wierwille:** Yeah. I mean, I typically, I find myself more arguing on the other side because I have to a lot of situations where people have challenged you. Why are we working with this company, with this brand? Do they  reflect our values? And, um, I think there's sort of a misunderstanding there where doing business with a company doesn't necessarily mean that you agree with every single decision that they make, or that you even like them, right? If you're going to go and get a haircut this afternoon, are you going to ask the barber if, if they share all of your political values? Probably not, right? In that moment, what's more important for you is that you're going to look good afterwards. And I think on an ethical level, for me, the important question is always, is the work we're doing going to make the world a little bit better or a little bit worse? We can't possibly, if you're working with Google, let's say, or any other number of company that has hundreds of thousands of employees, those hundreds of thousands of employees make numerous decisions every day. Who am I to evaluate that? Right? I mean, eventually we might, I think that we're beginning to see more, more frameworks, just like the way there's, um, third parties that evaluate credit risk. There's people that are trying to do more an  evaluation of corporate actions based on ethics, but it's very difficult to do. And, and I am not in a position to judge whether a company is good or evil. I don't really want to. The best thing I can possibly do is try to assess to the best of my ability that the things that I'm going to be doing on a project are going to make the world a little bit better, a little bit worse. **David DeRemer:** That's really good advice, and I think you could expand that notion to any number of things, right? It's purpose, but also, like, is it the right feature for the user? Are we improving our execution culture, uh, on our team? Or, uh, I think, just, are we making it a little better? You know, it's, uh, it sounds obvious in retrospect, but I think probably a lot of companies are out there doing things, and teams are doing things where no one understands if what they're doing is actually making it any better. What, what strategies have you adopted as a leader over the years, um, to help kind of navigate some of those conversations or motivate your teams and your employees around some of these? Like, I know in our interaction, oftentimes you've taken the role of like a skeptic in a conversation, right? Kind of like provoke  more conversation or dig into an issue. Um, uh, what, what strategies like that have you found are useful, um, to help people really get to the meat of an issue? **Carsten Wierwille:** You know, I think people tend to have very strong opinions, and they often go into a difficult conversation, either afraid, because they might be speaking with somebody that has more authority than them, and they don't want to be disciplined, or, um, they go into it combative, and they, they're looking for evidence that supports their, their theory or their assumptions. So I think the best thing you can do, and this is true, not just for, I think, a business context, for a personal context as well. Is to help the person understand where you're coming from or where other people might be coming from to create some basic empathy. And I think if you're in any kind of client service role, having the empathy for a client is essential and equally, you can't take it for granted. I think  people sometimes are going, why are they asking us to do things? This is just stupid. And so you have to get teams to understand or try to understand why a client wants to do certain things. It doesn't necessarily mean you agree. And sometimes you want to push back. But the starting point has to be why are we being asked is what's the context? What's the background? Where does this individual come from? Or what is the culture and the background for this company? **David DeRemer:** And do you ask those same questions of the leaders you advise as well? Have you been able to translate that to your client relationship skill, to helping someone who's not necessarily a client in that regard, that you're doing a service for them, but you're a coach or mentor? **Carsten Wierwille:** Yeah, and I think often in that context, um, I, I ask founders to reflect on their own role. I, I find the, one of the things that I'm very interested in is to combine the question of what should the business be doing and what should the founder be doing? Because often the, those 2 are quite related and it's,  it's often difficult for founders to have this conversation. You know, they might, they might want to, at some point decide that they want to sell the business and they go to a banker and broker. Right. Or, or maybe they're trying to be more productive and maybe they talk to an executive coach. But the question of how those things are related, um, you know, often the people that I'm talking to are working with. Are tired, and then because they're tired, they're thinking of, do we sell the business or do we bring someone else in here, but the fatigue might be related to what they're doing every day. And so often selling is not the best answer. It's often it goes back to the question of why did you start this company? What motivated you? What, what were the highlights of your time here? And how can you go back to that? Or how can you rekindle the passion for work and for your company in a different way? Um, rather than looking for kind of an easy way out and and if you know, if it's just a matter of, you know what, I, I've done this long enough. I want to do something else.  I want to take some money off the table. I want to start spending more time at the beach. More power to you. Then let's talk about how we make that happen. **David DeRemer:** Well, I think that, um, the insight you're giving there and that advice, I think it applies to anybody no matter where they are in their career. Cause I think that same like story arc you just went through, maybe a different situation if you're not a founder, but you know, you're somebody who's been in a role for three or four years. There's a lot of red tape and bureaucracy. There's some personalities that are difficult. There's mundane routine things. You're tasked with some big initiative, new project or new product development effort. Yeah. Absolutely. Absolutely. And you got to keep that passion. You got to keep that energy, you know? And so I think, uh, it's good advice to kind of think about how do you reflect around what is it that you're doing every day that is getting in the way of that passion? Um, and maybe can you change your frame a little bit and get some help or talk to a coach? Um, I think, I think I was just, uh, Reflecting not just in my experience as a founder, but as an employee and as a, as a member of other teams, and even our, uh, the people that we have on our team. And as you know, people go through these journeys,  you know, no matter where they are in their career. And it's interesting to kind of keep that focus. Um, well, you also gave some advice that I thought was really interesting. One point at one point around sort of how to manage people, right. That I just thought was really interesting. Sort of that going against the grain mentality, right. I'm a, I tend to personally tend to be a very, um. Ideas driven person, yeah. And I think like, um, one thing that you've helped me with that I'm curious if you've helped others is like injecting that a little bit of like, like skepticism to some of this stuff. And like, I really focusing on the risk, um, versus like flipping that toggle back the other way when people are feeling a little lost or. We're like kind of getting them back on, on paths that are successful for them. Um, I was just wondering if maybe you could tell us a little bit more about some of your, how do you manage those two things where people have like very overconfident opinions versus when they're a little lost. Um, how do you get people to, to be back on track? **Carsten Wierwille:** And we're all very social animals. And so we, we kind of get into these hurt into the herd mentality, right? If a company is doing  well, then everybody feels very confident, sometimes overly confident, if there are challenges in the market, you can sort of get into this kind of negative mindset. And I think often as a, as a leader, it's helpful to be, to try to take a step back, but also a bit of be a bit of a contrarian. So I often try to. I don't want to say stifle, but, but, but maybe challenge some of the optimism when things are good and when things are not going great and everybody thinks the world is ending, to emphasize the opportunity. That often exists, particularly in the downturn. So I think the skeptic part comes more when I say kept skeptic. I mean, there's more in a less than a much anyway and more in the sort of philosophical Greek tradition of, of not trusting the facts and the information that you have in front of you because often you tell yourself the story that you want to hear or you want to believe. And the skeptic in me is always saying, well, do I really know what's going on? Do I really know why clients are buying or not buying our product?  And so the, I think the skeptic pushes me towards getting closer to the buyer, to the customer or prospect. Um, but I think it's often good, um, when you are in a leadership role to try to be a bit of a contrarian and work against the grain when it comes to the mood, the prevailing mood of the moment. **David DeRemer:** I would think that would help to identify some of those atomic habits too, right? If we're like all super motivated, really happy, mission driven, we've got some big, bold ambition and we're just forging ahead. Sometimes you need to kind of take a step back and think, are you actually doing the hard little things along the way to get there? You can't just jump ahead. So that's, **Carsten Wierwille:** That's true. That's true. Yeah. I mean, I think you. As a competitive person, I think a lot of people that work in business have a competitive streak. You have to, your mood can often depend on whatever happened this week or this morning. So, if you lost the deal, you're going to get frustrated or maybe angry at your colleagues. If you want, you think you're on top of the world. Yeah, going to be the  next Elon Musk. And so the, I think one, one habit that I like a lot is to sort of take a pulse, take a measurement. At the same time every week. So Friday 3 o'clock, I look at a certain set of data, but that's the forecast or it's, um, I don't know, sales day or whatever it is. If you do this over a period of time, you can kind of look at the patterns and you, you sort of balances things out a little bit and you're less, you're less victim to whatever happened last in your life or what happened last in your life. In your sales pipeline. **David DeRemer:** It's not an easy thing to do to separate those things and kind of look at it, uh, objectively it's, uh, you know, I think anybody in their career when they're, they're doing interesting things with deadlines and objectives and goals, it's a rollercoaster up and down, you know, but I think it's, it's important to like, I, you know, You're like, uh, identify that, like, you know, the, or a thing I've often say is like, you need a valley to have a  peak, you know, you, the rollercoaster doesn't just constantly enjoy the fun part. Right. Um, it's got to clink up and climb the mountain before it can go down.  So.  **Carsten Wierwille:** No, and it's that is very taxing, particularly on on on early stage companies, right? You, I just last week had a conversation with, um, with the founder and they were just about to kick off their first project and they were so excited and they had work lined up for a year and it seems like finally there was proof that what they, what they were wanting, what they were going to do had merit. And then for for a reason that had absolutely nothing to do with them. Or even the client, the client that they were talking to, the whole thing fell apart. And then you sort of fall back into the valley of despair. You have to have something that sustains yourself in that situation. And I think a lot of that you have to find in yourself and hopefully in your personal life, your family and things that are arguably a lot more important than the business. But I think  that can be also habits and routines to sort of shield yourself a little bit from, um, from the roller coaster ride. I **David DeRemer:** Yeah, so I just want to maybe change tacks a little bit. Just, um, thinking about the actual products and the tech initiatives themselves. Like we talked a lot about the teams and some of the culture and the leadership you and you've observed and, you know, been aware of or participated in so many. Projects and, uh, tech initiatives from your time at all to your, you know, in your various consultancies that you've worked at, um, across many iterations of internet and mobile and all kinds of growth and all these things. Why? Why do you think that some companies or some products or tech initiatives succeed when others don't? Is there something kind of unique to certain things that you've, when you look back, you see there is a theme that themes that kind of companies or, or teams or products that match this kind of pattern tend to be more successful? Is there  anything like that that's popped up? **Carsten Wierwille:** think for the last decade or maybe more there's been sort of an obsession with disruption and transformation and innovation. I think doing things dramatically different and I'm, I'm, I think more often than not those things never work, don't work. I, I think that tech initiatives or, or possibly any initiative is more successful if it amplifies something that already exists. It amplifies beliefs, it amplifies practices. So if you are an operationally focused company and you are engaging in, you know, a revamp of your mobile app, whatever it may be, chances are that if that mobile app amplifies and emphasizes your operational excellence, then it's probably going to be successful. If, if that app all of a sudden is meant to be more about innovation or  a customer intimacy that you never had, it's probably not going to work. So, I'm not saying it can't work, but I think in general, it's easier to do the things you're already doing well better than to try to get good at the things you suck at. That's true for us as individuals, but it's also true for companies. So particularly in pre digital businesses, so I'm not talking about the Silicon Valley, but retailers, health care companies and the like. There's still traces of the old IT versus business conflict, typically. And so then you, um, you might have a more operationally focused tech team. You have maybe on the marketing side, a more innovation focused team. And there's constantly this back and forth between trying to disrupt and innovate and do all those things that are important and then, and then this idea of, of kind of maintaining the status quo. And that can be a kind of an uneasy back and forth that if it doesn't get resolved, you're probably not going to make a lot of progress.  So I think you have to agree on what are we good at? Where do we play? What can we realistically make a dent? And I think if technology serves to amplify that, you're going to be more successful. **David DeRemer:** It's very insightful. Uh, in the, in the startup world, I think a lot of people look for the home run, you know? Where's the big market opportunity and, you know, but why? What's that belief or connection that you have? So I think kind of grounding it in those things that, you know, I think that's just a very good way to put it. Like, like you were saying, if you're operational, then, then playing to your strengths, uh, is, is probably going to work well if you try to, if you're very buttoned up, rigorous, operationally driven company, and you're trying to do something really innovative and loose and disruptive, it's gonna not align with your culture and you're probably going to face some, some, Uh, headwinds there, like do the, is that company culture that drives that the leadership or the sort of like how a project is set up? I mean, you've obviously worked in a consulting context. So a lot of these are programs that have come to a partner,  right? And by the time you engage a partner, like, why are they coming to a partner, um, in some of these situations and what's the role of a, of a partner maybe to help with that to be more successful? **Carsten Wierwille:** So I think there are really 2 questions, right? Why, why, where are these things manifest themselves? Is it culture? Is it incentives? Is it organization? It's all of them. And I think it's often all of the above. I, I worked once with a, a global, um, quick service restaurant leader. On on their global digital digital initiatives, and they have more of a franchise organization in which really the franchisees had a great deal of authority in terms of what's put on the menu and how you price it. And there was this idea that from headquarters, you created a global digital platform. You can imagine how well that went. So if you have a, but so that was reflected in incentives and culture and muscle memory, basically every aspect of the business, and it was never going to work. It would have only worked  if the digital platform had been enabling, but it had protected the decision making authority at the franchise level, and that didn't happen. And so, as a result, I think it was kind of a wasted opportunity in many ways. Um, I guess your other question is, is, um, what was the other question? **David DeRemer:** No, it was, um, how can a partner help to navigate **Carsten Wierwille:** Oh, and why, yeah, why, why, why do companies go out of house? Right. And what's your question? Yeah, I think, I think there are different reasons, but I would say, like, very often companies go out of house because They're thinking about what's next. So maybe we are a quick service restaurant and our, our core expertise is to deliver delicious, delicious food quickly to our customers in thousands and thousands and thousands of  places around the world. Um, and our expertise might not be in, in delivering a great mobile app or, or a consumer friendly website. And so those are situations where companies go to a partner because they privilege. They don't have that expertise. They need help. Maybe they want to develop the expertise in the process. And that's, so I think that's outsourcing for what's next. I think there's also a fair amount of outsourcing for what used to be or what's not that important anymore. So you take maybe aspects of your product portfolio that are more established and you decide to get some cost out. So you move the, you move the jobs offshore and that's, of course, something we've seen for at least two decades now. So that has more of an operational cost saving mindset where the former is a bit more about, uh, innovation, developing new capabilities mindset. **David DeRemer:** Is there anything around, I guess the, what's already been, that's like your scale teams, like  augmentation, like that side of it. That's what you mean by that one. Gotcha, gotcha, gotcha. Um, so I guess, uh, given your experience, um, what has made, when you think back on some of the partnerships you've had, um, where you've really kind of helped firms navigate this and, and kind of help them figure out how to be successful with the product initiatives. Are there hallmarks of those relationships that seemed to be important to the relationship, like getting the results that you were expecting? **Carsten Wierwille:** I mean, it sounds kind of trite probably, but I think trust, openness, clear communication are really important. I also think the, the best relationships that I've seen, there, there was flexibility, both in terms of the, the outcome, but also the relationship, um, a few years ago, during my time at IS2, we worked with, with Peloton, and we worked on a home fitness game, and Their idea was to offer a  different way of, of working out. So the typical Peloton fitness experience is an in class experience, right? You, you log on from the confines of your home, and then you watch an impossibly good looking instructor yell at you for an hour or so, and not everybody likes that. I mean, I personally don't like to get yelled at in any context. And, uh, the, what we developed was more of a music and rhythm game that you could quote, unquote, play. So you could work out music. Uh, on the bike on the treadmill and you just try to stay in rhythm and pick up obstacles. And that would just be a way to more playful way to work out. And when, when that engagement started, they didn't have any expertise in this area. They didn't have game designers or Unity developers, any of those things. And they're like, well, we're never going to develop this team. And then we launched this product, um, which is called lane break and it was successful. And there was really a, an interest there on the part of Peloton subscribers. And they realized, you know, Well, we have to maintain this now, and maybe we want to do more of those types of things. And so then they did hire people  that have skills that's similar to ours. And there was sort of open communication throughout. So initially, we were more of a, um, of a design and development partner creating something against. Not a spec, but at least a clear objective. And then over time, we became more coach enabled, helping them to bring these capabilities in house, knowing full well that we would put ourselves out of business. But it worked because there was clear communication about it. Sometimes in a, particularly in heavily procurement driven organization, you break the outcome of a service down into small parts. And then if your needs change along the way, you can't adjust. But it's also, it's a fine balance, right? If you're, if you, David, you and your wife are decorating your kitchen next week, and the carpenter comes in and says, I work in an agile way. So I can't tell you exactly what's going to happen, but it's going to look great. You're probably going to be a little uncomfortable about it. So there's situations where the people that we are paying services from, where we expect them to deliver against,  precisely against our expectations. And then there are many situations where we're more open to flexibility. Ideally, over time you build trust and the outcomes, the deliverables become more flexible. The outcome and the objective remains firm. I think those are the best collaborations. **David DeRemer:** You were talking a lot earlier about purpose too, and I think that kind of comes through here in terms of like aligning your purpose as a partner and, and as a, as a firm, right? If you're, you're aligned around a scope or a budget, you know, or a fixed number of hours or something like that, you're not all actually aligned on your purpose. You don't give you the room to evolve, um, if your, if your purpose is aligned. So in, in your example. You know, the, the inverse of that story is if I say, okay, okay. We got 50 grand to renovate the kitchen. Uh, we agree on these things, you know, but then as that build happens, I'm like, yeah, you know what, I really liked this really exotic marble I found. Let's just add that. Oh, I really liked these door handles that are much more,  I know they're a little bit more expensive, but let's add those on, you know, um, I think where it works best is when you have a real partnership in the relationship where you're, you're, you're, you're seeing this as a mutually beneficial. together, the tension we're going to maintain. As a service provider and as a, as a client is tension, like holding up a drawbridge, right? Or holding up a suspension bridge. It's good tension. It's going to help to add stability and make sure we get the right result. Um, if you don't have any tensions at all, if it's just an, an unlimited blank check or no scope, you know, um, or no parameters at all, then you're going to get a lot of waste. Right. But I think that sort of healthy tension can be really good sometimes. And I think aligning around that purpose, like we were talking about earlier on the conversation is really important. **Carsten Wierwille:** In honesty, I think it's, it's quite common in any kind of service industry Clients are looking to, to do what you're doing over time that they're trying to replace you. So, like, hey, we're going to bring these guys in and they're going to do  some development work or they're going to come up with a strategy and then we're going to just take this and run with it. Um, but it doesn't really happen that way unless you put specific steps in place. So, if you want someone else to teach you how to fish, you should pay them not just for the fish, but also for the teaching. But both the vendors and the buyers of the service are often not honest about that. Arguably, if it's important, then it should be monetized and it should be part of the service that's delivered. **David DeRemer:** Yeah. Well, it's a, it's a tough world because we have so many constraints, right? Budgets and conflicting issues and procurement teams and all kinds of stuff that, um, make some of this stuff complicated. And a lot of times you get a client partner and, and you as a, as a service provider, you're totally aligned, but there's all these other layers and things you have to navigate through. And it's about getting creative through that process. But I've always also felt like at the end of the day, right, it's just about relationships And I think that's one of the things that's I think a little bit worrisome about some of the  AI stuff going on right now is just sort of like eliminating the human interaction and some of the things that go on, because when you think about it, everything we do, at least as of 2024, June 18th, uh, you know, It's still human to human like everything involves humans and I think, um, nothing in my opinion transcends just being good people and having good honest relationships with people. So much good comes from that. So, um, well, Carson, this has been great. Um, I think we're getting a little bit tight on time. So, um, I just wanted to just kind of wrap up and say, I have two questions for you to kind of wrap up. One is, uh, do you just have any sort of parting insights or advice that we didn't touch on already? That you, uh, when you think back and reflect on your careers, then it's something that you've really learned or your go to, uh, bit of advice that you give to your, uh, the companies you advise. **Carsten Wierwille:** Yeah, I think that often the I think often the most interesting things that I've done, both in my career, but  also the most interesting things that I've seen on project is that when you just remain open to whatever you discover. So, I have ended up doing things with my professional career that I've never anticipated. And similarly, often projects end up in a completely different place. Then you imagine, you know, I worked on a project that was supposed to be improving the, the queuing, um, in an amusement park, a very, very specific, narrow problem, and it ended up completely transforming the park experience. So, if you sort of stay open to A, the problem at hand, but then what else could you do if you solve that? And I think that's probably what I, what I would tell people is just you, you want to be fully in the moment, but be open to what you can do with this experience on a broader scale. **David DeRemer:** That's great advice. Um, would you, uh, when you think in your advisory work or, or when you, uh, meet someone new, are there any, uh, go to books or podcasts that you always recommend people pick up?  **Carsten Wierwille:** Um, beyond the, the James Clear that I just already touted, I'm not sure I can think of anything right now. I think it would depend more on, on the specific  **David DeRemer:** Gotcha. Fair enough. Fair enough. Um, if people wanted to find you for advisory services, where would they, uh, where would they look you up? **Carsten Wierwille:** Yeah, they can, they can find me online, um, and just look up my name. There's only, as far as I know, one person in the world with my, with my name. Okay. **David DeRemer:** Wow, that's impressive these days, for sure. Great. Um, cool. Well, thanks so much for this. It was awesome. Thank you so much for taking some time out of your day. I know you're a busy guy, and um, I've had a pretty profound impact on me and VGB, so I appreciate you getting a little bit of time here for us to chat with you. **Carsten Wierwille:** You're most welcome. Thanks so much. **David DeRemer:** Carson. Okay. ### Transforming UX With Design and Technology Source: https://verygood.ventures/podcasts/kyle-crouse-transforming-ux-the-power-of-design-technology/ On today’s episode, we’re joined by Kyle Crouse, whose previous roles include Senior Manager, Design Technology at Amazon, Principal Design Technologist at frog and Creative Technology Director at Method and Visa. He is currently Director of Concept Technology at Very Good Ventures. Kyle discusses the integration of design and technology in product development. {% audio_embed url="https://player.cohostpodcasting.com/7e43e085-8937-429a-875e-9ed8452e07e5/b96e811f-31eb-4ec3-90ce-b8990b000bf6" /%} ## Transcript **Host - David DeRemer:** Alright. \[00:00:00\] Hey Kyle. How's it going man? **Guest - Kyle Crouse:** Good. **Host - David DeRemer:** First, first VGB podcast. It's pretty, pretty cool. I know. And, **Guest - Kyle Crouse:** honored to be your trial. **Host - David DeRemer:** and to do it with a good friend. Um, so, so, uh, how are things in California these days? **Guest - Kyle Crouse:** Uh, nice and warm. Uh, you know, summer's, summer's here. Ready for the beach. **Host - David DeRemer:** Nice. Well, for us East coasters, it sounds like that place is about to fall off into the, some sort of weird crack in the earth and do some weird stuff. But, um, yeah, I guess that's a good segue. So you're out in California. Why don't you kick us off by telling us a little bit about your background and where you're at. Nice. **Guest - Kyle Crouse:** I am sure. So I am, uh, based in la um, I, uh, been with VGV for about a year. Um, prior to that, uh, I was in a design studio, uh, within Amazon, um, both here in LA and in San Francisco. Um, doing advanced product, uh, design and concept work, coming up with new devices services. Anything really within the entirety of the Amazon ecosystem. Um, and generally speaking, I have a background in, uh, design agencies as a design technologist. Uh, I worked with you at Frog for a very long time. Um, and then at uh,  Method. Um, mostly all of that in, uh, in New York City. **Host - David DeRemer:** And I famously defeated you in the one and only frog foosball tournament we ever \[00:01:00\] had. **Guest - Kyle Crouse:** hey, hey, hey, I don't wanna, I don't wanna hear about that. We were formidable on the, on the team. Wild stallions forever, you know. **Host - David DeRemer:** Yeah. Well, you know. **Guest - Kyle Crouse:** ha. **Host - David DeRemer:** Couldn't, couldn't defend us, but, uh, yeah, I'll get it. Well, so listen, you say you're a design technologist, creative technology. I thought maybe we'd start there. Cause that's something that, you know, I've talked a lot about and. Um, even within VGBA, as we start building out new disciplines, I think that's a, a category of work or a role or function that confuses a lot of people. And I was wondering if you could just start by explaining what you think those, like, what is that? What is that discipline? **Guest - Kyle Crouse:** Sure. So design technology I think really is what it says. It sits kind of right in the middle of both disciplines, design and technology. And, um, I think folks who specialize in this kind of creative technology, they're, they're makers, they're tinkerers, um, hackers, experimenters, but they also tend to have a really strong sense of, uh, you know, User experience and quality in that regard and like a focus on details and patterns of design and why you do certain things you do and how they're in service of quality \[00:02:00\] Customers so that you can, um, bring technology expertise to the design process. Um, so that as you're thinking through new concepts, you, um, have confidence knowing that things are, might be particularly feasible one way or another where trade offs can be made for like. Uh, expediency or cost savings or whatever it might be. Um, you can very quickly, uh, prototype options to prove out design hypotheses and kind of iterate really rapidly to gain more and more confidence in the, in the, uh, Feasibility of a certain design. Um, and then importantly, go back downstream as something transitions from design, goes more into engineering. Um, it's a bridge opportunity to take the knowledge around design and how something is, Is meant to be implemented and translate that downstream to make sure that, uh, the, the, the fidelity of the user experience is, is maintained as much as possible that the, uh, technical criteria that were kind of defined as you work through the design are kind of successfully \[00:03:00\] transitioned downstream. If there's more prototyping that you need to do in more detailed level, that's great. Um, oftentimes design technology, design technologists, um, can and do play like a bit of a front end UI engineer type role sometimes. Um, you know, with such a keen eye for making sure that there's a high fidelity of user interface implementation, that can be a good role if it's someone who is well versed in the nuances of, of production engineering and what the differences are for more of that upstream, like, you know, quick and dirty, uh, prototyping type of engineering. So it's, it's, it's a nice hybrid, you know, , in both places, but you're really there to like ease that transition and make both ends, uh, smarter. **Host - David DeRemer:** Do you think it's, um, mindful and purposeful that it's called like design technology as opposed to like technical designer or, or sort of the inverse of that? Like, is there a difference between, is it more design led or, or engineering and making led? Does that make sense? Thanks. **Guest - Kyle Crouse:** Yeah, it does. And I, I've always. appreciated that it kind of is design technology or design technologists because I think with design first it's accentuated more that it's about design and \[00:04:00\] technology within design. Um, I think the inverse, you know, has a much more specific, uh, role connotation around technical designer and, you know, being someone that's coming up with more of a, of a, of a hardened technical solution, right. Um, whereas I think in the, in the, in the other sense, you are more designing through technology. Um, and. I've seen it used in a lot of different ways, and they have,  think they can have slightly different connotations, whether you think of design technologist versus a creative technologist, you know, um, and and what that might imply. But either way, you are someone who is more trying to come up with solutions for customer experience, and you're just looking at whatever technical options you you have that you can bring to bear that might get you to that solution in an elegant and efficient way. **Host - David DeRemer:** Yeah. I was curious, you know, a lot of people who maybe haven't heard this might think it sounds like, well, wait, when you're doing a startup, you're doing a lean MVP, you're kind of just, you know, trying stuff out, prototyping, shipping things quickly, how would you compare sort of like that type of activity where you're kind of like literally a lean MVP and you're just going to build something and putting out there and see if it's successful? \[00:05:00\] How would you contrast that with the actual discipline of, of design technology or creative **Guest - Kyle Crouse:** I think it's very similar. Um, I think, you know, I think the biggest trait of the biggest traits of design technologists are that, you know, you have a keen sense and understanding of design. Um, you have a thorough understanding, um, uh, an aptitude for technology. Um, but you're really adaptable to whatever the situation might be. And you're really comfortable with ambiguity. Um, because I think You know, if there's if there's three kind of aspects to general like technologist, you know, there's the ability to like, build an idea out of thin air, there's, you know, create something out of thin air, there's ability to like, make it into a real thing. And then there's ability to scale that thing. I think, you know, more often than not, any kind of technologist is really going to be good at Two out of those three things. Um, and I think a design technologist and people, um, in that ilk, which is where you compare to startups, I think they're going to be really, probably pretty good at like coming up with a new thing, building through like, uh, how to make that thing a reality, um, and getting it out there to a certain state. And then. You need to start shifting and think like, how does this become durable? How do we scale \[00:06:00\] this? It starts to become a whole different animal. Right. And a lot of times there's a real difference in, uh, design technology because you don't concern yourself with scale more often than not. Right. You don't concern yourself with like longevity of something you are building things to kind of prove a point and then throw it away. Um, or kind of like get to a certain point. And, you know, uh, move on from there. Um, so. That might be the case early on in a startup when you're just trying to ship something to market you're just trying to get It out there and you're not trying to be considerate of where you go with something long term Which you probably end up with a lot of technical debt because now you have something out in the world that wasn't built hardened in the first place But but going back to the original question, I mean, I do think that there are similarities in that regard It's just that you know from a startup perspective. Ideally, you're more concerned with like What is going to happen to you once you do get it out there? And I think on the design technology perspective, um, you, you might be a little bit less concerned with that depending on where you are, uh, what you're working on. **Host - David DeRemer:** Yeah, I think they're like if I understand them correctly, they're both trying to get at similar results, which is how do I improve my product? But when \[00:07:00\] you're thinking about a lean startup MVP type thing The idea is like get it out there and your users will tell you if this thing is being successful whereas as In our conversations and my understanding of this is you're investing in some technology and prototyping and explorations really to inform and improve the design, um, which is a little different than like once out there. You're like improving the product or getting a sense of are the features doing well with clients or something like that. Um, so I almost think that the same mentality could live in both areas potentially. Um, **Guest - Kyle Crouse:** A hundred percent. Yeah. Yeah. A hundred percent. I think you, you are trying to improve, um, inform and improve that design process. This also kind of, uh, is under the assumption that you're building functional prototypes to begin with. Um, you know, I think it's, it's, it could. often be the case, you know, within design technology that what you're building is not even functional. You know, you're doing something more smoke and mirrors, Wizard of Ozzie, you know, where you're just trying to understand and experience, um, or, or hook up small component of it and not try to build something more functional. Whereas, you know, on the lean side, you You know, your, your primary focus is to make it functional, um, and worried about checking off those boxes of something working, um, versus, uh, uh, proving out some discrete aspect of the, of the, of \[00:08:00\] the feature. **Host - David DeRemer:** Yeah, yeah. Um, you've worked in places where these companies have prioritized this. You've worked at some pretty amazing places, you know, so like Frog Design, one of the top design agencies or consultancies in the world. Method, owned by Global Logic now, which is another big, incredible, one storied firm. And Amazon, of course. Everyone knows Amazon. Um, yeah. So, you know, what is it about those firms that allow this, function, this capability really came to bear? Because I think especially these days, it might be hard for a lot of companies to understand how to invest in this. You know, everything's so lean, so fast. Um, what are the situations that kind of like allowed this discipline to emerge? And what is it about, you know, those types of companies that make this discipline thrive? **Guest - Kyle Crouse:** When you look at companies like frog at the time when we were there, you know, frog did not have any engineering capacity other than, you know, it's kind of wholly separate, um, engineering company that sometimes it would deliver things to, but the majority of the client engagements, um, that, that, that we would work on would never, have a specific engineering resource or capability attached to it. Um, so you, you ended up with these scenarios where you would design all of these beautiful artifacts and then \[00:09:00\] hand it over and Whatever happened to it happened to it. And more often than not, because of handing it off to an engineering team, uh, the, the faithfulness to the fidelity of whatever that design was, uh, may not be met. Um, design technology helps you fill that gap because the role that we played in that scenario was to, um, Help bring the technical knowledge from the client teams upstream into our design process so that as we were designing, um, we weren't doing so in a black box. We weren't handing things over that we knew would not. Like, things that we handed over we knew could work some way in their system with some level of effort attached to it. Um, and then we might, uh, prototype some of those to give to the client as examples, especially if they were for, uh, richer, more fluid things, not just flat screens. Um, If we, uh, if, if, if in some scenarios we would develop like what we called reference code, where we would basically build the front end of the application in a way that would be appropriate for, um, integration into their systems and then production hardening. Um, and then kind of, as I was saying earlier with the two sided nature of it, then going and sitting with the client team and actually helping them. \[00:10:00\] implement the front end, you know, whether it was the code that you delivered or, you know, just helping them build out the designs within their front end environment, you were able to kind of bridge that gap to ensure that the client had more success in what they shipped because it met more of their expectation on their users needs around what was originally designed in the first place. Right. And it made sure that their investment in all of that design was was sound because they ended up with the thing that they spent the money on instead of something subpar. **Host - David DeRemer:** Yeah, I feel like so much of it is still, we, we talk about this even still today. And it's one of the things we're trying to do here is. improve that designer developer handoff, right? And a lot of times there's a lot of gap, you know, you go develop, like you said, you develop some beautiful artifact and then you hand it off to the developers and they're like, okay, uh, what do I do with this? And how does it behave? And you missed a million things and bugs in code are obvious, but bugs in design often are not. Um, so is that one, like, do you think design technology helps to identify bugs in the design as well? **Guest - Kyle Crouse:** 100 percent and gaps. Um, I think that that was one of the bigger things where, where we kind of started was, you know, you, especially before like some of the more modern tools came online that allowed you to be more, um, \[00:11:00\] interactive in your design. Um, you dealt with just flat files, right? Like photoshop comps and things like that. And as, um, you moved away from like traditional web pages that were just like one page, one page, one page more, kind of like almost like print layout. And you thought about web applications and how things moved and transitioned and like going from one state to the next, um, and, and more discreet components, um, the design technology. was really vital there because you could stitch together those different flat artifacts, um, as references and have a deliverable that highlighted those transitions and that overall, like, statefulness of the application, right? So that you could have a much more robust, uh, deliverable while not having to overextend, you know, the, the, the skills and time required of the design team. **Host - David DeRemer:** I mean, you mentioned that it's like it's changed a lot, right? I mean, back in the day, you're right. Early on, it was just Photoshop. I mean, people were designing early iPhone apps and, uh, and things like that in Photoshop back in the day or Adobe Illustrator or those types of tools. And obviously the tech \[00:12:00\] has really come a long way of tools like Figma and other things where you have all this capability to develop clickable prototypes and, you know, Design to code solutions and things along those lines. Um, but I think we, we all still feel like there's oftentimes a real need, not just to just have that sort of clickable prototype that you need a kind of more fully formed experience to really validate the design or really learn from it. Um, so I know you've had probably a ton of these examples from your time at frog method or, or Amazon other places, but are there like any specific examples you could talk about where. The, the ability to really do this, that you had the, the client, um, the budget, the, the mandate, or the, at least the, uh, uh, the permission to build something real to validate the design, um, do you have any examples where that really proved to be vital to the success of the product? **Guest - Kyle Crouse:** Yeah, one really stands out to me that was really a comprehensive effort, um, was while we were working at Method, um, working for McDonald's. And it was around the time Method was working on design of a new mobile application that was meant to kind of globally \[00:13:00\] serve, McDonald's across, uh, almost every country that they operate in. And as a part of designing this application, um, we started working with the restaurant innovation team to understand what is the digital to physical transition when a, uh, McDonald's customer, uh, starts, uh, creates like an order or starts approaching the property with some kind of intention to take a digital behavior into the physical world. Um, and we, uh, answered that question through many, uh, rounds of, uh, physical prototype testing, um, and doing lots of research that started out, um, very, uh, um, uh, paper prototype, kind of, uh, uh, quick and dirty around, like, creating totally fake drive thru screens with applications that didn't work, that just had like comps and things like that, whatever would answer all these questions. Because what we first needed to add, uh, to understand was, um, there are many different types of McDonald's in the world. Um, and from one country to the next, uh, customers engage with McDonald's in different ways. And they have different expectations of what happens there. You know, in \[00:14:00\] some countries, McDonald's can almost be considered, is almost considered like a morphology. Um, and then you also consider, um, you know, the formal, like, night out kind of dining experience, where you go and you sit there for a long time and, and you, you  occupy your table and you, yeah, no, it is, but it's like, it operates differently, right? In some countries, this was kind of, before it was a bigger thing here, um, but in some countries, it was all about delivery. Um, you know, and then you also consider, um, you know, All the different ways that you can engage with McDonald's, whether it's going to the drive through going up to the front counter, um, you know, curbside was a nascent offering at the time, um, and considering like table service and things like that. So you had all these different ways that a customer could just appear within a McDonald's environment and expect their food,  um, or from an ordering perspective. Um, so, you know, we. sat down and first tried to identify it. Well, what are the technologies that we could potentially bring to the restaurants that would enable this handover? What are the pros and cons of each, um, in terms of cost, ease of implementation, ease of like fleet management, ease of integration with the app, customer experience, crew experience, like all of these different things, and you create. Um, all of these really rapid prototypes, um, that will \[00:15:00\] allow you to test out all of these different types of configurations, some more or less functional than others, um, and you, we created, you know, like a, a fake drive thru board and had people drive around a parking lot and pretend that they were using the drive thru and testing this app and we had like crew scripts that we would read out as if we were actual McDonald's people where you can basically get to an assessment to say like, These are all the impacts of these different technology and UX experiences on drive thru, on in store, um, et cetera, et cetera. Formulated down to, like, what's your plan, so that we've kind of eliminated a lot of the, like, trial and error around, like, big technology investment to really be able to hone in on what's gonna work for the different types of configurations that would exist country to country. Then, once we kind of have that Simplicity. Then we can invest a little bit more to do a higher fidelity round of prototyping. Right? So we actually built a mobile application. We connected it very lo fi connected it to, uh, the, uh, in restaurant. APIs \[00:16:00\] to submit. Uh, orders, deals, whatever, into the restaurant. Um, and then start taking people through a live restaurant. Um, going through the drive thru, going through the ordering, having them do all different kinds of things, having them experiment with many different types of user interfaces, different methods for checking in, etc. Where, you know, By spending a week on site run running, you know, five or six customers through an hour of trials every day. We can very quickly identify this technology is not going to work in the restaurant for these reasons. This UX is not going to work in the restaurant for these reasons and and say, and I and narrow it down to solutions that we have a lot of confidence, um, if we were to invest in a production, build and roll out of this technology and this design. it will be successful. Um, and then we iterate over that, you know, uh, for a couple of months through different kinds of tests, getting more refined and more refined and more refined to a point where a crew playbook is written, restaurant functionality is developed, mobile functionality is developed, and then it ships in \[00:17:00\] the application and you have a successful mobile check in, you know, that you don't need to go and do a lot of amending to, you know, you've proved out and gotten to the point that it works well. Um, **Host - David DeRemer:** designers do some research, design something, do some user research again, um, and then just fund a development of something and you better hope it's right. Right. I mean, if it's, if it's, if you missed a key detail, uh, some of these things you unpacked, like, were there some, well, maybe not some key details, but you know, it's, it's interesting to think about like, what are the, uh, The things you learn from having a real experience for people to interface with, um, to validate your design versus just a, you know, focus group or some sort of online tool or, or something like that, you know, people don't give the same answers a lot of times. **Guest - Kyle Crouse:** being able to iterate on it in the real world without just, you know, kind of coming up with comps and putting, you know, ideas in front of people and asking them how they would, how they would behave or how they would use it, you know, having it functional in the real world, um, allowed us to see the like, nuance in interaction, um, and, and difference in impact to the overall experience in business at a much more granular level and allowed us to \[00:18:00\] iterate much more quickly, um, to, to have a higher velocity overall to get to the end point that we needed, um, without having to, um, you know, reset the whole process and do all this guess, guesswork and like, You know, be uncertain if something will work or not, go with a gut feeling, right? We can actually prove it out and generate the data that we needed, um, to, to get where we needed to get. You know, and it's like you're talking about like, one interaction would, uh, have like a five second difference on drive thru. Um, overall time and drive through, and that doesn't seem like a lot, but when you stand up those experiences side by side and you see a difference like that, that's like thousands of dollars a day to a restaurant in something like that, right? So, like being able to prove it out at that little bit of detail, um, and generate data around that has a huge return on investment. **Host - David DeRemer:** Not only that, but customer experience too, right? No one likes to sit there for five seconds, even five seconds, right? Um, it, it's, uh, when it could take one or less, uh, and you have that, you're sitting there waiting, should I advance? Should I move up? What should I do? You know, **Guest - Kyle Crouse:** Yeah, well, you know, we can even see that by like by running through a very, you know, diverse customer base through all these things we can \[00:19:00\] start to identify things like, you know, there are certain things that people are more inclined to say than not that we want to lean into. There are certain, um, uh, like if we give them a code. Um, there's certain letters and numbers that we can kind of reduce it down to that will remove any like language challenges, you know, as you communicate over sometimes not a very great quality speaker to someone on the other end and things like that. So like having real people go through all of this stuff and then be able to iterate super quickly. I mean, we make, you know, changes day to day, uh, in the mobile application to try out an entirely different scenario the next day. Um, so yeah, it's very, very quick. **Host - David DeRemer:** Yeah. Yeah. What were some things about those environments? You know, what, like, again, this sort of the, to the question of, of the environments you've worked in, um, are there certain like cultural, Behaviors that teams need to be able to be effective with this type of approach. Um, like what kind of collaboration do you need between different disciplines and how might that be different from what a lot of like large organization product teams might be used to. **Guest - Kyle Crouse:** It's really helpful when the organization, um, I think first and foremost appreciates the customer experience and that, that, that \[00:20:00\] making sure that, um, what they're providing to their customers is, um, something that is going to be as friction free as possible and as delightful as possible. Trying to eliminate frustration, right? And, and if you have a, a client that's really invested in what they are giving to their customers, that's a big start because you can ground your conversations in what's going to create value for the customer, what's going to create delight, um, uh, for them. And then. You know, it's, it's, it's added bonus on top of that. If it's a client that can recognize the value that design brings to the table to answer those questions, um, to generate data and validate hypotheses that get you to a solution that you have confidence in implementing so that you're not just. Going and running off and building, um, without data, um, that talks about how customers would use something, how they would feel, uh, about something, um, \[00:21:00\] that allows you to craft something and bring the data to the table that will influence how something gets implemented, that you can then take those requirements to a build and say, like, we know that this is what we want to put together and then let's go do that. And you can just have more confidence that what you come out with the other side is not something you're going to need to go make like a huge new reinvestment in because you like miss the mark or something like that, right? Um, it just gets you there faster. **Host - David DeRemer:** Yeah, so I'm curious, like, obviously that's a huge company, this example you're giving, and they have tons of locations, and you would think of the, the cost of getting it wrong would be really, really high. Um, but I think these, these ideas and these approaches are useful for any company, even a two person startup. What would you say to like the early stage company or, or, or a larger company that's like, ah, we don't need to pay for this. This sounds really expensive and let's just like, you know, we got smart designers, smart developers, and we know our business really well. Um, you know, how do you sort of like get around that, you know, how do you kind of illustrate this value? I think the things you're saying are like long, long-term value. Like, hey, you're gonna really de-risk your future and you gotta invest a \[00:22:00\] little now to do that. Is that really the case? Or what are their business results? Um, would stakeholders be able to look at here that would help, you know, unlock new value or justify an investment. **Guest - Kyle Crouse:** I think that's really where the where the opportunity is, is, you know, if you have the patience for what an investment in this type of work is going to provide for you long term, you're going to get like compounding value on that as you go through this process, because you've you might have spent a little extra effort initially. to really understand what it is you need to do and validate that it's the right thing to be doing. But you can have that confidence so that when you then go into the next phase and you start putting it together, it's not really an unknown. You, you have all this data that confirms that you're going in the right direction. You're not going to have to undo things that you, uh, that you did. Um, you can spend less time on, uh, uh, Like iterating and trying to get it right and more time on \[00:23:00\] growing. Um, and because you've already started with that really solid foundation. Um, and then, you know, specifically from a design technology practice, I think you also accelerate that initial handover right into knowing that what you might ship, uh, From a code perspective is going to be more robust, um, more elegant, um, more capable because you've had this transition from design through engineering and you can really focus on providing like quality code quality for an end, um, you know, and, and I think in the end it all adds up to, uh, maximizing that return on investment so that you have more opportunity to grow in the longterm and maximize your, your, your revenue and things like that versus, you know, sinking it into trying to like fix the things that you didn't get right the first time. **Host - David DeRemer:** Yeah. And get that delightful user experience too. Right. I mean,  I mean, I think that's, well, that's it. Let me ask you about that one. Cause you said that a couple of times earlier and. You know, I think people talk about user experience and the importance of it and the quality of it. And I think, um, a lot of times when we're talking about design, we're trying to get that right. Um, and that we, we inherently feel like there's good value in safety. You know, obviously there's real value, like saving \[00:24:00\] five seconds at scale. That could, that's a lot of time, a lot of money. Um, but what, what is delightful? Like what is a delightful user experience? Like how would you actually define that? And what is the real value of that? **Guest - Kyle Crouse:** Yeah. I mean, to me, delightful is, um, doing something that giving, giving a user something that saves them a few seconds in their day. without frustrating them. Um, you know, if, if I can, if I can make someone's life through some kind of digital product or experience a little bit better, happier, less painful, then that's success. That's delight, right? Like they don't have to like use something and have like a giant smile on their face. That'd be great if they did, you know, but at the same time, it's like, if you do something and you think, Oh, that was easy. Or if you just don't even think about it at all, then like, It's worth it to put that effort in because it doesn't end up with a negative connotation, **Host - David DeRemer:** I think that's super key. I think so many companies, they focus on like, we need some, we need to win a design award, right? Like we need some flashy UX that Apple will celebrate or something like that. And I think you're right. Like realistically, like, you know, you want to get in and out. I think I've always appreciated Uber's approach to design in that sense where they're \[00:25:00\] like, It's almost like their metric is minimize the time in app, you know, like I want to open it, get my car and that's it. And then next time I open it, that's when I rate and add a tip or whatever. And like, you're not trying to keep people in it's exact opposite. I think a lot of people focus on, we have to have some beautiful design, you know? And I think that's an important, really strong call out that sometimes it's just like making it more efficient, making it so you're like, huh, I didn't have to waste too much extra time doing that thing I needed to do. **Guest - Kyle Crouse:** yeah, it's really about getting out of people's way, right? Like, meet them where they are, and then get out of their way. Um, and I think that that's, that's where you really have a lot of opportunities to, engage with people and make them feel good about an experience that they're having is if you don't, uh, if you aren't making things difficult for them, right? It's like, look at like, um, things that, People complain about a lot, um, you know, different experiences that they have a really hard time with. And there's always some kind of like really complicated friction associated with that thing that's like a huge pain point that just makes it difficult on people. And if you can like remove those little tiny points of friction and just make it overall better for them, you save them some time to go focus on something that they want to be focusing on versus like making \[00:26:00\] them slog through whatever to like deal with, you know, whatever you've given them. **Host - David DeRemer:** Yeah. Yeah. Yeah. You mentioned when we were talking the other day about this, so like sort of The role of, of thinking about the customer and these experiences as well. And sort of the, the confidence to push and test and prototype. And I think that's also part of it. I think a lot of companies. Or teams wanting to start something, they're afraid to put something out as a prototype. They're afraid to like, engage real customers with something that's not like, totally perfect or done. Um, and you had this comment, I think about like, they will never know what you didn't do or couldn't do. Um, and I was just wondering if you could like, elaborate on that a little bit more in terms of like, how to think about how to engage technology and design in an early prototyping phase and have the confidence to test with real users. **Guest - Kyle Crouse:** Yeah, I think, you know, sometimes, uh, going through the prototyping process helps you narrow in your focus. Um, and maybe you have, like, some great idea, um, or some, like, really cool functionality, and you would love to be able to work that in, and you would love to be able to provide this experience or that experience, but your kind of prototyping process allows you to Assess those things in a \[00:27:00\] way of like, well, how feasible is this going to be? What value is it going to give me? And if, if it becomes too difficult or it's not even possible to do this kind of thing, then like, well, that's a really bummer. We'd like to be, we would have liked to have been able to done that. Let's hold on to that thing, you know, and keep it in our, in our backlog. And maybe the technology becomes more available or our systems become more capable  or customer acceptance is higher, uh, in the future for this kind of behavior. And let's focus on the things that we know would meet our, uh, criteria. Because while we might be disappointed, we couldn't have necessarily shipped a particular thing, we can still provide a thing, you know, and we've kind of proven that we can provide a thing that will get the job done and make people happy about it. And they'll be fine with that because they're happy. They're not going to be disappointed necessarily that they didn't get the great thing because they never even knew the great thing was something we were considering, you know, but we shipped them the thing that works and that made their lives better. And that's the important thing. **Host - David DeRemer:** Yeah, I think that's super important. I think if people are looking for that great thing and, um, executives push for that, designers and product people push for that.  You know, I think there's a healthy \[00:28:00\] tension that comes for that. You want to push, but you're right. You know, you gotta realize that only, only we know the people making the thing, uh, all the deliberations we had or the things that we talked about doing or could have done, no one externally knows about those things and you can't get hung up on that stuff. I think it's a really good insight as you think about building a product. Um, what other like insights have you collected over the years that you have stuck with you and that you kind of have taken forward with you in your career as you continue to grow? **Guest - Kyle Crouse:** Well, I think, you know, even back to what you were just saying, I think that that's where really the, the, the generating the success, um, you know, as you work through a product like this, I think, you know, that's where it's really important is to say like, you know, By going through these prototyping processes, you can, uh, and validating feasibility and desirability of these different things. You're like guaranteeing more opportunity for success, um, in how you're going to ship these things because you're not. Designing some pie in the sky feature that can't be done. And then, you know, internally, everyone is upset that they couldn't make it happen. Engineering teams miss marks and people start, you know, uh, uh, \[00:29:00\] flailing, trying to make things happen or something gets said out in the world and everybody's disappointed that you never met those marks, right? You know, by, by putting in that extra effort, you can kind of shape What success is really going to be with confidence. Um, so that you can, you can really get there. And I think that, you know, that's being able to establish the right expectations, um, and, and, and generate that confidence is like one of the biggest things I think you can really do when you're working with clients to like, get products out in the world. **Host - David DeRemer:** That's, that's really, I mean, I hadn't really thought about that exact angle before about the influence that building something early in the process has on the, on the output. Because if you're going to get to build that automatically is a constraint on the experience because you have limited amount of time and it takes extra time to build something. Right. And so  I've seen this in my career, working with  especially, especially a startup team, small team where the founders are really design oriented. You know, in the absence of having a technical partner, what do they do? They iterate the design. They just keep like adding features. They keep twiddling, like twiddling things around, tweaking things around. Um, I don't think twiddling is the right word, but you get my point. Um, and, uh, and before you know it, you have this like \[00:30:00\] design that is like version seven and it's got all these things and you hand it to an engineering team and they're like, this is going to cost like a million bucks. You know, it's so much stuff. Um, but if you inject a little technology early on, Like you're automatically going to put a little bit of a throttle on your design execution ideas in some ways, right? Because you, you, you need to think about testing the features that you're already working on, not the next one. You know, um, and I think that's a, a good compelling way to think about it. Not to say that you're limiting your creativity. That's not what I'm trying to say, but it's like, it forces you to test your assumptions and the things you've already been thinking through before you just move on to the next feature or the next designs concept. Right. **Guest - Kyle Crouse:** I think it's about being clear eyed around, you know, what, uh, what's, what's, what's possible. Um, you know, and, and it's like, we do ideation all the time where we just come up with as many things as we can throw at the wall and, and try and synthesize that down into, you know, interesting ideas, um, that we could potentially try, um, either as a new product or as a part of a product or a greater service offering or experience, whatever it may be. Yeah. Um, part of that process is to use, you know, our expertise \[00:31:00\] in technology and the current landscape and user behaviors and all of that stuff to kind of categorize those ideas in order to understand how to interrogate them so that you can say, you know, this is a near-term idea. This is something that people know how to do. I have the capabilities to pull this off. It's technology that exists. We could consider this. Much closer in if we wanted to, you know, on the other hand, you know, we have our far, far out ideas that are, you know, maybe we need, uh, to build out some other systems on our side. Um, we need X, Y, and Z services to come online. Maybe we're still trying to like get customers used to a particular new behavior that maybe this is something that's like two to three years out on our roadmap before we really get there. Maybe something is totally speculative whereas like 10 years from now. When, like, these technologies align and we have this kind of customer experience, this is what we can enable, right? And it becomes more of, like, a vision, um, something that you can work towards. But in all of those buckets, you are, you know, using that expertise you've acquired through, um, understanding users and, and technical abilities \[00:32:00\] and client needs and all that stuff to, add that correct lens to everything. Um, you know, it's essentially, like, you know, like prototyping. The ideas, you know, without actually building them out to figure out where they could potentially come into play. **Host - David DeRemer:** Nice. Yeah. Makes sense. Um, so this is great. I mean, we're getting close to our time here, so I just wanted to see if you could bring us home and. Just ask you if, uh, if you had advice for people building digital products out there, it's a complicated world. There's a huge variety of scale and different, you know, platforms and ways that you can build these things. But someone out there who's, you know, maybe working on a team and trying to be successful in their career with their company and with their product, like, do you have any advice based on the experiences you've had in your career and in your life, um, for those people who are looking to maximize their, their chance of success? **Guest - Kyle Crouse:** I mean, I think the thing I would say is like experiment early and often and put things in front of real users as often as you possibly can to get feedback, you know, show them different versions, whatever. Try and validate, um, As soon as possible, um, to before you \[00:33:00\] get too far along in a process, um, and kind of end up doing something that you can't undo. Um, I think the more you can do that, the more you set yourself up for success long term. Um, and then I would say from more from a practitioner's perspective, you know, Be curious and don't sacrifice quality, right? Like, I think that you always have to be asking questions. Why this? What about this? How about that? Etc. Right? Like, just be constantly interrogating stuff to like, you know, find the right avenue, poke holes in things, test them. You know, it's almost like asking those questions or like trying to like prototype something before you've even prototyped it. Um, and then, you know, Don't give up on aspects of the user experience because they're really important and like, don't sacrifice that quality because that's what really differentiates is delivering something that gives customers something that is going to provide that delight that we talked about earlier. Um, **Host - David DeRemer:** Um, I have one more question for you. Um, have you, uh, given all the, the topic of this conversation, have you had any experiences recently, like a, a, an app or digital product, or even like a real world experience where you're like, damn, like, this is really great. Like \[00:34:00\] clearly like a really smart team of designers, design technologists, engineers looked at this. Um, anything come to mind of just an example of that? **Guest - Kyle Crouse:** well, to toot our own horn, I'm really happy with an internal app that we've been working on around some race data and visualization. And I think that there's been some very smart people on our side that have been putting that together. And I'm really excited to someday be able to kind of generate some content around that to talk about AI and things like that. Providing better user experiences through, um, you know, more natural inputs and things like that. It's super exciting space, really looking forward to work there. Um, I think if I was going to call out a third party thing, um, I'm I just, uh, picked up, uh, an app called Copilot, uh, like around personal finance, um, management, like, uh, kind of along the lines of like what, you know, Mint and things like that tried to do back in the day, but I think that there's some really elegant design pieces in that, um, that try to simplify a lot of what, uh, makes someone like me that is terrible at it a lot better at managing their own personal finances and things like that. I think there's a lot of elegance in that. It's in the application and a lot of great functionality um, and it's brought in a lot of, uh, uh, smart capabilities under \[00:35:00\] the hood. You know, the thing that I really like about it, um, that I try to bring into my work is that, um, you don't often see is that, I think the technology kind of recedes a little bit into the background. Um, and I think that that's really important because you don't want to like hammer people over the heads with like a technical capability. You kind of just want to like use it to get the job done and be out of there. Right. Like if they never even see the technology, that's great. Um, and I think that this app does a good job of that, of, of, of kind of putting that stuff in the background and really maximizing the value to the user. Yeah. **Host - David DeRemer:** technical. Um, cause I, but I think it's, it is the same idea that we've been talking about here, which is, um, I got this bag recently, just a, you know, a travel bag for my laptop and stuff by this company called air. I might've shown it to you last time we got together and it's like, there's this pocket on the front where you can put like your keys or your, you know, Pack of mints or something like that. And I took the bag off and I like realized that it was weird to like get to the zipper. And as I'm trying to use this for the first time, there's just a little flap of nylon, just this little flap. And it was like perfect to grab it. So you could have a posing force to open the zipper. And I was like, this is the type of thing, right? That you need, you need to test with it. You need to see someone \[00:36:00\] actually use the bag because a designer or a product person would be like, um, you know, Oh, this works. It should work fine. Or maybe like, ah, the engineer would be like, ah, some extra stuff costs more money. Like every little piece we add to this costs a bunch of money, but seeing real user behavior where they probably had a prototype of that and someone struggled to open that zipper and you're like, Oh, we got to add this little, you know, little flap, you know, and I think it doesn't like it's obvious in a physical product. Um, cause you can like have a visceral reaction. You can observe someone do it, but I think the same things apply whether you're building like a drive through experience or a mobile app or something like that. You need to observe real people and all the myriad ways that people find freaky, bizarre, strange ways to use your product, right? Um, to react to those and improve the product along the way. So, um, **Guest - Kyle Crouse:** Yeah, well, it's an attention to detail that's really important, you know, and it's like you recognize that that there's something there. Um, you don't just skip over it. You, you give it the, the appropriate attention and you put the little extra investment in it to make it, to make it worthwhile. That's the, that's the **Host - David DeRemer:** Yeah. And despite all the research we might do, how much we know our business or our company, how much we think we know our end user and our customer. \[00:37:00\] The reality of it is, is like, we probably don't know them as well as we think we do. And you got to get real people to test it out. So, um, well, Hey man, this is awesome. Really appreciate you taking some time here today to do this. Um, it's really exciting to think that I get a chance to do this with you after all these years, uh, here we are doing this, um, appreciate you coming on and I'm just really psyched that we get to hang up this call and go back to keep working together. So that's pretty awesome. Um, all right.  **Guest - Kyle Crouse:** man. Enjoyed it. **Host - David DeRemer:** Yeah, I appreciate it. Thanks very much. Catch you soon. **Guest - Kyle Crouse:** Yep. All right. I had to resist the urge to hang up there. ## Whitepapers ### The Enterprise Advantages of Flutter Development Source: https://verygood.ventures/whitepapers/building-enterprise-apps-with-flutter/ ### Unlock Enterprise App Development with Flutter This cost-effective, multi-platform framework from Google ensures scalability and success with its thriving community, simplicity of use, and ability to create powerful user experiences, aligning with best practices and reliable methodologies for impactful advancements in the future of your app and organization. ### How companies are reaching quality while delivering faster  Across industries and around the world, organizations have realized that their ability to connect with audiences is directly tied to the success of their mobile applications. To reach the widest segment of users while keeping development costs low and not overtaxing your teams, organizations are embracing Flutter as their platform of choice. ## A Deep Dive into Flutter's Impact ### Empowering both code and teams The Very Good Ventures method of building apps helps you scale both code and teams. By focusing on architecture, testing, configuration, automation, and the use of the rich Flutter ecosystem, our best practices unlock positive outcomes for development teams. ### Efficient and smart One development team instead of multiple unlocks productivity. Rather than putting in twice (or more) the work to create parallel codebases, companies are concentrating their resources on unified, efficient, and effective development teams. ### Accelerating time to revenue The Flutter ecosystem now hosts a thriving network of users with access to 25,000 packages that may save your team considerable manual work. And leading enterprises across industries trust VGV and Flutter. > Businesses are delivering app updates on a faster and more responsive cadence, all without running into issues such as over‑complex codebases and excessive tech debt. {% hubspot_form id="21eaedb2-f052-426e-b182-46aa7b29a8c7" /%} ### Discover the Business Value of Flutter Source: https://verygood.ventures/whitepapers/business-value-of-flutter/ ### Here’s how to accelerate the delivery of digital products and directly impact business outcomes. The past 20 years have been challenging for companies when offering digital touchpoints, compounded by multiple mobile operating systems, as well as web, desktop and embedded systems. Flutter allows companies to break out of this arms-race and reach their entire target market at once and launch with more feature-rich products. This whitepaper will lead you to the reasons why you should switch to Flutter to fully scale your products. ## How Flutter can scale your business ### Build faster, build better The time to launch a product to market with Flutter was reduced to 62% of the time it took when compared to working with multiple teams on native applications.By switching to Flutter, you will be delivering better products with vetted features, faster. ### Increase adoption + revenue opportunities Using Flutter means bringing revenue forward by launching a product sooner:You will be doing it within a few months earlier to the full addressable user base, pulling revenue forward for your company. ### Improve customer value and satisfaction The engineering team’s greater bandwidth, coupled with Flutter’s extensive libraries and tools, results in applications that work well and create greater value for users. There isn’t that typical lag between launching iOS apps and Android, meaning all customers feel equally valued. > In order to grow, and to be successful in the market, you need to provide great products with delightful customer experiences that meet and exceed the expectations of your customers. And this is where Flutter truly excels. ### How Agentic AI Systems Drive Scalable Business Impact Source: https://verygood.ventures/whitepapers/ebook-agentic-ai-systems/ Discover what Agentic AI is, how it works, and how to design enterprise-ready Agentic Systems that can perceive, reason, and act autonomously. This eBook provides a clear framework for Agentic AI architecture, key use cases, and the guardrails needed to deploy multi-agent and autonomous workflows safely and at scale. ## Agentic AI Systems: The Ultimate Practical Framework for Designing Autonomous Systems ### How Agentic AI Systems Drive Business Leverage & Scalable Impact Agentic AI isn’t just another trend—it’s a fundamental shift in how organizations design products, workflows, and decision-making systems. But autonomy without architecture leads to chaos, and hype without process leads to failed pilots. This eBook breaks down **what it actually takes to build Agentic AI Systems** that make useful decisions, act independently, and scale responsibly across your business. Whether you’re exploring agent-driven workflows, intelligent automation, or personalized user experiences, this guide gives you a proven framework to move from experimentation to enterprise-grade impact. ### What Is Agentic AI? **Agentic AI** refers to autonomous systems that can **perceive context, reason toward goals, and take action** without step-by-step human control. Unlike traditional automation, which follows rigid rules, Agentic AI adapts, decides, and executes tasks within defined guardrails—making it ideal for complex workflows and dynamic environments. ### Why They Matter Now Products and operations can no longer rely on static workflows or linear decision trees.  Before designing any Agentic AI system, teams must answer: - **What should the agent actually do?** - **How do we ensure reliable, useful decisions?** - **What guardrails must exist for safety and trust?** This eBook shows you how to design for autonomy without losing control. ### Designing Agentic AI for Trust & Control AI is moving fast—and the companies that thrive will be those that turn Agentic Systems into real, scalable advantages. Here’s what this eBook will help you uncover:  - **What Agentic AI Systems Really Are—And What They Aren’t:** Learn the difference between simple automation and true agentic autonomy. Explore single-agent vs. multi-agent models and how they simulate teams, not just tasks. - **Business Foundations for Agentic Success:** Agentic AI Systems aren’t features—they’re capabilities. You’ll get a decision framework for scoping outcomes, defining ownership, and aligning with measurable business goals. - **Designing Agentic UX for Trust and Transparency:** Autonomous behavior introduces new UX challenges. This guide outlines principles for clarity, control, reversibility, and trust. - **Data, Infrastructure & Retrieval: The Core of Agentic Intelligence:** Most agent failures stem from poor data readiness, not poor AI. Learn the essential systems needed for context, memory, and real-time decisioning. - **Organizational Readiness & Cultural Alignment:** Agentic AI Systems require cross-functional ownership, clear policies, and a feedback loop for continuous improvement. We break down how to structure teams for success. - **Technical Architecture & Orchestration Patterns:** Explore the three layers every agent needs (perception, reasoning, action) plus orchestration models, observability standards, and policy layers. - **Proven Agentic Use Cases:** See patterns that consistently drive ROI, including workflow acceleration, content intelligence, and real-time personalization. - **Risks to Avoid (and How to Mitigate Them):** Misaligned autonomy, poor explainability, brittle integrations—learn the common pitfalls before you encounter them. - **How to Get Started: Workshop → Prototype → Validate:** A practical roadmap for turning an idea into a safe, high-impact Agentic AI System. Use this framework to build autonomy intentionally—and unlock real business impact. ### eBook: Enterprise Tech Teams and Flutter Source: https://verygood.ventures/whitepapers/ebook-enterprise-tech-teams-and-flutter/ High-performing teams want to get ideas to market fast. Leading companies like MarketWatch, Betterment, and V1 Sports are ditching the native tax and building for multiple devices from a single codebase with Flutter, unlocking company-wide efficiency along the way. This ebook shows you how. ## How Flutter can scale your business ### Save time and effort Target \*every\* screen with Flutter - mobile, web, desktop, embedded, in-vehicle ### Improve collaboration Unleash efficiency with an intuitive language, customizable widgets, and sharable libraries and assets ### Portable code & teams VGV training, best practices, and tooling help you standardize design and development > Flutter is a framework for building apps. But also for building high performing tech organizations. Every time we accelerate development, we make better products and put them in the hands of users faster. {% hubspot_form id="874f3200-a96c-43ae-82d1-87c7063ecc70" /%} ### eBook: Introducing Very Good Start Source: https://verygood.ventures/whitepapers/ebook-introducing-very-good-start/ Very Good Start is based on Very Good Ventures team's best practices, taken from years of experience across multiple projects and industries. Our Very Good Start offering is accompanied by optional hands-on support from our team and allows your internal development team to get to the ‘fun part’ of a project right away. Learn how it works! ## With Very Good Start you will gain access to: ### Accelerated Development & Strong Foundations Get started fast with a robust, scalable and Bloc-based architecture that enables immediate UI and feature work. ### 100% Code Coverage A codebase that starts with 100% code coverage to ensure you have a great starting point and code to learn from. ### Enhanced Features & Automation Efficient, time-saving automation combined with scaffold features that offer robust, comprehensive support. > Very Good Start uses the latest version of Flutter and all related packages, so your starting point is always up-to-date and optimally compatible. Turning your app concept into a finished product has never been a lighter lift for your internal team. ### eBook: Migrate from Native to Flutter Source: https://verygood.ventures/whitepapers/ebook-migrate-from-native-to-flutter-the-vgv-way/ There are a few signs that indicate a change is needed in your team’s app development approach. Are features taking longer and longer to deploy? Are your applications getting out-of-sync across platforms? This ebook shares battle-tested best practices to successfully shift your apps and teams from native to Flutter. ## How Flutter can scale your business ### When is the right time to switch to Flutter? Some signs it might be time to switch: If app features are taking longer to deploy or apps are getting out of sync across platforms. Embracing a mobile-first approach to app development or toward modernizing your stack are also prime times to migrate to Flutter. ### Benefits of team transformation As engineering teams and practices are consolidated and optimized around consistent best practices, the way that other teams operate can be radically redefined and optimized as well. ### Get started the right way Our experience working with dozens of enterprises has allowed us to build Flutter migration paths that are both low risk and high impact. > Deciding to make the move to Flutter puts your goals in sight: one development team, one app, and features rolling out so quickly that the sales team can’t even keep up! {% hubspot_form id="a845633d-e784-4598-92f9-d53741c557b8" /%} ### Software Development Trends Report 2024 Source: https://verygood.ventures/whitepapers/software-development-trends-report-2024/ ## Webinars ### Full Stack Efficiencies Source: https://verygood.ventures/webinars/full-stack-efficiencies/ ## Unlock Capabilities with a Unified Technology Stack! View this webinar replay of a Very Good roundtable as full-stack experts deep dive into the world of efficiencies with a unified technology stack! ### In this webinar, you'll gain: - Expert insights from industry leaders who've seen firsthand the transformative power of a unified stack. - Discover the business & technical benefits of building with a single language. - ✨ The power of Dart & Flutter: Learn when to make the switch for ultimate developer & product efficiency. - ✅ Real-world success stories: Hear from CTOs, Founders & Engineers as they share their journeys. ### Don't miss this opportunity to: - Uncover the secrets to building high-performing applications. - Boost your development team's agility and productivity. - Learn how to leverage a unified stack for long-term success. > "We're not going back to anything else anytime soon." {% hubspot_form id="0c2baffe-dd33-4028-9ab9-19cb6fba90e5" /%} ### Maximizing Organizational Success Source: https://verygood.ventures/webinars/maximizing-organizational-success/ ## Unleashing Flutter's Value Across Industries Picking the right framework or technology for your product is vital. But that is just the first step: Getting into the route of what your organization needs to face and how you can leverage technology to assist you in that, is also key. Lauren Henry, VP of Engineering at Tide, guided by Jorge Coca, Head of Engineering at Very Good Ventures, shares her experience and approach to the initial challenges that led to exploring and adopting Flutter in companies like BMW and Tide, and the processes that came with it, including internal shifts and external expectation management. > “There was excitement about reducing the time spent in managing multiple operating systems, and expanding globally by having one codebase we could scale to multiple markets faster.” ### Building the right thing, the right way Lauren talks about real experiences and challenges faced by companies like BMW and Tide. Discover how Flutter’s efficiency, innovation, and flexibility positively impacted both organizations. ### Learn from Experts Capture insights from industry leaders who have successfully implemented Flutter in the automotive and financial sectors. Get a sneak pick on how BMW tackled complexities across regions, brands, and platforms with Flutter, enhancing functionality and design updates. Lauren also shares how Flutter provided Tide with a global platform, supporting specific market needs and regulatory integrations. ### Organizational Transformation & Cultural Shift Learn how Flutter adoption can transform organizational performance from operational and financial standpoints. Explore the excitement and innovation a new team and technology can bring to organizations by being focused and enabling rapid response and connection, plus the significant impact of Flutter on personal growth and career opportunities. {% hubspot_form id="12867546-4ac0-44a2-93f1-251a4d473c66" /%} ### The Business Benefits of Flutter Source: https://verygood.ventures/webinars/maximizing-your-business-value-with-flutter/ ## How to Maximize Your Business’s Potential with Flutter Are you facing challenges in developing cost-effective, high-performance mobile apps that reach wider audiences across platforms? Or, not sure how this change will impact your company? VGV CEO, David DeRemer talks with Sam Moore, VP of Architecture of Betterment, a financial advisory company, to discuss how Flutter has impacted their organization and the decisions Betterment made to achieve success. > "It was 33%+ faster than we thought we could deliver things" ### Dive into the backstory and the impact Flutter has had Sam shares how Betterment started its journey into improving its mobile app development and how Flutter was as much a business decision as it was a technology one. ### Gain insights into Flutter adoption Learn how Flutter's single codebase approach reduced development time and costs, while delivering native-looking apps for iOS and Android. Sam and David discuss key challenges and solutions in mobile app development and how Flutter has addressed them. ### Hear real world, long-term business success Sam shares concrete examples and unexpected benefits of how Flutter has delivered tangible business value for their teams and efficiencies -- opening up room for new improvements and possibilities. {% hubspot_form id="fadfc23f-af4f-415e-8f7c-ec33409eed43" /%}