Technical

How to Reduce React Native OTA Update Size by 98%

Learn how differential patching reduces React Native OTA updates from 20MB to 200KB. Technical deep dive into binary diffing, compression, and bandwidth optimization.

S
SwiftPatch Team
Engineering
10 min read

Introduction: The Bandwidth Problem

Every time you push an OTA update to your React Native app, your users have to download it. If you're sending the full JavaScript bundle every time, that's 20MB or more per update — even if you only changed a single line of code.

For a million active users, a single full-bundle update means 20 terabytes of bandwidth. That's expensive for you and slow for your users, especially those on metered mobile connections in emerging markets.

What if you could reduce that 20MB to just 200KB?

That's exactly what differential patching does. In this article, we'll take a deep technical dive into how SwiftPatch achieves a 98% reduction in OTA update size.

How Traditional OTA Works

Most OTA update systems (including the now-deprecated CodePush) use a simple approach:

Traditional OTA Update Flow:
1. Developer makes a code change (fix 1 line)
2. System bundles the ENTIRE JavaScript bundle (20MB)
3. Uploads full bundle to CDN
4. User's device downloads full 20MB bundle
5. Replaces old bundle with new bundle
6. App restarts with new code

The problem is obvious: You changed one line of code, but your users download 20MB. That's like re-downloading an entire book because of a single typo fix.

Real-World Impact

Consider a typical React Native app:

MetricFull Bundle Approach
Bundle size20MB
Daily updates1-2 per day
Active users500,000
Daily bandwidth10-20 TB
Monthly bandwidth300-600 TB
Monthly bandwidth cost$2,500-$5,000
Download time (4G)3-5 seconds
Download time (3G)15-30 seconds
Download time (2G)2-5 minutes

For users on slow or metered connections, a 20MB download is a significant burden. Many will simply not wait and miss your critical update.

How Differential Patching Works

Differential patching (also called "binary diffing") takes a fundamentally different approach. Instead of sending the entire new bundle, it computes the difference between the old and new bundles and sends only that difference.

Differential Patching Flow:
1. Developer makes a code change (fix 1 line)
2. System compares old bundle with new bundle
3. Generates a patch containing ONLY the differences (200KB)
4. Uploads patch to CDN
5. User's device downloads 200KB patch
6. Applies patch to existing bundle to produce new bundle
7. App restarts with new code

The Math

Full bundle approach:
  Change: 1 line of code
  Upload: 20MB
  Download per user: 20MB

Differential patching:
  Change: 1 line of code
  Patch generated: ~200KB
  Download per user: 200KB
  Reduction: 98.99%

The BSDIFF Algorithm Explained Simply

SwiftPatch's differential patching is built on a variant of the BSDIFF algorithm, originally developed by Colin Percival. Here's how it works at a high level:

Step 1: Suffix Sorting

The algorithm starts by analyzing both the old and new bundles using suffix sorting. This creates an efficient index of all substrings in the old bundle, making it fast to find matching regions.

Old bundle: "function login() { return api.call('/auth'); }"
New bundle: "function login() { return api.call('/v2/auth'); }"
                                                 ^^^
                                          Only this changed

Step 2: Finding Matching Blocks

Using the suffix array, the algorithm identifies regions of the new file that match regions in the old file. These matching blocks don't need to be transmitted — only a reference to the old location is needed.

Match 1: bytes 0-35 of new file = bytes 0-35 of old file
  "function login() { return api.call('"
Diff:     bytes 36-38 of new file are NEW
  "v2/"
Match 2: bytes 39-49 of new file = bytes 36-46 of old file
  "auth'); }"

Step 3: Generating the Patch

  1. Control data: Instructions for how to reconstruct the new file
  2. Diff data: Byte-level differences between matched regions
  3. Extra data: New bytes that don't exist in the old file

Step 4: Compression

The patch is then compressed using brotli or zstd compression, further reducing its size.

Patch generation:
  Diff data:    ~150KB (byte differences)
  Extra data:   ~30KB  (new content)
  Control data: ~5KB   (instructions)
  ─────────────────────
  Raw patch:    ~185KB
  Compressed:   ~200KB (with brotli)

Real Numbers: Benchmarks

We benchmarked SwiftPatch's differential patching against full bundle downloads across various change types:

Small Change (1-5 lines)

Full bundle:    20.3 MB
Patch size:     47 KB
Reduction:      99.77%

Medium Change (1-2 files modified)

Full bundle:    20.3 MB
Patch size:     198 KB
Reduction:      99.02%

Large Change (10+ files modified)

Full bundle:    20.3 MB
Patch size:     1.2 MB
Reduction:      94.09%

Major Refactor (50+ files)

Full bundle:    20.3 MB
Patch size:     4.8 MB
Reduction:      76.35%

