2026-09-18 · 10 min read
Responsibility: Drawing the Lines Between Layers Before You Regret It
The specific kind of mobile bug that seems impossible until you notice it always traces back to the same root cause: nobody decided which layer of the app was actually responsible for what.
There's a specific kind of bug every mobile developer eventually meets: you fix something in one screen, ship it, and a completely unrelated screen three taps away breaks. Nobody touched that screen's code. Nothing about it changed. And yet it's broken, and the only clue is that it happened right after your unrelated fix went out.
This isn't bad luck, and it isn't usually a bad developer's fault either. It's what happens when responsibility was never actually assigned; when no one decided, early on, which layer of the app owns which decision. The bug isn't really in the screen that broke. It's in the boundary that was never drawn.
Responsibility, the second lens in CRAFT, is about drawing that boundary on purpose, before your codebase draws it for you by accident.
What "Responsibility" Actually Means Here
This isn't about who on your team is responsible for what feature. It's about which layer of your app is responsible for which kind of decision and, just as importantly, which layers are explicitly not.
- Should your UI know how data gets fetched, or just that data exists and needs displaying?
- Should your data layer understand what a specific screen plans to do with a result, or just return it and stay out of the way?
- Should validation logic live next to the input field, inside a shared use case, or scattered across both because nobody picked one?
Every one of these questions has a reasonable answer. The problem isn't picking the wrong one; it's not picking at all, and letting the answer become whatever felt convenient in the moment for each individual feature.
A codebase without clearly assigned responsibility doesn't look broken. It looks like ordinary code, right up until the day two unrelated features turn out to secretly depend on each other.
What It Looks Like When Responsibility Isn't Assigned
The tell isn't always dramatic. It usually shows up as a slow accumulation of small, reasonable-seeming decisions that quietly work against each other.
- A widget starts making a small API call directly, because it's just one call and setting up a proper data layer for it felt like overkill.
- A validation rule gets duplicated between the UI and a use case, because someone wasn't sure which one "owned" it.
- A piece of business logic ends up inside a state notifier because that's where the data already was, even though nothing about that logic actually belongs to state management.
None of these decisions look wrong in isolation. Together, they produce a codebase where nobody, including the person who wrote it six months ago, can confidently say what would break if a specific piece changed. That uncertainty is the real cost. It's not that the app doesn't work. It's that changing it safely requires re-reading everything nearby, every time, because the boundaries were never trustworthy to begin with.
When you're about to add logic somewhere, ask one question first: "if I moved this piece of code to a different layer entirely, would anything outside it need to change?" If the honest answer is yes, that logic has a dependency it shouldn't have, and that's exactly where responsibility is leaking.
A Simple Way to Draw the Line
You don't need a formal layered-architecture diagram to start applying this. A useful, low-ceremony version looks like three questions, asked about any given piece of logic:
-
Does this decide what to show, or how to get the data to show it? Those are two different responsibilities; display logic and data logic, and conflating them is the single most common source of the "unrelated screen broke" bug.
-
Does this know about a specific screen, or could it be reused by a screen that doesn't exist yet? Logic that's quietly coupled to one screen's specific needs isn't really shared logic, even if it's technically callable from elsewhere. Naming that honestly prevents a false sense of reusability.
-
If I deleted this piece and rebuilt it from scratch, would I put it in the same place? This one cuts through sunk-cost thinking. Code often lives where it does because that's where it was easiest to write at the time, not because that's where it belongs.
None of these questions require you to have read a specific architecture book. They require slowing down for thirty seconds before writing a line of logic, which is a much lower bar than most developers assume "doing architecture properly" requires.
A codebase without clearly assigned responsibility doesn't look broken. It looks like ordinary code, right up until the day two unrelated features turn out to secretly depend on each other.
Why This Pays Off Beyond the Bug You're Avoiding
Fixing the "unrelated screen broke" problem is the most visible payoff of getting responsibility right, but it's not the only one. Code with clear boundaries is also the code that's actually testable, because a use case that doesn't know anything about widgets can be tested without spinning up a UI at all. It's the code that's safe to hand to a new developer, because the boundaries tell them where to look without having to trace the entire app first. And it's the code that survives a rewrite of any single layer, because swapping your data source or your state management approach doesn't ripple into layers that were never supposed to know about it in the first place.
None of this happens by accident. It happens because, at some point, someone decided on purpose which layer was responsible for what, and held that line even when the shortcut would have been faster in the moment.
This is one of the areas where working through real examples changes how you think about it faster than reading about it does; seeing exactly where a boundary should sit in an actual Flutter codebase, and not a diagram. That kind of walkthrough is what the Engineering Residency's course tracks are built around.
Written by Mobterest Studio: a mobile engineering brand helping developers and founders build better mobile products through architecture-first thinking. Follow along on YouTube for video breakdowns of the concepts covered here.
Want to go deeper?
Free and paid courses, async mentorship, and a beginner's path — everything Mobterest teaches, in one place.
Explore Engineering Residency →More articles
RSS feed2026-09-17·9 min read
Constraints: Why Every Good Architecture Starts With What You Can't Do
Before comparing architecture options, most developers skip the question that actually narrows them down; what are you actually working with? Team size, timeline, and skill level decide more than any pattern does.
2026-09-16·11 min read
The CRAFT Framework: A Mental Model for Every Mobile Architecture Decision
A five-part lens for approaching the decisions that are hardest to reverse in a mobile app (constraints, responsibility, adaptability, fitness, and tradeoffs) before you commit to a direction.
2026-09-15·12 min read
Explaining Mobile App Architecture
Most developers think architecture is about folder structure and patterns. It isn't. It's about the decisions that are hardest to reverse and learning to see them before they become expensive.