Skip to content

36. Constrained devices and networks

The question. How does this app change for cheap phones and poor networks, and does it change at all?

Most teams build on recent, expensive phones and fast, unmetered networks. A large share of the world’s users have neither. Their phone has a fraction of the memory and processing power, its storage is small and nearly full, its operating system is several versions behind, and its network is slow, intermittent and paid for by the megabyte.

Two of the seven constraints (Chapter 2) meet here at their extremes. Limited resources stops being a matter of tuning and becomes a matter of whether the app runs. Chapter 35 describes the budgets, and on a constrained device every one of them is a small fraction of what the team is used to. Unreliable network stops being an interruption and becomes the normal state. Chapter 13 describes how a client survives a poor network, and here the poor network is all there is.

A third constraint, slow release, appears in a specific form. Old devices cannot install new operating system versions, so supporting those users means supporting old platform capabilities for years.

The cost of ignoring the problem is concrete. An app that is too large is never installed. One that needs too much memory is ended by the operating system as soon as the user switches away. One that assumes a fast network shows a spinner and nothing else. For a product whose growth depends on these users, the choice of how to serve them is a product decision as much as an engineering one, and the result is visible in the store before you install anything.

There are five approaches. They are not exclusive, and a large product often uses several.

Approach 0. One app, no adaptation. The same behavior everywhere. This costs nothing to build. On a constrained device the app is slow or unusable, and the team may not know, because its measurements come mostly from users for whom it works.

Approach 1. One app that adapts. The app measures the device and the network at run time, places each in a class, and changes its behavior by class: fewer animations, smaller images, no prefetching, features turned off. There is one codebase and one store listing. Every adaptation is a branch that has to be tested, and the app’s size stays the same for everyone.

Approach 2. A separate small variant. The maker publishes a second app with its own listing, built for small size and low memory. It has fewer features, simpler screens and stricter data use. It shares the backend and the user’s account with the full app. The cost is a second client to build, release and keep in step, and a product that differs between users.

Approach 3. A variant that moves work to the backend. A small variant taken further. The client becomes a thin display, and rendering, compression or processing that the full app does on the device is done by a backend service. Pages are laid out or compressed on the backend, media is transcoded to exactly what the device needs, and heavy features such as filters or recognition run remotely. The device does less and the client is very small. The cost moves to the backend and to latency: every interaction needs a round trip, on the very network that is least able to provide one.

Approach 4. Fallbacks outside the app. For some tasks the answer on a constrained device is not the app. A text message, a voice call or a simple page in the browser reaches a user whom the app cannot. Section 36.9 covers these.

ApproachApp sizeWorks on low memoryWorks on a poor networkCost to the teamSignature
0 No adaptationFullPoorlyPoorlyNoneSame behavior everywhere, and failure at the edges
1 One adaptive appFullYes, with reduced featuresYes, with reduced qualityBranches to testBehavior differs between devices and networks
2 Small variantSmallYesYesA second clientA second listing in the store
3 Backend does the workSmallestYesNeeds a connection for everythingBackend load, latencyLittle works offline. Output looks processed
4 Fallback outside the appNoneNot applicableYesA second channel to runText messages or calls carry the task

The choice between Approach 1 and Approach 2 turns on install size. Adaptation can change everything an installed app does, and nothing about the download a user must complete before the app runs once. When size itself is the barrier, a separate variant is the only answer. When the barrier is behavior, a single adaptive app avoids splitting the product.

Figure 36.1 shows the components of an adaptive app. A small variant uses the same parts with fixed, low settings in place of some run-time decisions.

Figure 36.1. An adaptive app. Three inputs feed one policy, which sets limits for the components that spend resources.

The policy has three inputs, and they differ in how often they change.

  • The device class is decided once, at first launch or at each launch. It does not change while the app runs.
  • The network class changes from minute to minute and is measured continuously.
  • User settings change only when the user changes them. They include the device’s data-saver mode and low-power mode, and any setting the app offers itself.
function adaptationFor(device, network, settings):
p = remoteConfig.limitsFor(device.class) // tuned without a release
if network.class is poor or settings.dataSaver:
p.imageQuality = low
p.autoplay = off
p.prefetch = off
p.loadMediaOnTap = settings.dataSaver
if network.class is offline:
p.readPolicy = cacheOnly
if device.freeStorage is low:
p.cacheBound = small
p.downloads = askFirst
return p

An explicit user setting ranks above a measurement. A user on a fast network who has turned on data saving is telling the app something about cost that no measurement can detect. The reverse does not hold: a measured poor network reduces quality even when no setting asks for it.

