đŚElectron: The Bad Parts
Most cross platform programming languages and frameworks contain good and bad parts. Electron probably has more than its share of the good â but also hides some dark secrets under its shining facade.
While the first impression of Electron might be that it solves all the problems related to cross platform development the reality is that many things wonât work out of the box and they probably canât. It took me quite some time to understand this, and it took even more time until I had all the DIY solutions in place to provide users with things like correct installers, updates, a feedback mechanism.
Iâm a big fan of Electron and thought the following list could be very helpful for people who are new to the topic. But project managers who are evaluating different stacks and want to avoid âsurprisesâ should benefit as well. The list presents things that are not completely clear in the beginning. Things where itâs worth to spend some extra time on evaluation before a decision and long term commitment is made.
Installers
Once the coding is over and the release planning starts, the first tough decision is waiting. One can either use the installer and update mechanism as described in the Electron documentation or use the installer and update mechanism that one of the best builder tools (electron-builder) recommends.
The official Electron documentation suggests the Squirrel (Windows and Mac) installer and the built-in autoUpdater. An open source release server (âNutsâ) is available which handles the server side management of updates and releases.
The other option is electron-builderâs own update mechanism âelectron-updaterâ. It is based on the established NSIS installer (Windows) and plain old mac packages. electron-builder uses this approach as the default setting for all generated installers.
The Electron and electron-builder project do very much disagree on which solution is better. This leads to a tricky situation where devs have to decide between one official solution (which many consider to be complicated and not flexible enough) and a simple and powerful one that has built-in tool support. Personally, I think electron-builder is a blessing if you actually plan to provide installations of your software to millions of people around the world and you donât know where to start. It can save you weeks if not months of development and I would consider it to be an essential part of the ecosystem.
Here is a list of pros from the electron-builder/electron-updater docs that should be considered before one solution is chosen over the other:
- It doesnât require a dedicated release server.
- Code signature validation not only on macOS, but also on Windows.
- electron-builder produces and publishes all required metadata files and artifacts.
- Download progress supported on all platforms, including macOS.
- Staged rollouts supported on all platforms, including macOS.
- Actually, built-in autoUpdater is used inside on macOS.
- Different providers supported out of the box (GitHub, Bintray, Amazon S3, generic HTTP(s) server).
- You need only 2 lines of code to make it work.
Continuous Integration (CI) & Multi Platform Builds
Electron should not be confused with a pre-installed âJava virtual machineâ. You will have to make (build, sign, distribute) your app cross platform. It wonât be cross platform âautomaticallyâ.
This can be quite confusing and shocking, but cheap and easy multi platform builds are a myth. It is almost ironical how often one will find âcross platformâ Electron applications that are âonly for Macâ or âonly for Windowsâ. The reason is not that itâs complicated to write code for multiple platforms (Electron handles it amazingly well and the documentation is clear). The reason is simply that many developers do not have the big budget or access to other hardware to test and build on. Even worse: if you plan to be a âlegitimate developerâ your app needs to be signed and it might have one or more native dependencies.
The recommended solution in these cases is to get accounts for different CI providers: one for Mac (and Linux) and one for Windows. Why? Because Apple officially prohibits the use of their operating systems
- in Virtual Machines on Non-OS X systems
- on non-Apple Hardware
The result is that if your app is closed source it will cost twice as much to build for two OS. And if money and a complex build system is not and issue, you still have the issue that the app will only be tested and built on multiple systems. At this point it cannot be shipped yet. Signing your app remotely with an Extended Validation (EV) code signing certificate for example is pretty much impossible with all existing CI tools available since your certificate is bound to a physical hardware token which cannot / should not be transferred.
Size
The fact that each application comes with its own version of Chromium (the 20 million LOC, ~30MB [packaged] Web runtime) is one of the most criticised aspects. Fun fact: if you would decide today to build your own simplified competitor version from scratch it would probably take you 5,099 years to build. Some have tried to put Electron on diet and critics say it creates the feeling of installing a full operating system on top of an operating system. Every time.
While many people would expect Chromium to drastically slow down the performance or âconsume precious RAMâ I havenât experienced any of this in practice and havenât heard bad feedback from thousands of test users. I guess it isnât such a big issue if you are used to have the Chrome browser running in background anyways. But here is the thing:
Not just installations bundle Chromium. Every update also comes with Chromium, Node and all the other Electron components unless the update is designed as delta or differential update.
Delta Updates
Everyone whoâs experienced a Windows update knows that updates suck! But they donât have to. Updates can introduce cool new stuff, donât have to interrupt the workflow and they can happen in the background instead of blocking your machine for one hour â Iâm looking at you Microsoft. The đ is âdelta updatesâ. Instead of uninstalling and re-installing everything, only the relevant parts should be updated incrementally.
Imagine the example from this blog: A class of students is trying out a software and the update is 70MB big. 30 people start their computers and get presented with an update. They download the update simultanously resulting in 2.1GB. Now, compare this to a delta update, handled in background, which is only 100KB in size and brings down the initial 2.1GB to 3MB total. It will shorten the time each student is waiting for the update to write itself to disk, to register with the system after the download; and it takes load from servers, saves bandwith, saves $$$, creates happy students and teachers.
Squirrel, in theory, supports delta updates. As of last week, electron builder with NSIS provides a beta solution for this too. However, I wouldnâ call either one of them to be available out of the box.
One of the applications Iâm working on â Autobeat Player â uses its own âdeltaâ update mechanism which updates the whole app. The twist: the whole application without external modules and libraries is only ~750KB(!) in size. Whenever I mention this, the reactions are priceless. Most people cannot believe the small size considering the feature richness and complexity of the application. A whole desktop application that has the size of a single image is how I would describe a new generation of (Electron) desktop apps. And the way weâve implemented updates in Autobeat is straighforward and no rocket science.
Everything that can be remotely considered static is not part of the application image: jQuery, Font Awesome, Bootstrap, or all the other frontend libraries and any of the node modules are not part of the app itself.
If one strips everything that is not frequently changing such as these libraries and only updates the app logic part â the things youâre actually working on â a 99.9% size decrease is absolutely possible. However, it requires custom update logic. There is no standard solution available. The benefit: in Autobeatâs case we can update the app as a whole as long as external dependencies are not changing which rarely happens. Updates take between 1â2 seconds and go often unnoticed. It is like updating a website â after a refresh it looks different. No need to worry that the deltas are miscalculated or parts are not working together as expected. Once in a while we add more dependencies and have to do a âfullâ 31MB update. But weâre making a long term bet here, assuming that these intervals get longer and longer while our regular release cycles can be as short as a couple of hours. And everyday we are learning more about how to leverage things that work for the Web and how to bring them to desktop to release more efficiently.
Security
The separation between app logic and libraries as described above unlocks another very powerful feature: it potentially allows to release a second app, which shares the same, existing core libraries and does not require the installation of another Chromium bundled package or the need to signand distribute a new installer. It creates an environment like a super-powered browser that supports jQuery, Vue, Font Awesome, Polymer and every other framework as well as all relevant node modules out of the box and minimizes the appâs size. Think about it for a second.
In recent tests, our team has successfully launched other âinstant appsâ (<1MB) in a distributed, P2P fashion in a fraction of a second. In the same way one would start apps on a Chromecast powered TV, it is also possible to remotely load apps on other machines. When I realized that we had just launched a fully featured desktop software from a web server running on a smartphone it partly felt like creating the next generation of decentralised apps and partly like the next generation of viruses.
In the same way that browsers are able to load any website, Electron potentially can load as many apps as it wants without the need to re-install the runtime over and over again. And the more the line between Web and desktop gets blurred, the more challenges we face as users to protect our machines and data.
Electron can also load websites just like any other browser. There are even tools like the extremely popular nativefier that wrap existing websites. These ânative-website-appsâ can potentially activate the webcam or microphone, access the filesystem and create, delete, encrypt things as they wish, or send them to servers â and most of the time this is intended. Eventually, the hosting server becomes the command and control server of a gigantic botnet.
Electron provides many protection mechanisms: The sandbox model can be activated, node integration can be turned off, preload scripts and webviews create local environments with different permissions... However, it concerns me that these desktop web apps evolve and distribute faster than our old security models. Things like installer signing and trusted authorities are in the process of becoming meaningless in a world where websites can be run as full-fledged apps outside their sandbox.
Recently, I was attending a talk from a well-funded startup that scans and analyzes npm modules for vulnerabilities to warn developers before they integrate them. When I asked if they would also consider npm module security in a desktop context the answer was that they havenât looked into it and that it has no priority. But every module that we reference and install on a userâs machine comes with its own dependencies and we end up sharing the userâs trust and permissions with modules we lose track of and often donât even know. In a time where a single cyber attack such as the recent WannaCry ransomware can have a financial impact as high as $4 billion, we are facing complete new challenges. This gets especially concerning when you read about popular modules that retweet hot pocket ads in the background every time they are installed.
Code Protection
In the same way we want to protect users we might also want to protect our own and our companyâs work. Electron apps are usually distributed in asar container files. To retrieve the plain source code from an installation and its contained asar file is as simple as:
asar extract app.asar secret_source_code
Files donât get obfuscated, encrypted or protected by default which means someone who wants to temper with an app can pretty much receive the working copy of the repository. This makes Electron a very poor fit for commercial solutions.
The official Electron statement reads like this:
The idea is that any sort of built-in protection can be bypassed anyways (which is true) and would just steal resources from more important areas (which is true).
Our products use a couple of obfuscation and encryption techniques and are definitely not as sensitive as say your Ethereum tokens. But many struggle with protection so here is one simple example to achieve source code encryption during packaging:
const crypto = require('crypto')
var password = new Buffer(âmy secret passwordâ);
function transform(filename) {
return crypto.createCipher(âaes-256-cbcâ, password);
}
asar.createPackageWithOptions(src, dest, { transform: transform }..)
Asarâs transform option allows to specify a stream transformer that is applied when the files are packed into the asar container. However at some point you will have to decrypt your files and this is where it gets really tricky. Also, if your static password leaks youâre not winning anything. There are other options like creating a V8 snapshot or to use C++ binaries and one can definitely make it their âbattleâ as much as they want. The main takeaway here is that Electron provides close to no protection and every minute that you invest in security hopefully creates 5 minutes for a hacker.
More
If you have an Electron app yourself and you liked the article, would like to get another perspective, or if you think that something is missing, presented in the wrong way or inaccurate, please leave a comment, clap or contact me.
To keep this article âshortâ, Iâm thinking about compiling additional and more detailed material. Additional topics could be crash reporting, separation of main and renderer process logic, analytics, installer distribution, campaign tracking and reengaging users through notifications. If you are generally interested in more information please let me know:
Link to form: https://goo.gl/forms/cnXgxM8fmg60f4lX2
Links
Good electron overview guide: https://blog.dcpos.ch/how-to-make-your-electron-app-sexy
electron-builder: https://github.com/electron-userland/electron-builder
Official updater docs: https://electron.atom.io/docs/api/auto-updater/
electron-updater: https://www.electron.build/auto-update
Squirrel to NSIS migration: https://github.com/electron-userland/electron-builder/issues/837
Multi platform builds: https://www.electron.build/multi-platform-build
electron-builder delta update support: https://github.com/electron-userland/electron-builder/issues/1523
Source code protection: https://github.com/electron/electron/issues/3041