Documentation

Everything to ship over-the-air updates with Pulsar — from install to multi-deployment testing.

Hermes bytecode

Hermes is the JavaScript engine React Native uses by default. Your store build doesn’t carry your JavaScript as text: the build compiles it ahead of time to Hermes bytecode, which the engine loads and runs directly. From CLI 1.6, pulsar release does the same for over-the-air updates.

Why it matters

Before CLI 1.6, every OTA was plain JavaScript. Once one was installed, the app parsed and compiled the whole bundle, often several megabytes, on every cold start. On low-end Android phones the app was slow to open, and some phones stopped it before it finished loading. With a bytecode update, the app starts about as fast as the store build does.

How it stays safe

Bytecode only runs on a Hermes engine with the same bytecode version, and React Native upgrades change it: React Native 0.82 and 0.83 use v96, 0.84 to 0.86 use v98, and a later upgrade can change it again. Bytecode sent to a build with another version would crash the app at launch. So every bytecode release carries a JavaScript copy of the same release, with the same assets, and SDK 1.9 tells Pulsar which bytecode version each build runs when it checks for updates.

  • A build that reports the same bytecode version gets the bytecode.
  • Every other build gets the JavaScript copy: builds that don’t report a version (an SDK older than 1.9, or no Hermes) and builds on another React Native version.
  • If the JavaScript copy is missing, for example because the upload was cut off, the release reaches only builds that report its version. Every other build stays on its current release.

Pulsar doesn’t send bytecode to a build that hasn’t reported a matching version. It looks inside every upload it can read: bytecode uploaded without its version, for example by another tool, is labelled and only reaches builds that report that version. It can’t look inside an encrypted upload, so for private bundles it relies on the CLI, which reads the version before it encrypts.

Rolling it out

Only builds on SDK 1.9 or later report a bytecode version, so your first bytecode releases go mostly to the JavaScript copy. SDK 1.9 sends the report from JavaScript and has no native changes over 1.8, so a store build on SDK 1.8 starts reporting once it installs an OTA built with SDK 1.9, and gets bytecode from the next release on.

Before uploading, the CLI prints how many active devices on your --target report the matching version and get the bytecode; the rest get the JavaScript copy. The Store builds panel shows how many devices on each store build report each bytecode version.

Flags
  • Hermes is on unless your project turns it off: hermesEnabled=false in android/gradle.properties, enableHermes: false in android/app/build.gradle on React Native 0.70 and older, or :hermes_enabled => false in ios/Podfile.
  • --no-hermes releases JavaScript only, as before CLI 1.6. --hermes compiles even when your project says Hermes is off.
  • The compiler comes from your app’s node_modules (hermes-compiler/hermesc, react-native/sdks/hermesc on React Native 0.82 and older, or hermes-engine on 0.68 and older), so the bytecode matches your React Native version. If it’s missing, the CLI stops and suggests --no-hermes. It never ships JavaScript in its place.
  • The CLI checks that your Pulsar server can serve bytecode releases this way, and stops before uploading if it can’t.
bash
pulsar release MyApp Production --platform android --target 1.0.0

# JavaScript only
pulsar release MyApp Production --platform android --target 1.0.0 --no-hermes
What else changes
  • The CLI runs Metro twice: once minified for the JavaScript copy, the same kind of bundle earlier releases shipped, and once unminified for the bytecode, the way React Native’s own release build compiles it. Both builds carry the same assets.
  • When the app has a signing key, both builds are signed. Each build gets its own source map, so on apps without private bundles crash stacks map to your files whichever one a device runs.
  • For private bundles, both builds are encrypted, and --plain-copy-for-old-builds uploads the JavaScript build as the plain copy.
  • Diffs are built for the bytecode, including from the previous release’s JavaScript copy. Builds that get the JavaScript copy download it whole.
  • --max-growth and the size warning compare bytecode with the last bytecode release and the JavaScript copy with the last JavaScript.
  • The dashboard tags these releases bytecode v96 + JS copy, and pulsar history tags them [bytecode v96 + JS copy]. pulsar promote and pulsar rollback keep both builds, and warn when a release has no JavaScript copy.
  • Pulsar won’t roll back to a bytecode release that has no JavaScript copy, since every build that doesn’t run its bytecode would stay on the release you’re rolling back from. Release the fix again instead.
  • On a deployment that needs approval, the JavaScript copy has to be uploaded before the release is approved, as the CLI does. Once a release is approved or rejected, a copy can’t be added, because the approval didn’t cover it.
  • pulsar migrate doesn’t copy a release that is already Hermes bytecode, since it has no JavaScript copy for other builds. Release it again from source.
Limits
  • Downloads are bigger. Bytecode compresses less well than minified JavaScript. On one app we measured, the full download was about 1.9 times larger.
  • It doesn’t hide your code. Every bytecode release also ships its JavaScript copy, and any client can get that copy by not reporting a bytecode version, so your code is as easy to read as it was before. The benefit is the faster cold start. Bytecode on its own makes code harder to read, not unreadable: Hermes decompilers exist, and strings such as URLs and messages stay visible. Keep secrets on your server.
  • It is not a way to hide anything from app review. Your store build already ships bytecode, and every OTA must still follow the store rules. Apple and Google Play allow downloaded JavaScript that doesn’t change the app’s purpose, so keep what you ship over the air within what was reviewed.