Key takeaway: For the most common OTA use case — bug fixes and small improvements — differential patching delivers 98-99% size reduction. Even for large changes, it's still significantly smaller than the full bundle.

Impact on User Experience

Download Speed Comparison

NetworkFull Bundle (20MB)Patch (200KB)Speedup
5G1.6s0.02s80x
4G LTE4.0s0.04s100x
3G26.7s0.27s99x
2G267s (4.4min)2.7s99x
Slow WiFi16s0.16s100x

For users on 3G or 2G connections (common in many global markets), the difference is between a 4-minute wait and a near-instant update.

Data Usage Impact

For a user receiving 2 updates per week:

Full bundle approach:
  Monthly data: 20MB x 8 = 160MB per month
  That's significant on a 2GB data plan (8%)

Differential patching:
  Monthly data: 200KB x 8 = 1.6MB per month
  Barely noticeable on any data plan (0.08%)

Impact on Costs

Bandwidth Cost Comparison

For an app with 1 million active users deploying 2 updates per week:

Full bundle approach:
  Weekly bandwidth: 20MB x 1M x 2 = 40 TB
  Monthly bandwidth: ~160 TB
  Monthly cost (at $0.01/GB): $1,600

Differential patching:
  Weekly bandwidth: 200KB x 1M x 2 = 400 GB
  Monthly bandwidth: ~1.6 TB
  Monthly cost (at $0.01/GB): $16

Monthly savings: $1,584 (99% reduction)
Annual savings: $19,008

CDN Storage Savings

With differential patching, you store smaller patch files rather than full bundles for each version:

50 versions with full bundles:  50 x 20MB = 1GB storage
50 versions with patches:       50 x 200KB = 10MB storage

Comparison of Update Approaches

ApproachSizeSpeedComplexityReliability
Full Bundle20MBSlowSimpleHigh
File-level Diff2-5MBMediumMediumHigh
Binary Diff (BSDIFF)200KBFastHighHigh
SwiftPatch (optimized)200KBFastestManagedHighest

SwiftPatch handles all the complexity of binary diffing, compression, and patch application behind a simple CLI and SDK interface.

How SwiftPatch Implements Differential Patching

Server-Side Processing

When you deploy an update:

swiftpatch release --platform ios --description "Fix: cart total calculation"

SwiftPatch's server:

  1. Receives your new JavaScript bundle
  2. Retrieves the previous version's bundle
  3. Runs the optimized BSDIFF algorithm
  4. Generates a compressed differential patch
  5. Stores both the patch and full bundle on CDN
  6. Records metadata (hash, signature, size)

Client-Side Application

When the SDK downloads an update:

1. SDK requests update from SwiftPatch API
2. Server determines user's current version
3. Server selects appropriate patch (current → latest)
4. SDK downloads compressed patch (200KB)
5. SDK decompresses and applies patch to existing bundle
6. SDK verifies integrity (hash check)
7. SDK verifies signature (if signing enabled)
8. New bundle is staged for next app restart

Fallback Mechanism

If patch application fails for any reason, SwiftPatch automatically falls back to downloading the full bundle:

Patch download → Apply patch → Hash verify ✓ → Done
                                Hash verify ✗ → Download full bundle → Done

This ensures updates are always delivered, even if the differential patch encounters an edge case.

Setup Guide

Step 1: Install SwiftPatch

npm install swiftpatch

Step 2: Configure the SDK

import { SwiftPatch } from 'swiftpatch';

SwiftPatch.init({
  deploymentKey: 'YOUR_DEPLOYMENT_KEY',
  // Differential patching is enabled by default
  // No additional configuration needed!
});

Step 3: Deploy

swiftpatch release --platform all --description "Your update description"

That's it. Differential patching is enabled by default. SwiftPatch automatically generates and serves the smallest possible patch to each user based on their current version.

Advanced Configuration

SwiftPatch.init({
  deploymentKey: 'YOUR_KEY',
  patchingStrategy: 'differential', // 'differential' | 'full' | 'auto'
  maxPatchSize: 5 * 1024 * 1024,    // Fall back to full if patch > 5MB
  compressionAlgorithm: 'brotli',   // 'brotli' | 'zstd' | 'gzip'
});

Conclusion

Differential patching is not just an optimization — it's a fundamental improvement in how OTA updates work. By reducing update sizes by 98%, SwiftPatch delivers:

  • Faster updates for users (seconds instead of minutes)
  • Lower data usage (critical for metered connections)
  • Reduced bandwidth costs (save thousands per month)
  • Higher update adoption (users don't skip small downloads)
  • Better global reach (works on slow networks)

Every byte matters on mobile. SwiftPatch ensures you're only sending the bytes that changed.

Get started with SwiftPatch for free →

Ready to ship updates faster?

Get started with SwiftPatch for free. No credit card required.

Join Waitlist

Related Articles