The request to the backend carries hints: the class of the device, the class of the network, and whether the user asked to save data. With these the backend can return fewer items, smaller media addresses or a simpler screen. This is where adaptation becomes cheap for the client. One response field replaces a branch in the app.

A device class is a coarse bucket, commonly three: low, middle and high. The client assigns it from properties it can read at run time.

InputWhat it predicts
Total memoryHow much can be cached and how many screens can stay alive
Number and speed of processor coresWhether animation, decoding and on-device models hold up
Screen size and densityThe largest image worth downloading
Operating system versionWhich platform capabilities exist
Age of the hardware designA summary of all of these
Whether the system marks the device as low on memoryA direct statement from the platform

Some designs skip the properties and use a table kept on the backend that maps each device model to a class. This is more accurate, because it can be corrected from real performance measurements (Chapter 34), and it needs a default for models the table has never seen.

A third option measures instead of guessing: the app times a small piece of work at first launch and derives the class from the result. This catches a fast phone made slow by age, heat or a full disk.

AreaOn a low class
MotionAnimations shortened or removed. Blur, shadows and transparency replaced with flat fills
MediaLower resolution. No autoplay. Fewer items decoded at once
ListsSimpler rows. Smaller pages (Chapter 15)
MemorySmaller memory cache. Fewer screens kept alive under the current one
FeaturesHeavy features hidden or replaced: camera effects, on-device models, live previews
ProcessingWork moved to the backend that a high class does on the device

Features are turned off through remote config and feature flags keyed by class (Chapter 30, Remote config and experiments). That lets the team move a model of phone from one class to another without a release, after the measurements show it struggling.

From outside, the class is invisible on one device. It becomes visible only by comparison: the same account, on the same network, on two devices of different capability. A difference in animation, image sharpness or available features that follows the device and not the account is the signature. P36.8 runs that comparison.

Chapter 13 covers how a client learns the quality of its network and what it changes in response. This section adds what is specific to networks that are poor all the time.

Type is a weak signal. The operating system reports whether the connection is wireless or cellular and which cellular generation. A crowded wireless network can be far slower than a good cellular one. A design that keys on type alone gives full quality to the first and reduced quality to the second, which is backwards.

Measurement is the strong signal. The network estimator in Figure 36.1 times the app’s own recent requests and tracks throughput and failure rate. It costs nothing extra, because the requests were being made anyway. Its weakness is the first request of a session, for which there is no history. Designs start from the last class seen on this network, or from a cautious default.

Cost is a separate axis. A metered network may be fast and still expensive. Speed decides what the app can do. Cost decides what it should do without asking. A design that treats the two as one either wastes a user’s money on a fast cellular connection or starves a user on a slow unmetered one.

The class must not flap. A network that hovers at a threshold would switch quality back and forth. The estimator therefore changes class only after the measurement has stayed on the far side of a threshold for some time, and it moves down faster than it moves up.

Network classTypical behavior
Good and unmeteredFull quality, prefetching, autoplay
Good and meteredFull quality on request, limited prefetching, autoplay by setting
PoorLow-resolution media, text first, no prefetching, longer timeouts
OfflineCache only, writes queued where the feature supports it (Chapter 12)

On a poor network the order in which things arrive matters more than the total time. Text is small and carries most of the meaning. A design that sends text and layout first, and media afterwards, gives the user something to read within a second or two. A design that waits for everything shows a spinner for the whole duration.

Text first needs three things. The text and the media must come in separate requests, or the text must come first within one response. The request queue must rank text above media (Chapter 13). And the layout must reserve the right space for each image before it arrives, so that the text does not jump when the image appears.

Media then loads in stages. A placeholder occupies the space. A very small version of the image follows, often a few hundred bytes carried inside the first response and drawn blurred. The full image for the display size comes last, and on a poor class it may be a lower-quality rendition, or it may not load until tapped. Chapter 16, Images, covers the renditions and the loading sequence. Chapter 17, Audio and video playback, covers the equivalent for video.

The device has a data-saver mode that applies to every app. Many apps add their own. This chapter calls the second kind an in-app data saver, to keep it apart from the device setting. It exists because the app knows what its own data goes on and can offer finer choices.

ControlWhat it changes
Media qualityThe rendition requested, sometimes per network type
AutoplayNever, on unmetered networks only, or always
Load images on tapImages become placeholders with a size label
Automatic download of attachmentsBy media type and by network type
PrefetchingOff, or on unmetered networks only
A data use screenA count of what the app has sent and received

The granularity of these controls tells you about the internals. A single switch means one flag read by the adaptation policy. Separate choices by media type and network type mean the policy already reasons in those terms. A screen that counts data used means the network client meters its own traffic.

