Automated crawlers struggle with reactive state machines. Modern Compose apps need state-aware testing, or a clean scan report becomes false confidence.
Fireland Labs · 25 October 2026
Let’s be fair first. Automated scanning earns its place. It is fast, repeatable, and cheap to run on every build.
It is good at known-pattern issues: outdated dependencies, weak transport configuration, missing hardening flags, obvious misconfigurations. If you are not running it, start. Nothing in this article argues against it.
The problem is not the tool class. It is what we assume a clean report tells us.
Many scanning approaches were designed around a simple model: an application is a set of pages or endpoints, and a crawler can discover them by following links and submitting inputs.
Jetpack Compose apps do not work that way. The UI is a function of state. What the user sees, and what the app allows, depends on who is signed in, what has loaded, what was selected a moment ago, and what the last response said.
In a typical Compose app, a screen is less a destination and more a rendering of the current state. Navigation is often state too. Some states only exist after a specific sequence, with specific timing and specific data.
A crawler that explores screens will see the screens it can reach. It has no reason to know the ones it cannot.
Imagine a subscription app with a premium feature gated by a state holder. The gate is checked when the screen is first shown. A later state transition, say a plan change followed by a retry after a failed request, leaves the feature visible while the backing check is no longer enforced.
Nothing here is exotic. Each step is normal product behavior. The weakness lives in the combination, and the combination only occurs in one ordered path through the state model.
A page-by-page scan can visit every screen in that app and report nothing. The state that matters was never presented to it.
These are the typical failure modes: logic mistakes, authorization checks that live in the UI layer instead of being enforced consistently, and error paths that nobody exercised. They tend to surface in exactly the flows that carry trust: payments, account changes, personal data.
The business consequences are rarely dramatic on day one. They are quieter:
Deal friction. A buyer’s security review asks how the app was tested, and “we run an automated scan” invites follow-up questions.
Trust and churn. Users do not read scan reports. They notice when access or data behaves in ways they did not expect.
Roadmap risk. Findings that arrive late, near a launch or a diligence process, get fixed under pressure and push other work out.
None of this means the scan was wrong. It means the scan answered a narrower question than the one a founder or CTO is asking. “Is the app free of known patterns?” and “Can the product be misused through its own logic?” are different questions, and only the first one is easy to automate.
A clean scan is useful information. It is not evidence of coverage for state-dependent flows.
Illustrative model, not a prediction. Content to follow.
Customer impact (mechanisms): qualitative only, for example trust loss, churn, support load. Pending review.
Investor impact (mechanisms): qualitative only, for example deal friction, timeline risk, gating fixes. Pending review.
Regulatory impact (high level): applicable frameworks and risk category to be confirmed. Not legal advice, and not a compliance certification.
Assumptions: to be listed.
State-aware testing starts from how the app actually behaves, not from a list of screens. At a high level, that means three things.
Map flows and states. Identify the journeys that matter and the states they pass through, including the unusual ones.
Trace data through state holders. Follow sensitive data and decisions from the source, through ViewModels and state, to what is rendered and what is enforced.
Test gating and error paths. Check that route and feature gating hold across transitions, retries, failures and restores, and that enforcement does not depend on the UI alone.
For fixes, the same lens applies. Enforce decisions where the data lives, not only where the screen renders, and keep state models small enough that you can reason about every transition.
Automated tooling still helps with the known-pattern layer. The state-dependent layer needs someone who understands the architecture and can reason about it.
Three questions help separate shallow testing from the real thing. A fourth helps with what you receive at the end.
How do you cover state-dependent flows in Compose?
Do you model user journeys and state transitions, or only crawl screens?
What evidence do you provide for logic and authorization findings?
How do you label what was tested, what was assumed, and what was out of scope?
Good answers are specific and mention flows, roles and evidence. Vague answers about “full coverage” are worth a follow-up.
If you want a fast, high-signal view, start with the Security Health Check. It focuses on the few flows that matter most before your next milestone and gives your team a prioritized plan to ship and verify.
Have a question about your product? Leave your email and we’ll get in touch within 1–2 business days.