Retry Policy in Mobile Development — Essence, Strategies and Principles

Author: IT Sectr Published: 2026-03-11 Reading time: 10 min

Retry Policy is a set of rules that determine when and how a mobile application automatically retries failed network calls. With unstable connections or temporary server errors, a well-designed retry policy improves application reliability without user intervention. According to Google Developer Relations research (2025), proper Retry Policy implementation reduces the percentage of lost requests by 40–60% in mobile applications with frequent network operations.

Key Takeaways

  • Retry Policy is a strategy for automatically retrying requests during network failures or temporary server errors.
  • Exponential backoff is a method of increasing delay between retries to reduce server load.
  • Jitter is a random delay variation that prevents the thundering herd effect.
  • Idempotency is a key requirement for safe retries: a repeated request must not cause side effects.
  • Circuit Breaker is a mechanism for stopping retries during prolonged service unavailability to conserve resources.

What is Retry Policy?

Retry Policy is a software strategy that defines client behavior upon a network request failure: which errors should be retried, how many times, with what delay, and when to stop trying. In mobile applications, a retry policy is critically important due to the instability of mobile networks and possible temporary server-side failures.

A basic Retry Policy includes three parameters: maximum number of retries (maxRetries), initial delay (baseDelay), and the backoff strategy. Additionally, a list of HTTP status codes that should trigger a retry and a timeout for aborting all attempts can be specified.

According to Martin Kleppmann’s book “Designing Data-Intensive Applications”, 50% of failures in distributed systems are temporary and can be resolved with a retry. This makes Retry Policy one of the most effective and inexpensive ways to improve mobile application fault tolerance without changes to server architecture.

Which Errors Should Be Retried

Temporary errors (retriable) are the only type of failure that Retry Policy should respond to. These include connection timeouts (SocketTimeoutException), temporary server unavailability (HTTP 503, 502), and DNS errors. Permanent errors — HTTP 400, 401, 403, 404 — should not be retried, as they indicate a problem with the request, not the network or server.

According to the AWS Architecture Blog, correct classification of errors into retriable and non-retriable is the most important decision when designing a Retry Policy. Retrying a non-idempotent request on HTTP 401 may lead to account lockout, while retrying HTTP 400 may create duplicate data. Always explicitly configure the list of codes to retry.

Basic Retry Strategies

Fixed interval is the simplest strategy: each retry occurs after the same time interval. For example, with a 2-second delay, the application retries the request after 2, 2, 2 seconds. Fixed interval is simple to implement and predictable, but creates uniform server load during mass failures.

Incremental interval — the delay increases linearly with each retry: first retry after 1 second, second after 2, third after 3, and so on. This strategy gives the server more time to recover upon repeated failures, but is still predictable for many simultaneously failing clients.

StrategyDelay FormulaCumulative Time (3 attempts)Application
Fixeddelay = D3 × DSimple scenarios, local timeouts
Incrementaldelay = N × D6 × DGradual load reduction
Exponentialdelay = D × 2^N7 × DMass failures, cloud services
Exponential + Jitterdelay = random(0, D × 2^N)variesHigh load, microservices

The choice of strategy depends on the application nature. For background data synchronization tasks on mobile devices, the exponential strategy with jitter is optimal — it provides the highest probability of success with minimal load on the server and user device.

Exponential Backoff and Jitter

Exponential backoff is a strategy where the delay between retries doubles with each attempt. If the initial delay is 1 second, the delay sequence will be 1, 2, 4, 8, 16 seconds. This gives the server exponentially increasing recovery time.

Jitter is a random delay variation that prevents simultaneous retry requests from multiple clients (thundering herd problem). Without jitter, a thousand clients with the same Retry Policy would retry requests simultaneously, creating peak server load. Jitter spreads retries over time.

Implementation in Kotlin with Coroutines

Kotlin coroutines allow implementing exponential backoff with jitter without blocking the main thread. The retry function from kotlinx-coroutines takes a retry condition and a request body block, automatically managing delays and attempt counts.

kotlin
suspend fun RetryPolicy.executeWithRetry(
    block: suspend () -> Result<T>
): Result<T> {
    var lastError: Throwable? = null
    repeat(maxRetries + 1) { attempt ->
        try {
            return block()
        } catch (e: Exception) {
            if (!isRetriable(e) || attempt == maxRetries) {
                return Result.failure(e)
            }
            val delay = (baseDelayMs * (1 shl attempt))
                .toLong()
            val jitteredDelay = (delay * (0.5 + Random.nextDouble())).toLong()
            delay(jitteredDelay)
            lastError = e
        }
    }
    return Result.failure(lastError!!)
}

The executeWithRetry function takes a lambda with a network call and executes it with exponential backoff and jitter. If the error is not retriable or the maximum number of attempts is exceeded, the function returns an error. The delay is multiplied by a random factor from 0.5 to 1.5 for uniform distribution of retries.

Circuit Breaker and Stopping Retries

Circuit Breaker is a design pattern that prevents endless retry requests during prolonged service unavailability. When the error count exceeds a threshold, the Circuit Breaker transitions to the OPEN state and immediately returns an error without executing the request, giving the server time to recover.

In mobile applications, Circuit Breaker is especially useful when an API is unavailable due to planned maintenance or carrier network failures. Without it, the application would consume battery and traffic on endless retry attempts, degrading user experience and reducing device battery life.

Circuit Breaker State Machine

Circuit Breaker has three states: CLOSED (normal operation, requests are executed), OPEN (failure, requests are blocked), and HALF_OPEN (trial request to check recovery). After a specified timeout in the OPEN state, the breaker transitions to HALF_OPEN and executes one request — on success it returns to CLOSED, on failure to OPEN.