A data budget goes one step further. The app holds itself to a quota, for example an amount of prefetching per day on a metered network, and stops optional work when the quota is spent. This is invisible except in the totals over time (Chapter 35).

Showing the cost before spending it is a related design. A download control labeled with its size, or a video that states its size before it plays, hands the decision to the user. An app that does this was built for users who count megabytes.

A small variant is defined by its size limit, and everything else follows from it.

  • Bundled resources. Fewer images, fonts and animations in the install. More is drawn with plain shapes and system fonts.
  • Features. The long tail of features is left out. What remains is the core loop of the product.
  • Third-party libraries. Each library adds size. A small variant carries few.
  • On-device processing. Media editing, recognition and ranking move to the backend or are dropped.
  • Motion. Transitions are minimal.

Some small variants are a thin shell around web content. The shell supplies the icon, notifications and storage, and pages supplied by the backend supply the screens. Chapter 32, UI technology: native, cross-platform and web, covers how to tell.

The account, the data and the backend are the same. A message sent from the small variant arrives in the full app. This is what you can establish most firmly: sign in to both with one account, and the same data appears. It follows that the backend serves both through a compatible API, and that the differences between the two are differences of client only.

A small variant is also not merely the full app with a flag set. It has its own listing, its own size, its own version numbers and its own release dates. Those are all visible in the store, and P36.1 and P36.2 read them.

Approach 3 moves the heavy steps across the network.

  • Rendering. The backend lays out a page or compresses it into a compact form and the client only draws it. A scroll may need a new request, and nothing works offline.
  • Media. The backend transcodes each image or video to exactly the device’s screen and the current network class, more aggressively than the full app accepts. Images look visibly compressed.
  • Processing. Recognition, translation, filters and search run remotely. The feature shows a wait and needs a connection where the full app answers at once.

The observable difference is where waiting happens. In the full app a filter applies instantly and works in airplane mode. In a backend-processing variant the same filter shows progress and fails offline. Running one feature in both, offline, separates the two designs with an Inferred claim.

36.8 Low storage and old operating systems

Section titled “36.8 Low storage and old operating systems”

A nearly full device is a normal condition for the users this chapter concerns. It causes failures of a kind that fast phones never show.

  • Writes fail. A local store that cannot write may refuse new data, or in a poor design, corrupt what it has. A sound design checks free space before large writes and handles a failed write as an expected result.
  • The cache disappears. The operating system reclaims cache space, so the app refetches what it had, spending data the user also lacks.
  • Updates cannot install. The user stays on an old version for want of space (Chapter 33, Releases and compatibility).
  • Downloads stop part way. A resumable transfer continues later. A simple one starts again.

Designs for low storage keep the install small, bound every cache tightly, and scale the bound to free space. They give the user a storage screen inside the app that lists what is kept by kind and size, with controls to remove each. That screen is the clearest signal available: a team builds it only when its users run out of space. Some apps also offer to move downloads to removable storage where the device has it, or to delete media already backed up.

Do not fill your own device to test this. A full device can lose data in other apps and fail to take photos or receive messages. The probes read the app’s storage controls, and observe low-storage behavior only if your device is already in that state.

Each app states a minimum operating system version. The store listing shows it, and it is a direct published statement of how far back the team supports.

PolicyWhat a user on an old device sees
Low minimum, one appThe current app installs. Newer capabilities are absent or replaced
High minimumThe app is not offered, or the store says it is incompatible
Last compatible versionThe store offers an older version of the app, which no longer updates
Small variant with a lower minimumThe full app is not offered and the variant is

Supporting an old version means every use of a newer platform capability is guarded by a check, with a fallback: a simpler widget, no rich notification, a plainer sign-in. Each guard is a branch to test, which is why teams raise the minimum once the share of users on old versions is small.

A user left on a last compatible version continues to call the backend with an old client. The backend must keep serving that API until it retires the version with a forced-update gate, which for these users ends the product, because they cannot update. Chapter 33 covers that decision.

Some tasks must succeed even when the app cannot run or cannot reach the backend. Designs route them through channels with weaker requirements than a data connection.

ChannelUsed forWhy it works
Text messageSign-in codes, alerts, receipts, simple commandsNeeds only cellular signaling, not a data connection
Voice callCodes read aloud, confirmation by an automated callWorks on the oldest phones and for users who cannot read the text
A plain page in the browserTracking a delivery, viewing a ticket, payingNeeds no install
A code shown on screen or printedTickets, pickup, payment at a counterNeeds no network at the moment of use
Transfer between nearby devicesSharing files or the app itselfNeeds no network at all

