The Moment When Your Update Crashes the App
It's Friday night, 11:47 PM. You've just fixed the last bug and proudly push the update. "Finally done," you think. You close your laptop and head to bed.
Saturday, 8:00 AM. 47 missed calls. 283 new emails. 4.2 stars dropped to 1.8. Your "small bugfix" has made the app unusable for 50,000 users.
Sound like a nightmare? This is reality for many developers, and the reason I want to talk about the most important thing that can happen to me as a self-employed Flutter developer: Patch Rollbacks.
What is Code Push Anyway?
Before we talk about rollbacks, we need to understand what Code Push is. Traditionally, an app update works like this:
- You change code
- You build a new version
- You upload it to the App Store
- You wait 1-3 days for review
- Users have to manually update
With Code Push (also called OTA updates), it looks different:
- You change Dart code
- You push a patch
- Users receive the update immediately on next app launch
No App Store review. No waiting time. No manual update action from users.
Shorebird: Code Push for Flutter
While React Native had Microsoft's CodePush for a long time, Flutter was missing a comparable solution. That changed with Shorebird, founded by former Google Flutter team members. The basics and the setup are described in code push: Flutter updates without the app store.
How Shorebird Works Technically
Shorebird uses a modified Flutter engine that checks for updates on app launch:
// The app automatically checks for updates
// On the next restart, the patch is active
// Or manually with the shorebird_code_push package:
import 'package:shorebird_code_push/shorebird_code_push.dart';
final updater = ShorebirdUpdater();
Future<void> checkForUpdates() async {
final status = await updater.checkForUpdate();
if (status == UpdateStatus.outdated) {
// Update available!
await updater.update();
// On the next app restart, the patch is active
}
}
What Can Be Patched?
- ✅ Any Dart code
- ✅ Generated code (e.g., localizations)
- ✅ Dart dependencies (without native changes)
- ❌ Assets (images, fonts), coming soon
- ❌ Native code (Java/Kotlin, Swift/Objective-C)
- ❌ Flutter Engine version
The Workflow: From Release to Patch
1. Create a Release
First, you create a release, this is the base version that goes to the App Store:
# Initialize Shorebird
shorebird init
# Create release for Android
shorebird release android
# Create release for iOS
shorebird release ios
Upload the generated .aab or .ipa to the stores as usual.
2. Create a Patch
When you later change code, you create a patch:
# Fixed a bug in lib/main.dart?
shorebird patch android
# Output:
# 📱 App: My App
# 📦 Release Version: 1.0.0+1
# 📺 Channel: stable
# 🕹️ Platform: android [arm64, arm32, x86_64]
#
# ✅ Published Patch!
That's it. All users with version 1.0.0+1 automatically get the fix on their next app launch.
Staging: Test Patches Before Rollout
This is where it gets interesting, and important for our rollback topic. Shorebird supports different tracks:
# Deploy patch only for staging
shorebird patch android --track=staging
# Test locally with shorebird preview
shorebird preview --track staging --release-version 1.0.0+1
This way you can test patches internally before releasing them to all users.
Percentage-Based Rollouts
Even safer: Gradually roll out to only a portion of users:
// In your app
Future<UpdateTrack> determineTrack() async {
final rolloutPercentage = await fetchRolloutPercentage(); // e.g., from Firebase
final userGroup = await getUserGroupNumber(); // 1-100, stored locally
// Users in group 1-25 get beta patches at 25% rollout
return userGroup <= rolloutPercentage
? UpdateTrack.beta
: UpdateTrack.stable;
}
Rollbacks: The Lifesaver
Now to the heart of this article. What happens when your patch causes problems?
Method 1: Change Track
The simplest approach: Remove the problematic patch from the stable track:
# Demote patch from stable to staging
shorebird patches set-track \
--release-version 1.0.0+1 \
--patch-number 2 \
--track staging
Users will then automatically receive the last working patch (or the original release version).
Method 2: Via the Shorebird Console
In the Shorebird Console you can:
- Navigate to the release
- Select "Change Track"
- Change the track (e.g., from
stabletostaging)
One click, all users are back on the safe version.
Method 3: Push a New Patch
Sometimes the quickest fix is simply a new patch:
# Revert in code (git revert, manually, etc.)
git revert HEAD
# Push new patch
shorebird patch android
What Happens Technically During Rollback?
When a patch is "rolled back":
- Shorebird marks the patch as no longer active
- On the next app launch, the app asks for updates
- The API responds: "No new patch available"
- The app uses the last known good state (previous patch or release)
Real-World Disaster Stories: Why Rollbacks Are Essential
The CrowdStrike Case (2024)
Although not a mobile example, the CrowdStrike incident perfectly illustrates the problem: A faulty update crippled millions of Windows machines, hospitals, airports, banks. Without quick rollback capability, recovery took days.
Mobile App Disasters
The app world has plenty of horror stories too:
- Spotify (2023): An update crashed the app on older Android devices. Thousands of 1-star reviews in hours.
- Banking Apps: Authentication flows regularly break after updates, with Code Push, this would be fixed in minutes.
- E-Commerce Apps: Black Friday is the worst day for a bug. With rollbacks, you could be back in seconds.
My Own Experience
On a client project, we once pushed a patch that caused a white screen on 5% of devices (certain Android versions).
Without Shorebird: Build hotfix → Wait for App Store review → Ask users to update → 3-5 days downtime for these users.
With Shorebird: Changed track → 30 seconds later everyone was back on the working version.
Best Practices for Safe Deployments
1. Always Use Staging
Never push directly to stable:
# NOT like this
shorebird patch android
# BETTER
shorebird patch android --track=staging
# Test...
shorebird patches set-track --track stable
2. Implement Percentage-Based Rollouts
Roll out your patches gradually: 5% → 25% → 50% → 100%
3. Set Up Crash Monitoring
Connect Crashlytics/Sentry with your deployment:
void main() async {
await SentryFlutter.init((options) {
options.dsn = 'YOUR_DSN';
});
// Set patch version in Sentry context
final patch = await ShorebirdUpdater().readCurrentPatch();
Sentry.configureScope((scope) {
scope.setTag('shorebird_patch', patch?.number.toString() ?? 'none');
});
runApp(MyApp());
}
4. Enable Patch Signing
For maximum security, only signed patches are accepted:
# Generate key pair
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
# Create release with public key
shorebird release android --public-key-path public.pem
# Patches must now be signed
shorebird patch android --private-key-path private.pem --public-key-path public.pem
5. Combine with Feature Flags
Code Push + Feature Flags = maximum control:
// Feature Flag Check
if (await featureFlags.isEnabled('new_checkout_flow')) {
return NewCheckoutScreen();
} else {
return OldCheckoutScreen();
}
This way you can disable features even without a new patch.
Store Compliance: Is This Even Allowed?
Short answer: Yes.
Google Play Store
An app may not download executable code from a source other than Google Play. This restriction does not apply to code that runs in a virtual machine or an interpreter.
Shorebird uses the Dart VM, fully compliant.
Apple App Store
Interpreted code may be downloaded to an Application but only so long as such code does not change the primary purpose of the Application.
As long as you don't push "major features" that change the app's purpose, you're safe.
Important: Code Push is standard practice in the industry. React Native has been using it for years (Microsoft CodePush, Expo). All major apps (Facebook, Airbnb, Lyft) use similar technologies.
Pricing: What Does Shorebird Cost?
- Hobby (Free): 5,000 Patch Installs/month
- Team ($20/month): 50,000 Patch Installs
- Business ($500/month): Unlimited Installs + Priority Support
For most side projects, the free tier is enough. For production apps, $20/month is a no-brainer, these are the running costs that definitely pay off.
Conclusion: No Production Deployment Without a Rollback Strategy
Patch rollbacks aren't a nice-to-have, they're mandatory for every production app.
With Shorebird you get:
- Instant updates without App Store waiting time
- One-click rollbacks for problems
- Staging tracks for safe testing
- Gradual rollouts for risk minimization
- Patch signing for enterprise security
The Friday night nightmare from the beginning? With Shorebird, that would have been a 30-second fix instead of a weekend disaster.
My advice: Integrate Code Push from the start. Not when you need it, then it's too late.
If you're still at the beginning of your app development journey, check out my article on validating your app idea. And if you want to understand how the backend behind it works, you'll find all the info in my backend guide.



