
Matt Brown
August 11, 2026
Part 2: Dash vs. full stack in the AI era
The risks you don't see on day one
Part 2 of 3 in a series on why you should ask Claude to build data apps in Dash instead of full stack.
Part 1 made the architecture case: a Dash app is one application, in one language, with a smaller blast radius when something breaks. This part covers what shows up later, after the app has been in production for a while. Part 3 covers the cost of development, and when full stack is still the right call.
TL;DR: When you ask Claude to build a data app, naming the framework in the prompt matters. For a live walk through, reserve your spot for a comprehensive webinar.
Here's the short version of why that framework should be Dash from the perspective of risk.
- A smaller, checkable dependency tree. A fresh Dash install resolves 32 packages vs. a full stack app which resolves to hundreds of dependencies and sub-dependencies, in an ecosystem seeing growth in malicious and AI-hallucinated packages.
- Easier handoff. Someone else has to read the code eventually. A Dash app can be reviewed by anyone on a Python-fluent data team. A full-stack app asks whoever inherits it to either trust Claude's account of unfamiliar code or learn a new language.
- Dash has a steward. Plotly owns the Python visualization stack and deployment platform: Dash (9M downloads/month), Plotly graphing libraries (62M/month), and on-prem/cloud hosting. No competing visualization stack is as vertically integrated or widely deployed. Full stack has no single company to call when something breaks.
Read on for the details behind each one.
Most vibe-coded apps don't fail on day one. They pass every test that matters at launch: it renders, the charts update, the demo goes fine. The risk shows up later, on a timeline nobody was tracking when the app got built, as a dependency that turns out to be malicious, or a key person who leaves the company, or a laptop-grade prototype that just quietly became load-bearing.

A smaller attack surface
Here's a number worth sitting with. We installed both stacks ourselves. A fresh pip install dash resolves to 1 direct package and 12 total packages installed, Dash and everything underneath it included. Assembling a realistic React equivalent, a Vite app with React Router, TanStack Query, Recharts, Axios, Zustand, and Material UI, the kind of stack you'd actually need to chart and manage data, resolves 12 direct dependencies and 194 total packages. And that's the front end alone. It doesn't include the separate back end service that stack still needs to talk to your data, which a Dash app already comes with. Every one of those packages is a file that runs on your machine or your user's browser, written by someone you've never met.
Direct dependencies
Total installed
React (bare)
1
1
React (full stack)
12
194
Dash
1
27
Full Stack dependency list: react, react-dom, react-router-dom, @tanstack/react-query, recharts, axios, zustand, @mui/material, @emotion/react, @emotion/styled (deps) + vite, @vitejs/plugin-react (devDeps).
It’s worth noting that Dash is built on React. The graphs, dropdowns, tables, and layout pieces you import in Python, are React components under the hood, and Plotly builds them with an npm toolchain: webpack, babel, React itself, and everything underneath those. The difference is where and when the dependency tree gets resolved. Plotly's engineers resolve it once, upstream, and ship the compiled output as a pinned, static JavaScript bundle inside the Python wheel. Nothing on your machine touches npm, and nothing re-resolves that tree when you or Claude run pip install six months from now. The 27 packages above are the dependencies you and Claude are on the hook for: the ones that get re-resolved on every install, the ones a new hallucinated name could slot into, the ones that show up fresh every time the project changes. It's not that Dash has no npm dependencies, but the dependencies are fewer, curated, and limit your exposure to a direct supply-chain risk, and doesn't compound every time your agent decides to include a new package.
That tree has gotten more dangerous over time. Sonatype's 2026 State of the Software Supply Chain report counted more than 454,600 new malicious open-source packages in 2025 alone, pushing its cumulative blocked total past 1.233 million, a 75% jump year over year. Over 99% of that malware showed up on npm specifically.