The most common fallback is for delivery of something urgent. Platform push needs a working data connection and a device the push service can reach. When the backend sends a push and receives no acknowledgment from the client within a time limit, it sends the same information as a text message.

Figure 36.2. A fallback from platform push to a text message when no acknowledgment arrives.

The signature is a text message that arrives a fixed interval after the event and repeats what a notification would have said. If both arrive, the acknowledgment was lost or the design sends both without checking. Text messages cost the maker money for each one sent, so a design that uses them freely has decided that the task is worth it.

P36.1 to P36.3 read the store and the settings and take minutes. P36.4 and P36.5 apply a constraint to the network. P36.6 and P36.7 are passive. P36.8 and P36.9 need a second, older device and are optional.

Open worksheet
Do
  1. Open the store and search for the app by name.
  2. Look at the other apps published by the same maker.
  3. Look for a second app with the same purpose whose name or description says it is smaller, lighter or made for slow networks.
  4. Check the maker's name on the listing so that you do not mistake a copy by someone else for a variant.
Watch

Whether the same maker publishes a second, smaller app for the same product. Some variants are listed only in certain regions, so absence in your store is weak evidence.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

P36.2 Compare the variant with the full app

Section titled “P36.2 Compare the variant with the full app”
Do
  1. Run this only if P36.1 found a small variant.
  2. Read both store listings and note for each: download size, minimum operating system version, date of the last update and the features described.
  3. If you have room, install both and sign in to each with the same account.
  4. Open the same content in both and note what the variant lacks.
  5. Pick one feature both have that transforms something, such as a filter, a translation or a search. Try it in each app in airplane mode, then turn airplane mode off.
Watch

The size ratio, which features are missing, whether your data appears in both, and whether the chosen feature works offline in one and not the other.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Open the app's settings.
  2. Look for controls on data use, media quality, autoplay, automatic downloads or a data-saver switch.
  3. Note whether choices differ by network type or by media type, and whether a screen reports data the app has used.
Watch

How many separate controls exist and how fine their choices are.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. On a cellular connection with the mode off, open a media-heavy screen and note image sharpness, autoplay and whether items open at once.
  2. Turn on the device's data-saver mode.
  3. Force-quit the app, open it and visit a similar screen with content you have not seen.
Watch

Whether images are lower in resolution or wait for a tap, whether autoplay stops, and whether the app mentions the mode.

Caution. This probe changes a device-wide setting that affects every app. Turn the mode off when you finish.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Find a place where the connection is weak but present, such as the edge of wireless coverage or a spot with one bar of cellular signal.
  2. Force-quit the app and open it.
  3. Open a content screen you have not visited, with text and images.
  4. Watch the order in which things appear. Repeat on two or three screens.
Watch

Whether text appears before media, whether images arrive blurred and then sharpen, and whether the app says anything about the connection.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Open the app's settings and look for a storage, downloads or data management screen.
  2. Note whether it lists what is kept by kind and size, and which controls it offers.
  3. If your device is already low on storage and has shown a system warning, also note how the app behaves when you save or download something small.
Watch

Whether the app accounts for its own storage, and, only if the device is already low, whether a save fails cleanly.

Caution. Do not fill your device to test this. A full device can lose data in other apps. Read the controls only, and do not remove anything you want to keep.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Open the app's notification and security settings.
  2. Look for options to receive alerts, codes or receipts by text message or by a call.
  3. Read the help pages for what to do without a data connection or without the app.
  4. Think back over past use. Note any text message from the maker that repeated a notification, and how long after the event it came.
Watch

Which tasks have a channel outside the app, and whether a text message has ever followed or replaced a notification.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Run this only if you have a second, clearly older or cheaper device at hand.
  2. Sign in to the same account on both devices, on the same wireless network.
  3. Open the same screens on both, side by side.
  4. Compare animations and transitions, image sharpness, autoplay, and the list of features on each main screen.
Watch

Differences that follow the device and not the account or the network. Ignore plain slowness. Look for things that are absent or simplified.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

Do
  1. Read the minimum operating system version on the app's store listing and write it down.
  2. If you have a device on an old operating system version, open the store on it and find the app.
  3. Note what the store offers. Install only if you are happy to have the app on that device.
Watch

Whether the store offers the current version, an older version, a small variant, or nothing.

Read

Select what you saw to record it in your worksheet. Select it again to clear it.

