Dos and don’ts when adding Electronjs to your product

In our last article about Electronjs, we discussed how our team decided to use Electronjs in our apps. Along the way, we learned a lot about Electronjs. We wanted to share some retrospective dos and don’ts for your team to learn from when integrating Electronjs into your current product. Let’s dive right into some of the key takeaways.

Do not rewrite your product from scratch.

We do not recommend rewriting your product from scratch using Electronjs. It is tempting to throw out all of the nasty hacks you have made over the years and start from scratch. In our experience, this approach leads to a significant loss of project momentum.

For example, initially, we planned a rewrite, but we struggled to converge on a solid plan. We then learned our stakeholders did not have the appetite for throwing away everything and starting over. Our team’s morale took a hit. You may find yourself in a similar situation where your team’s energy and leadership’s confidence wanes.

Rewrites are generally advised against by industry experts, and we mostly agree with this sentiment (we will touch on a caveat next).

Reset your team’s expectations if you find yourself in the same rabbit hole as us. We rebranded our efforts as a retrofit. Once we reoriented around this idea, we could more clearly break down the problem into smaller chunks and discuss our plan more concretely with our stakeholders. We even found our most compelling reason for taking on this work was improving our app’s security for our customers.

Do not build two products unless your new product vision is significantly different.

If you decide to rewrite and build something new, be very cautious. We were able to move, throw away, and refactor some of our oldest code even while retrofitting, but we referenced our old code a lot in the process. Do not expect to build something immediately better, different, or more innovative without taking aggressive steps to define what new looks like for your team.

Your customers’ needs or some new market opportunity should primarily guide the decision. We had to fight the urge to promote new Electronjs features and experiences before we needed them. Most of what we built looks a lot like what we already had.

Do prototype something to gain experience.

We learned a lot by prototyping a few small internal tools for our team to use before working on our retrofit. This process gave us some real users to work with, and we learned a lot from them. This prototype might not map directly onto the eventual work, but it serves as an excellent leading indicator for how your team will work with the new paradigms and development tools.

Do ship something side-by-side with your current app using feature flags and cohorts.

Plan for real-world failures. We recommend releasing your work side-by-side with your stable older code. We fell back to our old code whenever something unexpected happened in our updated app.

This approach will add some complexity to your deployment strategy. You should start by feature gating your new work, allowing Electronjs to start and stop at runtime.

Including Electronjs even when it is disabled will increase your app’s size because Electronjs is a large framework. We shipped an app update with Electronjs to all of our users, paying the download and bandwidth cost upfront. You may choose to limit those users who download an update with Electronjs to reduce the amount of bandwidth required to release your update. You will need to build two installers and split your users into two cohorts; users who will receive the Electronjs update when they install your app and users who won’t. With these feature gates and separate installers, you can manage the rollout efficiently.

Do expect to trade current problems for new problems.

It should not go without saying, once you make the switch expect a new set of problems to replace your old ones. In the beginning, we made sure that when our app encountered a problem, we would disable Electronjs, fall back to our old code, and record the errors. We also made sure it was straightforward for our users to contact our support team. If you don’t already have a “contact support” option, you should consider adding it and adding links to your FAQ and support material before you make the switch.

Do use higher-order inter-process communication.

You will find yourself needing to communicate or interop with your old codebase. The key to a retrofit is interoperability. Interoperability is a reasonably known problem space. We recommend that you stick with a higher-order inter-process communication layer for interoperability. We overly optimized and integrated a lower-level named pipe layer that required a lot more work to get right. We think a higher-order layer like HTTP will suit most app needs. You should only consider libraries like ZeroMQ when performance is paramount. HTTP is easy to implement in most languages, straightforward to work with, and if we had a “do-over,” we would opt for using it to interop.

Do focus on security early.

As we mentioned, our goal was to make our app more secure with Electronjs. Paradoxically, you can write very insecure code in Electronjs. When we took the contributor training, the maintainers were quick to remind us that Electronjs is not a browser. You can not treat it like one and benefit from strong sandboxing. Read and then reread the Electronjs security documentation. Areas to be mindful of:

  • Electronjs is NOT a browser.
    • You can not guarantee proper isolation.
  • Attackers can exploit your app by:
    • Taking control of the DOM
    • Cross-site injecting
    • Man in the middle attacks
    • Bypassing isolation
    • Executing remote code
  • Isolation pitfalls:
    • Access to Nodejs primitives
    • Experimental chrome-like sandbox, but it is not a true browser sandbox
    • Lack of isolation by default
  • Framework bugs:
    • CVEs are not uncommon
      • For example, this one line exploit was found on Windows in Electronjs:

        window.location = ‘myapp://foobar” --gpu-launcher=”cmd c/ start calc” --foobar=’
      • Can you see the exploit?

Do not block the main process.

The Electronjs team recommends running only fast operations on the main process, but we wanted to reiterate it. The main thread is the most crucial thread in your app. Do not block it, or your app performance will degrade quickly. If you need to process lots of data, do that in a hidden background renderer, a web worker on the main process, or a spawned child process.

Do consider Typescript.

Lastly, your team will find that it needs to transform a lot of data between your old code layer and your Electronjs Javascript layer. We recommend using Typescript instead of vanilla Javascript to help corral this data. You will have better data integrity by using Typescript’s static type checking. Typescript sped up our development and testing of our API layer quite a lot.

We listed out the dos and don’ts that we wish we had followed before integrating Electronjs into our product. It is not an exhaustive list but includes some of the most important lessons we learned along the way. We hope you found it useful. If your team has any questions or needs a sounding board, send us a message, and we can help validate your ideas and approaches.