How We Prevent Ads from Interrupting Critical User Workflows

Published: (December 22, 2025 at 05:39 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Why This Problem Matters

In many mobile applications, ads are inserted based on screen placement or timing rules, without considering what the user is actively doing. From an engineering perspective, this creates several problems:

  • The timing of ads, not the specific ads themselves, often disrupts the user experience.
  • Critical user workflows can be interrupted, leading to frustration and reduced engagement.

While building a production mobile application, we observed that the issue wasn’t which ads were shown—it was when they were shown. This led us to implement an interaction‑aware ad suppression system that treats monetization as a runtime decision based on the user’s interaction state. This post focuses on the engineering side of that system.

Tracking Interaction Events

The first step is capturing meaningful interaction signals locally. Examples of events we track include:

  • Screen taps and swipes
  • Form field focus/blur
  • Navigation transitions
  • In‑app purchases or other high‑value actions

Rather than reacting to individual events, we group them into short time windows and evaluate them as sequences. This avoids overreacting to noise while still capturing user intent.

Deriving Interaction States

From these event sequences, the application derives a small set of interaction states, such as:

  • Idle – no active user input
  • Active Input – user is typing or interacting with a form
  • Critical Flow – user is in the middle of a checkout or onboarding step
  • Background – app is running but not in the foreground

Each state has a different tolerance for interruption. For example, during a Critical Flow state, ads are fully suppressed, whereas in an Idle state they may be allowed. This keeps the decision logic explicit and explainable.

Suppression‑by‑Default Logic

A key design decision was to suppress ads by default. Instead of trying to detect every possible “bad” moment, the system assumes ads are disabled unless explicitly permitted by the current interaction state. This approach:

  • Guarantees that ads never appear during high‑risk moments.
  • Minimizes unnecessary UI re‑renders and network calls during critical workflows.
  • Simplifies the decision matrix, making it easier to audit and adjust.

Local Decision‑Making

All interaction‑state evaluation happens on‑device. The client application:

  1. Collects raw interaction events.
  2. Maps them to a state using a lightweight state machine.
  3. Determines whether ads may be shown based on the current state.

Remote services, if used, are limited to providing configuration (e.g., state thresholds) and are not part of the real‑time decision path. This improves latency, reliability, and behavior under poor network conditions.

Lightweight Feedback, Not Over‑Optimization

After an ad is shown (when allowed), the system observes simple signals such as:

  • Immediate user dismissal
  • Click‑through rates
  • Session continuation metrics

These signals are used to adjust thresholds gradually, not to aggressively optimize delivery. The goal is stability and a consistent user experience, rather than maximizing short‑term revenue.

What Worked Well in Practice

From an implementation standpoint, this approach resulted in:

  • Reduced interruptions during critical user flows.
  • Higher user satisfaction scores measured in post‑release surveys.
  • Stable revenue that aligns with genuine user engagement rather than forced impressions.
  • Simpler codebase: the on‑device state machine is easy to test and maintain.

Most importantly, the system aligned monetization behavior with actual user interaction patterns.

Final Thoughts

Ads don’t need to compete with user workflows. By treating monetization as an interaction‑aware engineering problem, rather than a purely revenue‑driven feature, applications can remain sustainable without sacrificing reliability or trust. Sometimes the best decision a system can make is not acting at all.

Back to Blog

Related posts

Read more »

SwiftUI Gesture System Internals

markdown !Sebastien Latohttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%...