SignalDesign it points toConfidence
The same maker lists a second smaller app for the same productA separate small variant on a shared backendObserved
One account shows the same data in the variant and the full appOne API serving two clientsInferred
A feature works offline in the full app and needs the network in the variantThe variant moves that processing to the backendInferred
The variant has a lower minimum operating system version than the full appThe variant is the route for old devicesInferred
Settings offer media and download choices by network typeAn adaptation policy that separates network cost from speedInferred
A screen in the app reports data it has usedThe network client meters its own trafficInferred
Media is reduced when the device data-saver mode is onThe app feeds the device setting into its adaptation policyObserved
Text appears before media on a weak connectionText-first ordering with media ranked lower in the request queueInferred
Images arrive blurred and then sharpenStaged media with a tiny preview, then a rendition sized to the displayInferred
Download controls or videos are labeled with their sizeThe cost is shown before it is spentObserved
Quality follows the network type and not its real speedNetwork class taken from type instead of measurementInferred
Quality flips every few seconds at the edge of coverageA network estimator with no dampingInferred
Effects or features are missing on an older device with the same accountDevice class with features set by classInferred
The app has a storage screen listing sizes by kindThe app meters its own storage and bounds it per kindObserved
A text message repeats a notification some time after the eventFallback from platform push on a missing acknowledgmentInferred
A notification and a text message arrive togetherBoth channels sent without checking for an acknowledgmentSpeculative
The store offers an older version of the app on an old deviceMinimum operating system version raised and old clients left in serviceObserved
Text jumps as images load on a slow connectionLayout does not reserve space because responses carry no image dimensionsInferred

Search the signals from every chapter in the signal lookup.

Adaptation on the client depends on choices the backend offers.

  • Media in many renditions. Media delivery must serve each image and video at several sizes and qualities, including very small ones, and must produce a tiny preview that can travel inside a response.
  • Responses that take hints. The gateway accepts the device class, network class and data-saver state, and services return fewer items, lighter media addresses or simpler sections accordingly. With server-driven UI (Chapter 31, Server-driven UI) the backend can send a different screen to a low class.
  • Remote config by class. Limits and feature flags keyed by device class and by region, and a table of device models where that design is used.
  • One API for two clients. A small variant and a full app share accounts and data, so the API must serve both, and each has its own old versions in the field.
  • Processing services. For Approach 3, services that render, compress or process on behalf of the client, placed close to users so that the added round trip is short.
  • A second delivery channel. For fallbacks, a provider of text messages or calls, delivery acknowledgments from the client so that the backend knows when to fall back, and a rule that prevents sending both.
  • Long support for old clients. Users on a last compatible version call old API versions indefinitely.
  • Measurements split by class. Without performance data from low-class devices and poor networks, the team cannot see the users this chapter concerns.
  • A spinner with nothing else on a slow network. No text-first ordering. The screen waits for one large response or for its media.
  • Text jumps as images arrive. The layout did not reserve space, so the response does not carry image dimensions.
  • Sharp images that take very long on a poor network. One rendition for all networks. The network class is not measured, or not used.
  • Full quality on a slow wireless network and low quality on fast cellular. The class is taken from the network type and not from measurement.
  • Quality switches back and forth every few seconds. The estimator has no damping at its threshold.
  • The app restarts whenever you switch away, on an older phone only. Memory use is not scaled to the device class (Chapter 35).
  • An error or a crash when saving on a nearly full device. Free space is not checked and a failed write is not handled.
  • A notification and a text message with the same content. A fallback that fires without checking for an acknowledgment.
  • A feature of the small variant that stops in airplane mode while the full app’s version works. That feature’s processing was moved to the backend.
  • The store offers an old version that then shows a forced-update gate. The backend retired a version that some devices can never leave.

These are the fields this chapter adds to the teardown worksheet. Record which devices and networks you tested on, since every claim here is a comparison.

Open worksheet
  • Constrained devices and networks turn limited resources and the unreliable network from tuning problems into the question of whether the app runs.
  • There are five approaches: no adaptation, one adaptive app, a separate small variant, a variant that moves work to the backend, and fallbacks outside the app. Products combine them.
  • An adaptive app feeds a device class, a network class and user settings into one policy. A user’s explicit setting ranks above any measurement.
  • The device class shows only by comparing two devices on one account. The network class shows as quality that follows measured speed, not network type.
  • Text first, staged low-resolution media and an in-app data saver are the visible parts of a data budget. The granularity of the controls mirrors the policy inside.
  • A small variant has its own store listing, size and versions, and shares the account and backend. Running one feature offline in both shows whether its work moved to the backend.
  • A storage screen inside the app, a low minimum operating system version and text message fallbacks each show that the team designed for these users.
  • Never fill your own device to test low storage. Read the app’s controls, and observe only what your device already does.