kotlin
class CircuitBreaker(
    private val failureThreshold: Int = 3,
    private val timeoutMs: Long = 30000
) {
    private var state = State.CLOSED
    private var failureCount = 0
    private var lastFailureTime: Long = 0

    suspend fun T.protect(block: suspend () -> T): T {
        checkState()
        return try {
            val result = block()
            onSuccess()
            result
        } catch (e: Exception) {
            onFailure()
            throw e
        }
    }
}

The Circuit Breaker implementation in Kotlin contains an error counter and a recovery timer. The protect method checks the current state before executing the wrapped request and updates the error counter on failures. After reaching the failureThreshold, all requests are immediately rejected until timeoutMs expires.

Retry Policy in Mobile Applications

Mobile networks have characteristics that make Retry Policy especially important. Switching between Wi-Fi and mobile data, signal loss in subways and tunnels, temporary carrier-level blocks — all these scenarios lead to request failures that can be successfully handled by retries.

On Android, the Retrofit library and OkHttp provide a built-in retry mechanism through Interceptor. On iOS, the task is solved via URLSessionConfiguration and custom delegation. For cross-platform development, Ktor (KMP) includes built-in retry support with configurable strategies.

Implementation on iOS with Combine

Combine is Apple’s framework for reactive programming. The retry operator in Combine repeats the publisher a specified number of times on error but does not allow configuring the delay between retries. For a full Retry Policy, a custom combination of catch and flatMap with delay is used.

swift
extension Publisher {
    func retryWithBackoff(
        retries: Int = 3,
        baseDelay: TimeInterval = 1.0
    ) -> AnyPublisher<Output, Failure> {
        return self.catch { error -> AnyPublisher in
            guard retries > 0 else {
                return Fail(error).eraseToAnyPublisher()
            }
            return Just(())
                .delay(for: .seconds(baseDelay), scheduler: DispatchQueue.main)
                .flatMap { self.retryWithBackoff(
                    retries: retries - 1,
                    baseDelay: baseDelay * 2
                ) }
                .eraseToAnyPublisher()
        }
        .eraseToAnyPublisher()
    }
}

The retryWithBackoff extension for Publisher in Combine implements exponential backoff through recursive calls with decreasing counter and doubling delay. The delay operator creates a pause between retries, while catch intercepts the error and decides whether to retry or return failure.

Common Retry Policy Mistakes

The first mistake — retrying requests without checking idempotency. If the server created a resource but did not return confirmation due to a network failure, a retry will create a duplicate. For POST requests, always use an idempotency key (Idempotency-Key) in the header or switch to retrying only GET, PUT, and DELETE.

The second mistake — retrying forever. Always set a maximum number of attempts (3–5 for mobile applications) and an overall timeout for all attempts. Infinite retries drain the battery and create parasitic server load, especially during database migrations or API changes.

The third mistake — ignoring the application context. If the user closed the app or switched to the background, active Retry Policy should be properly cancelled. Use coroutines with SupervisorScope or Combine with UI lifecycle for automatic retry cancellation when the screen is closed.

The fourth mistake — not logging retry attempts. Without logging, you won’t know how many requests were retried, what errors occurred, and how effective your Retry Policy is. Add metrics: retry count, success after retry, delay distribution. This data will help tune optimal strategy parameters for your specific application.

Frequently Asked Questions

How many times should I retry a request in a mobile application?

The optimal number of retries is 3–5 attempts for most scenarios. For background synchronization, 5–7 attempts are acceptable; for interactive requests (e.g., form submission), no more than 3. A higher number of retries does not increase success probability but consumes the user’s battery and data traffic.

What is exponential backoff in simple terms?

Exponential backoff is the doubling of delay between retry attempts: 1 second, 2, 4, 8, 16, and so on. If the server is overloaded, the short pause between the first retries allows it to respond quickly, while the growing pause with each subsequent retry gives the server more time to recover.

Which HTTP status codes should be retried?

Retry only temporary errors: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Errors 4xx (except 408 and 429) indicate client problems — retrying them is pointless and may be dangerous for user data.

How is Retry Policy different from Circuit Breaker?

Retry Policy manages the retrying of a single request upon failure. Circuit Breaker manages the connection state with a service: when errors accumulate, it opens the circuit (OPEN) and blocks new requests. Retry works at the individual call level, Circuit Breaker at the service integration level.

How to test Retry Policy on mobile devices?

For testing Retry Policy, use NetworkInterceptor (OkHttp) on Android and URLProtocol (URLSession) on iOS to simulate network failures. Set parameters: error frequency, unavailability duration, and response codes. Unit tests with MockWebServer (OkHttp) or OHHTTPStubs (iOS) verify retry logic without a real network.

Summary

  • Retry Policy is a strategy for automatically retrying network requests during temporary failures with configurable delay and attempt count parameters.
  • Exponential backoff with jitter is the basic strategy for mobile applications, reducing server load during mass failures and preventing the thundering herd effect.
  • Idempotency is a mandatory condition for safe retrying of non-GET requests: without it, a retry creates duplicate data or unwanted side effects.
  • Circuit Breaker complements Retry Policy by preventing endless retries during prolonged service unavailability and conserving device resources.
  • Error classification into retriable (503, 502, timeout) and non-retriable (400, 401, 403) is critical for correct retry policy operation.
  • Maximum 3–5 retries in interactive scenarios and up to 7 for background synchronization is the optimal value for mobile applications according to Google Developer Relations.
  • Recommendation — implement Retry Policy with exponential backoff, Circuit Breaker, and logging for all network requests in mobile applications.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also