Some of the risk now targets AI agents directly. Researchers at USENIX Security 2025 generated 2.23 million code samples across 16 popular models and found that 19.7%, nearly one in five, recommended at least one package name that doesn't exist, with 43% of those invented names reappearing consistently enough for an attacker to register and wait. Security researcher Bar Lanyado proved this by publishing an empty package under a name he'd watched models invent, and it picked up 30,000 downloads in three months. In January 2026, researcher Charlie Eriksen found a hallucinated npm package called react-codeshift, a conflation of two real tools, spreading through 237 GitHub repositories via copy-pasted AI agent skill files, with agents themselves driving the daily download count. (Source: Aikido Security's slopsquatting research, building on the USENIX study.)
No ecosystem gets to sit this one out, and it's worth pointing out that Lanyado's proof of concept ran on PyPI, the same registry Dash depends on. If you know npm well, you might also object that its nested node_modules can hold several versions of the same package side by side, while Python's flatter environment can't, so version conflicts bite harder in Python when they do surface. That's fair. It's also part of why npm's tree grows the way it does: tolerating five different versions of the same dependency across a project avoids forcing a choice, it doesn't eliminate the conflict, and it's a contributor to why a realistic front end stack resolves to hundreds of packages instead of dozens. ERESOLVE errors, npm's shorthand for "these two packages want incompatible versions of the same thing," are common enough to have their own extensive troubleshooting literature. Fewer packages, regardless of ecosystem, means fewer places for these kinds of problems to arise, and Dash's 27 packages instead of React's 194 (plus whatever you’re using as a back end) is a meaningfully smaller number.
The “bus factor” of a vibe-coded app
Most data apps aren't maintained by the person who built them, not for long. An analyst vibe-codes a churn dashboard on a Tuesday afternoon, it works, people start relying on it, and eight months later that analyst has moved teams or moved companies. Someone else on the data team inherits it. Whether it survives that handoff boils down to whether the person who inherits it can actually read it.
This well-studied problem in software engineering, called “the bus factor”, is the minimum number of people who'd have to leave before a project stalls because the knowledge needed to maintain it is gone. A vibe-coded app starts with a bus factor of one by default, whoever wrote the prompts, and the question is how fast that number grows once other people on the team actually need to touch it. It's tempting to assume the answer is mostly about language: a Python analyst inheriting a full-stack app is stuck until they've put in months learning a stack that was never their job. That's not really true anymore. Claude narrows that gap on any codebase. The analyst can point Claude at the full-stack app and ask what a component does, ask it to add a filter, ask it to track down why a chart stopped updating, the same way they'd work through a Dash app. Whoever inherits a vibe-coded project isn't starting from a textbook any more, instead they get an interactive conversation with AI, which is a huge advantage.

What doesn't carry over is the ability to tell whether Claude got it right. Making an edit and knowing the edit is correct are different problems, and Claude is better at the first than the second. When Claude explains a pandas groupby to someone who already works in pandas, they can catch errors if the explanation is subtly off, because they have independent judgment to check it against. When Claude explains a useEffect and a piece of Redux state to someone who has never touched React, they're evaluating Claude's account of the code, not the code itself. If that account is confidently wrong, or the fix quietly breaks something adjacent, there's no independent backstop catching it before it ships to whoever's relying on the dashboard. Inheriting a codebase Claude can narrate to you is a thinner form of ownership than inheriting one you can actually think in yourself, even though Claude smooths over the friction of touching either one. The difference is borrowed confidence versus earned confidence. Confidence built on reading the code yourself travels with you: it holds up on a new bug next year, on an edge case nobody's explained yet, on a day Claude gets something wrong. Confidence built on being walked through unfamiliar code only holds up as long as the walkthrough is accurate and happens to cover the situation you're actually in.
The second thing that doesn't go away is architecture, and it has nothing to do with who or what is doing the reading. A split front-end-and-back-end app still has more seams, more dependencies, and more places for a change to break something adjacent, whether the person making the change is a full-stack developer, a Python analyst working through Claude, or Claude itself navigating the codebase unsupervised. It doesn't shrink just because an agent is available to explain it. More seams means more places for an agent making a fast fix to introduce a mismatch as we covered in Part 1.
Undoubtedly, Claude lowers the cost of touching any codebase, but it doesn't teach the reviewer the finer details of a language they don’t know by default, and it doesn't reduce the number of seams in an app. A Dash app keeps both of those advantages for Python users regardless of who, or what, ends up maintaining it. A full-stack app asks a Python developer who inherits it to either trust Claude's account of unfamiliar code or spend the time to stop needing to, and it hands them more architecture to hold that judgment over while they do it.
Who you gonna call?
Most vibe-coded apps start as a side project. Some of them turn into something a whole team depends on, and at that point "reach for Claude and hope" doesn’t feel like such a great strategy.
This is where the two ecosystems diverge sharply. Full stack has no commercial steward. There's no single company you can call when you need SOC2 compliance, on-prem deployment behind your firewall, or a support contract with a human on the other end. You're either self-hosting on borrowed infrastructure or shopping around for a third party who didn't build the framework and doesn't control its roadmap.

Dash has Plotly behind it, and Dash Enterprise turns "the app Claude built me" into something with a real deployment story: one-click publishing, private model support so your data never leaves your network, and an actual team to call when it matters. That's a tremendous value for anyone whose vibe-coded project outgrows a laptop.
Up next
Everything in this post has been about risk exposure: what's sitting in your dependency tree, who can actually maintain the app, whether anyone's picking up the phone when it breaks. But what about the cost difference between AI development using Dash vs. full stack? Part 3 gives us an early look at what building the same app in Dash versus a traditional full stack actually costs in tokens and the counterpoint of when we think full stack is still the right call.
Want to learn more? Join a live walk-through of how Dash compares to full stack.