Electron in Practice: Update Strategies
Preface
After developing a desktop app with Electron,
the next question is how to update subsequent versions.
This article introduces several update strategies.
Electron Architecture
An Electron desktop app
consists of several parts:
1. The Electron runtime itself
2. The main process, which includes a Node.js part and a non-Node.js part (e.g. C++)
3. The renderer process, which can be built with any frontend technology

File Structure
Taking a Mac app as an example, the file structure consists of:
1. The installer DMG — think of it as a compressed version of the app bundle
2. The app bundle (.app)
3. The business code in the resources folder
4. The core business code in the app folder
5. The app folder contains renderer, main, and plugins folders

The mapping between architecture and folder structure:

Update Frequency and Strategies
Now that we understand the Electron architecture and the folder structure mapping,
let’s look at the update frequency and recommended strategies for each part:
1. Electron runtime: refers to Electron version changes. After initial development this rarely changes. If it does, a new DMG is needed — full update.
2. Main process — non-Node.js part: refers to C++ components like dylib, node files, etc. Changes are also rare. If needed, a full update is recommended.
3. Main process — Node.js part: business code written in Node.js. Changes are frequent. Incremental updates work well.
4. Renderer process: the frontend part. Changes most frequently. Incremental updates are ideal.

Update Package Size
Using a previous business app as an example,
comparing package sizes for different update strategies:
1. Full update: DMG — 90MB
2. Incremental update: main process Node.js part + renderer process — 3.4MB, which is 3.4% of the full update

So the parts that change most frequently (main process Node.js + renderer) actually have the smallest update package size.
Packaging Methods
To enable incremental updates, the packaging method needs some adjustments.
Default Packaging
The packaging tool used in this article: https://qiao-electron-cli.vincentqiao.com/#/
With default packaging, the app folder contains main and renderer folders:

Asar Packaging
Setting the asar option to true in qiao-electron.config.js:

The entire app folder becomes a single asar file.
This isn’t ideal though,
because the non-Node.js main process parts are also bundled in,
and those are large but rarely updated.
Incremental Update Packaging
The ideal result looks like this:

1. Inside the app folder, the non-Node.js main process parts remain as folders (e.g. plugins above)
2. Inside the app folder, the Node.js main process + renderer parts are bundled into a single asar file — keeping the size small and easy to update
Implementation
How do you implement these packaging and update strategies?
Here’s a recommended Electron packaging tool: https://qiao-electron-cli.vincentqiao.com/#/
File Structure
The corresponding file structure:
1. xx-renderer folder: contains renderer process code, builds the final output to xx-electron/dist/renderer
2. xx-main folder: contains main process Node.js code, builds the final output to xx-electron/dist/main
3. xx-electron folder: contains packaging config files
Default Packaging
Create a config file in xx-electron: qiao-electron.config.js
For default packaging config details, see: https://qiao-electron-cli.vincentqiao.com/#/packmac
After running the command, the Mac app bundle is output to xx-electron/out/xx-darwin-arm64:
> icns-electron@0.0.4 packmac:arm64
> qelectron pm ./pack/darwin-arm64.config.js
can not find ./qiao-electron.mac-sign.js
pack electron application by qiao-electron-cli:
{
arch: 'arm64',
overwrite: true,
asar: false,
dir: 'dist',
out: 'out',
platform: 'darwin',
name: 'icns',
icon: 'pack/icon/icon.icns',
appVersion: '0.0.4',
appCopyright: 'Copyright © 2024 insistime.com版权所有'
}
Packaging app for platform darwin arm64 using electron v30.0.1
pack electron application success!
Final output:

Asar Packaging
The only difference from default packaging is setting asar to true in the config file.
The app folder in the final output becomes an asar file:

Mac Signing and Notarization
See another article for details: https://blog.insistime.com/electron-mac-sign
For packaging with qiao-electron-cli, see: https://qiao-electron-cli.vincentqiao.com/#/packmac
An additional config file is added under xx-electron: qiao-electron.mac-sign.js
The packaging process now includes signing and notarization, which takes a bit longer.
Console output and final output are similar to the two methods above.
Incremental Update Packaging
Simply set the versionUpdate property to true in qiao-electron.config.js.
Console output:
> icns-electron@0.0.4 packmac:arm64
> qelectron pm ./pack/darwin-arm64.config.js
qiao-electron-cli / error / pack / can not find ./qiao-electron.mac-sign.js
qiao-electron-cli / info / versionUpdate / root /Users/vincent/Desktop/insistime/icns-monorepo/packages/icns-electron
qiao-electron-cli / info / versionUpdate / version 0.0.4
qiao-electron-cli / info / versionUpdate / postPath dist-post
qiao-electron-cli / info / versionUpdate / cpSrc /Users/vincent/Desktop/insistime/icns-monorepo/packages/icns-electron/dist
qiao-electron-cli / info / versionUpdate / cpDest /Users/vincent/Desktop/insistime/icns-monorepo/packages/icns-electron/dist-post/0.0.4
qiao-electron-cli / info / versionUpdate / cpRes true
qiao-electron-cli / info / versionUpdate / asarDest /Users/vincent/Desktop/insistime/icns-monorepo/packages/icns-electron/dist-post/0.0.4.asar
qiao-electron-cli / info / versionUpdate / asarRes sucess
qiao-electron-cli / info / versionUpdate / jsonSrc /Users/vincent/Desktop/insistime/icns-monorepo/packages/icns-electron/dist-post/0.0.4/package.json
qiao-electron-cli / info / versionUpdate / jsonDest /Users/vincent/Desktop/insistime/icns-monorepo/packages/icns-electron/dist-post/package.json
qiao-electron-cli / info / versionUpdate / jsonStr {
"name": "icns",
"private": true,
"version": "0.0.4",
"main": "main/index.js",
"dependencies": {}
}
qiao-electron-cli / info / versionUpdate / jsonRes true
qiao-electron-cli / info / versionUpdate / rmRes true
qiao-electron-cli / info / versionUpdate / mvRes true
qiao-electron-cli / info / pack.js / pack electron application by qiao-electron-cli:
qiao-electron-cli / info / pack.js / config {
arch: 'arm64',
overwrite: true,
asar: false,
versionUpdate: true,
dir: 'dist',
out: 'out',
platform: 'darwin',
name: 'icns',
icon: 'pack/icon/icon.icns',
appVersion: '0.0.4',
appCopyright: 'Copyright © 2024 insistime.com版权所有',
afterInitialize: [ [AsyncFunction (anonymous)] ]
}
Packaging app for platform darwin arm64 using electron v30.0.1
qiao-electron-cli / info / afterPack / buildPath /var/folders/t7/pcqmy6kj07b97z_2h5g5_sjr0000gn/T/electron-packager/darwin-arm64/icns-darwin-arm64-Sy6Xw1/Electron.app/Contents/Resources/app
qiao-electron-cli / info / afterPack / jsonSrc /var/folders/t7/pcqmy6kj07b97z_2h5g5_sjr0000gn/T/electron-packager/darwin-arm64/icns-darwin-arm64-Sy6Xw1/Electron.app/Contents/Resources/app/package.json
qiao-electron-cli / info / afterPack / jsonStr {"name":"icns","private":true,"version":"0.0.4","main":"0.0.4/main/index.js","dependencies":{}}
qiao-electron-cli / info / afterPack / jsonVersion 0.0.4
qiao-electron-cli / info / afterPack / jsonAfterStr {"name":"icns","private":true,"version":"0.0.4","main":"0.0.4.asar/main/index.js","dependencies":{}}
qiao-electron-cli / info / afterPack / jsonRes true
qiao-electron-cli / info / afterPack / rmPath /var/folders/t7/pcqmy6kj07b97z_2h5g5_sjr0000gn/T/electron-packager/darwin-arm64/icns-darwin-arm64-Sy6Xw1/Electron.app/Contents/Resources/app/0.0.4
qiao-electron-cli / info / afterPack / rmRes true
qiao-electron-cli / info / pack / pack electron application success!
Final output:
1. The app folder contains an asar file and a package.json file
2. out/update contains a zip of the asar file, ready for subsequent updates

Incremental Update
After building the incremental update package as described above, how do you implement the actual update?
An npm package has been created for this: https://code.insistime.com/#/qiao-x-update
Usage is straightforward:
updateApp(downloadUrl, appPath, appVersion);
Summary
1. Introduced the Electron app architecture and corresponding file structure
2. Discussed update frequency and package sizes for each part
3. Recommended a fine-grained packaging approach
4. Introduced incremental update packaging with qiao-electron-cli: https://qiao-electron-cli.vincentqiao.com/#/
5. Introduced the incremental update implementation package: https://code.insistime.com/#/qiao-x-update