Know which flag broke your app, before anyone is paged.
Culprit compares crash, ANR and error rates for every feature-flag variant against its control group. When one is clearly worse, it tells your team in Slack exactly which variant, on which app version, and where. If you allow it, it also switches that variant off.
Read the story behind Culprit → · See the test results and demo →
killswitch_new_checkout off in Statsig.
Concentrated in: Android 14 (62% vs 20% control) · Samsung SM-S918B (41% vs 9% control).A real alert format. Nobody had to open a dashboard to find the cause.
Mobile releases can't be rolled back. Flags can.
Once a build is in the store, a feature flag is the fastest lever you have. But crash tools tell you that crashes went up, not which of your dozen running flags and experiments caused it. Culprit takes over that investigation.
Someone pieces it together by hand
- Crash-free rate alert fires
- Open the crash tool, then the flag tool, then Slack history
- Guess which rollout overlaps the spike
- Check whether it's one app version or all
- Find the person who can flip the flag
The answer arrives in Slack
- Every variant is compared with control, per app version
- The alert names the flag, variant, version and signal
- It shows where the errors concentrate (OS, device)
- Opted-in flags are switched to control automatically
- Every alert and kill is logged for the dashboard
Names the cause
Not "crashes are up". It reports "new_checkout=on on v5.2 crashes 6× more than control".
Crashes, ANRs and errors
Each signal is checked on its own, so a variant that freezes the app is caught even when crashes are flat.
Few false alarms
Minimum sample sizes, a 2× ratio, and a sequential test that stays valid however often the job checks, corrected for the number of flags.
Safe auto-kill
Alert-only by default. Kills are opt-in per flag, only ever send users to control, and are capped at one a day.
Fits your stack
Crashlytics or Sentry for crash data, and Firebase Remote Config or Statsig for flags, in any combination.
History you can see
Every finding goes to BigQuery, with a ready-made Grafana dashboard or a one-click Looker Studio report.
Three parts, no new servers to run
A small wrapper in the app, a scheduled job on Google Cloud, and a kill switch per watched flag.
Tag
The app reads watched flags through a ~30-line Flags wrapper. It tags every crash and session with ff_<flag>=<variant> and checks killswitch_<flag> first.
Compare
Every 15 minutes a Cloud Run job counts affected users and active users over the last 7 days, per flag, variant, app version and signal, and compares each variant with control.
Act
A clear regression gets a Slack alert with its OS and device breakdown. For opted-in flags, the job also turns the kill switch on, and the app serves control on its next fetch.
Run the detector yourself
Below are the same rules and statistics the real job uses. Change the numbers to see when Culprit raises an alert, and what it would post.
The p-value is from a sequential test (mSPRT), which stays valid when the job checks again every 15 minutes as data builds up over 7 days. With N flags watched, the job makes 3 × N comparisons per app version, and each must beat 0.01 ÷ comparisons (Bonferroni correction).
Add Culprit to your project
Pick your tools and platform. Every step and snippet below updates to match.
Culprit is an early-stage open-source project. Its logic is covered by offline tests, so run it in a staging project and use --backtest before turning on auto-kill in production.
An automatic kill switch you can trust
A tool that can turn features off needs strict limits. These are built in, not optional.
Off by default
A new install only sends alerts. Auto-kill needs two opt-ins: "auto_kill": true for the flag in a reviewed PR, and AUTO_KILL=1 on the job.
Only ever toward safety
The job can only turn killswitch_<flag> on, which sends users to the control path. Code checks refuse any other change, and tests cover them.
Hard to trigger by accident or abuse
Non-fatal errors never kill. Kills need at least 10 affected installs plus a significant result, and are capped at one a day.
Least privilege
Read-only access to crash data. Secrets live in Secret Manager. No service-account key files. The dashboards use read-only access.
Questions teams ask
Does it replace Crashlytics or Sentry?
No. It reads from them. Your crash tool still collects and groups crashes; Culprit adds the "which flag variant caused it" answer and the kill switch.
What does "active users" mean with Sentry?
Sentry can't break its session counts down by custom tags, so Culprit counts distinct users on app-start traces, which carry the same flag tags. That means sampling app-start traces at 100%.
What happens if the breakdown query fails?
The alert still goes out, with "Breakdown unavailable". The OS and device breakdown is extra context, and it never delays a kill or blocks an alert.
How do I undo a kill?
Turn killswitch_<flag> back off in Remote Config or Statsig. Both keep a change history. Find the cause first: the detector will alert again if the rate climbs back.
Where does it run, and what does it need?
A Cloud Run job triggered every 15 minutes by Cloud Scheduler, with a BigQuery table for the audit log. One deploy.sh sets everything up.