“Everyone assumes AI replaced the need for structure. It didn’t. It just moved where the structure has to live.”

Every few months someone asks me some version of the same question: now that Claude Code and Cursor can scaffold an entire feature from a sentence, do you even still need Mason? Melos? Any of it?
Short answer — yes, and honestly more than before, just not for the reason you’d expect. This one’s going deep, with actual configs and commands, not just vibes. Let’s get into it.
The Problem Nobody Actually Names 🧱
“We’re not fighting a lack of code. We’re fighting a lack of the same code.”
Once a Flutter project grows past one app, the real pain isn’t writing code anymore — AI made that part cheap. The pain is keeping dozens of packages, apps, and feature modules structurally identical to each other over time.
The concrete symptoms, if you’ve lived through this: shared business logic gets copy-pasted between apps instead of shared through a package. UI components drift out of sync — one screen uses one button component, another screen uses a slightly different one nobody remembers building. A bug fix in one app doesn’t propagate to the sibling app that has the exact same underlying bug, because there’s no shared package for it to live in. Versioning shared code becomes its own part-time job once more than two things depend on it.
That’s the actual problem a monorepo setup is solving. Not “how do I write code faster” — “how do I stop dozens of moving pieces from silently drifting apart while everyone, human or AI, writes code faster than ever.”
Three different tools get thrown at this problem right now, and they solve three genuinely different layers of it. Conflating them — “just use Mason” or “just use AI” — is where most of the confusion online actually comes from.
Mason: Deterministic Templates, Not Suggestions 🧰
“Same input, same output, every single time. That’s the whole pitch, and it’s a bigger deal than it sounds.”
Mason is a code generation CLI created by Felix Angelov — at the time he built it, Head of Architecture at Very Good Ventures, later moving on to Shorebird. It’s written in Dart, and it’s the tool underneath a lot of Flutter tooling you’ve probably already used without realizing it: Very Good CLI’s flutter_app, dart_package, flutter_plugin, and flame_game starter templates all run on Mason internally.
The mental model: reusable templates called bricks. A brick is a directory of files using Mustache-style templating — {{name.pascalCase()}}, {{#condition}}...{{/condition}} for conditional blocks — plus a brick.yaml manifest defining the variables the brick needs and how to prompt for them:
name: feature_brick
description: Generates a new feature module
version: 0.1.0
vars:
name:
type: string
description: The name of the feature
prompt: What is the name of the feature?
use_bloc:
type: boolean
description: Include BLoC state management
default: true
Everything inside the brick’s __brick__/ folder gets rendered with those variables substituted in. Run it:
mason make feature_brick
Answer the prompts, and you get a fully generated feature — same file structure, same naming convention, same imports — every time, regardless of who runs it or how tired they are at 11pm before a deadline.
Why that determinism actually matters against an AI agent specifically: a model generates the most probable output given your prompt and whatever context it currently has loaded. That’s genuinely close most of the time — but “close” isn’t the same as “exactly the convention your team locked in eight months ago.” A brick doesn’t have an off day. It doesn’t quietly forget a naming convention because the context window got long this session. It runs the identical deterministic template on generation #1 and generation #4,000.
The full CLI surface is worth knowing, because it’s more than just make:
- mason new <brick> — scaffold a new brick
- mason make <brick> — generate code from an existing brick
- mason add <brick> — register a brick (local path or Git URL) in your project
- mason publish — push a brick to BrickHub.dev for public reuse
- mason bundle / mason unbundle — package a brick into a standalone bundle and back
- mason upgrade — bump registered bricks to their latest versions
Bricks can be shared publicly through BrickHub.dev, or privately via a Git URL pointing straight at your own team’s repo — no need to publish anything publicly to keep a convention consistent across your org:
bricks:
feature:
git:
url: "https://github.com/your-org/internal-bricks.git"
path: bricks/feature
Once a convention is written into a brick, it stops being a wiki page nobody actually reads and becomes the only mechanism for generating that kind of file — the convention is enforced structurally, not socially.
Melos: You’re Not Generating Code, You’re Orchestrating a Repo 🗂️
“Different problem entirely. Mason answers ‘what should this new thing look like.’ Melos answers ‘how do I run one command across forty packages that already exist.’”
Melos is a separate tool solving a separate layer, built and maintained by Invertase — the same team behind FlutterFire. It’s in active production use across FlutterFire itself, Riverpod, and Very Good Ventures projects like the I/O Photo Booth app, which tells you this isn’t fringe tooling — it’s genuinely load-bearing infrastructure across some of the most-used packages in the entire Flutter ecosystem.
What it actually automates:
- Local package linking (bootstrapping) — your app package can depend directly on your feature_auth package by path, with zero need to publish to pub.dev or hand-manage symlinks. Run melos bootstrap and it wires the whole graph.
- Cross-package script execution — melos run test runs your defined script across every matching package in the workspace in one shot. melos exec --scope="core" -- flutter test narrows execution to only the packages matching a filter, which matters once you're running dozens of packages and don't want to re-test everything for a one-line change in one of them.
- Automated semver and changelogs — Melos reads your Git history against the Conventional Commits spec. A commit like feat: add fare calculator gets parsed, Melos determines a feature was added, bumps the minor version automatically, generates a CHANGELOG.md entry, and tags the release — turning what used to be a manual, error-prone release checklist into a single command: melos version.
- Selective CI — because Melos knows the dependency graph, it can determine precisely which packages actually changed in a given PR and scope test runs to just those, instead of re-running the entire monorepo’s test suite on every commit regardless of blast radius.
Something that changed recently and matters a lot: Melos 7.3.0 moved its configuration directly into the root pubspec.yaml — no separate melos.yaml file required anymore. A minimal config now looks like:
name: my_monorepo
environment:
sdk: ">=3.10.4 <4.0.0"
dev_dependencies:
melos: ^7.3.0
workspace:
- apps/*
- packages/*
melos:
scripts:
test:
run: melos exec -- flutter test
And it now integrates natively with Dart Pub Workspaces, the monorepo support that shipped directly into the Dart/Flutter SDK itself starting with Dart 3.6+ and Flutter 3.27+. That’s a meaningful signal about direction of travel: monorepo support is becoming a first-class SDK capability rather than something bolted on entirely by third-party tooling, and Melos has repositioned to sit on top of that native layer instead of working around its absence.
None of this generates a single line of feature code. Melos doesn’t know or care what a widget should look like inside a package. It orchestrates packages that already exist. That’s a fundamentally different job from what Mason does — which is exactly why serious Flutter teams run both simultaneously rather than picking one.
Where AI Actually Fits In — And Where It Genuinely Doesn’t 🤖
“Give an agent a hundred-page CLAUDE.md and it’ll still forget which button component you meant. Give it a brick to call instead, and it structurally can’t get it wrong.”
Here’s where it gets genuinely interesting heading into 2026, and where a lot of hot takes online get the direction backwards.
The naive version of “AI replaces scaffolding tools” assumes an agent freehanding a feature from a prompt is strictly better than a rigid template, because it’s “smarter” and can adapt. In practice, teams running large AI-assisted codebases keep hitting the exact same wall documented across multiple independent write-ups on this problem: you document every convention exhaustively in a CLAUDE.md — component patterns, import styles, naming, error handling, state management, API patterns — and the agent still drifts. A different button component shows up on a settings page than the rest of the app uses. You point it out, it apologizes, generates a new version — wrong again, just differently wrong this time. The math genuinely breaks down: you end up spending more hours fixing convention drift than the scaffolding would have taken to write correctly the first time by hand.
The fix teams are actually converging on isn’t more documentation. It’s exposing deterministic tools to the agent as callable functions, instead of asking it to freehand structure from a prompt every single session. This is happening at the ecosystem level right now, not as a hypothetical:
- Very Good Ventures shipped an official AI plugin for Claude Code (vgv-ai-flutter-plugin) that exposes Very Good CLI — itself running on Mason underneath — as callable MCP tools. The agent doesn't generate a Flutter project structure from imagination anymore; it calls the actual flutter_app, dart_package, flutter_plugin, or flame_game templates directly, the same deterministic bricks a human would run manually from the terminal. The plugin bundles more than scaffolding too — skills covering Material 3 motion tokens and animation decision trees, WCAG 2.2 accessibility conformance across every platform Flutter targets, golden/widget/unit testing patterns with mocktail, and type-safe GoRouter routing via @TypedGoRoute.
- The official Dart and Flutter MCP server gives agents real, live capabilities inside a running project — inspecting actual app state, triggering hot reload, managing packages, live debugging against what’s actually running — instead of generating plausible-looking code blind and hoping it happens to match reality when you finally run it.
- FlutterFlow’s flutterflow ai init does something structurally analogous from the visual-builder side of the ecosystem: it scaffolds a workspace with a defined structure that an agent can then drive and modify, rather than asking the model to invent the entire workspace shape from a prompt with no starting scaffold at all.
The pattern repeating across all three, independently, from different companies: the AI agent becomes the decision layer, not the template layer. It figures out which brick to run, what variables to pass, whether a given feature actually warrants a new package or fits inside an existing one — and then calls a deterministic tool to actually execute that decision, instead of generating the scaffold token-by-token from scratch and hoping consistency holds across sessions.
That’s a meaningfully different architecture than “just ask Claude to build the folder structure,” and multiple independent teams landing on the same pattern is a much stronger signal than any one of them doing it alone.
So What Actually Belongs Where 🧭
“Stop asking ‘which tool wins.’ Start asking ‘which layer is this decision actually happening at.’”
Pulling all of that together, here’s how I’d draw the lines for a Flutter monorepo built in 2026:
Layer Tool Job Doesn’t do Package/workspace resolution Dart Pub Workspaces (native SDK) Single lockfile, shared dependency resolution across the whole repo Doesn’t generate code, doesn’t version packages Repo orchestration Melos Scripts across packages, automated semver + changelogs, selective CI Doesn’t know what a new feature should look like Code generation Mason Deterministic feature/package scaffolding from bricks Doesn’t decide whether you need a new feature Decision-making AI agent (Claude Code, Cursor) Judgment on what to build, which brick actually fits, where state should live Structurally can’t guarantee identical output twice on its own Agent-to-tool bridge MCP servers (VGV’s plugin, Dart/Flutter MCP) Lets the agent call Mason/Very Good CLI directly instead of freehanding structure —
None of these compete with each other for the same job — that’s the actual point I want to land here. Pub Workspaces gives you the native dependency foundation. Melos orchestrates what already exists in the repo. Mason generates new pieces deterministically when something new needs to exist. The AI agent sits on top of all three, making the calls a human used to make manually — which brick, which package, which convention actually applies here — and then executing through the same deterministic tools instead of reinventing structure from scratch every session and hoping it’s consistent with last time.
My Actual Setup 🛠️
“Not theoretical. This is genuinely what’s running the moment I start a new feature.”
For anything beyond a genuinely single-app project, this is the stack, layer by layer:
- Pub Workspaces as the native dependency resolution layer — one lockfile at the root, no manual path-linking between packages, resolved once for the whole workspace
- Melos on top of that for scripts, versioning, and selective CI — melos exec --scope=<package> when I only need to touch one specific package, melos version when it's actually release time and I want the changelog generated for me instead of writing it by hand
- Mason bricks for anything that needs to be generated repeatedly and identically — new feature modules, new internal packages, boilerplate that should never structurally vary between one instance of it and the next
- An AI agent wired to call those bricks through MCP, rather than freehand a folder structure from a prompt every time — so “generate a new feature” actually means the agent picks the correct brick and the correct variables, not that it improvises a plausible-looking structure that quietly drifts from the last five features it generated in earlier sessions
The honest reason this setup holds up isn’t that any single piece here is smarter than the others. It’s that each layer is only ever responsible for the thing it’s actually good at, and none of them are quietly trying to do another layer’s job badly in the background.
The Actual Takeaway 💭
AI didn’t make Mason or Melos obsolete. If anything, it made the case for deterministic tooling stronger than it was before AI showed up, because the exact thing AI is structurally bad at — guaranteeing the identical output twice, under identical inputs, regardless of context window length or which session it is — is precisely the thing these tools exist to guarantee in the first place.
The real shift happening in 2026 isn’t “AI replaced scaffolding.” It’s “AI became another consumer of scaffolding,” calling the same deterministic tools a human engineer would reach for, instead of reinventing the wheel from a prompt every single time and hoping this generation matches the last one.
Pick the right layer for the right job. Stop asking which tool wins. None of them were ever competing for the same job to begin with.
Tags: Flutter, Mason, Melos, Monorepo, AI Coding Agents, Software Architecture, Dart, MCP
