April 2021 by Benjamin Pasero

VS Code — The Story and Technology Behind One of the World's Most Popular Desktop Apps for Developers

About the Author
Benjamin Pasero works in the VS Code team at Microsoft since its beginning in 2011. His main focus is to ensure that VS Code, the editor that's used by millions of developers every day, works and integrates well across Windows, Linux, and macOS.

A desktop application with millions of users? Built with web technologies?! We are, of course, talking about VS Code, the go-to editor for software developers of all trades.

In our conversation with Benjamin Pasero, one of the engineers at Microsoft who was involved in this daunting project, we learned about the exact technologies they are using, what they like (and don't like) about them, and also some of the surprises they encountered on their journey.

Tell us a little bit about your application: what does it do and when did you start working on it?

Visual Studio Code is a cross platform code editor written in TypeScript based on Code OSS with support for extensions and a wide range of programming languages. I started working on it in October 2011 when I joined Microsoft. By then the project was ongoing for around 2 months.

What technologies are you using to develop it?

VS Code was originally written with a mix of JavaScript and TypeScript but we quickly adopted TypeScript for all of our code and immediately got attached to its improved tool support like static type checking and refactoring. There is a bit of native code involved too for some of our Node.js modules that we maintain. By using Electron as an application framework we can ship VS Code to macOS, Linux and Windows, including support for ARM-based chips. Electron is built on top of Chromium and Node.js, which enables us to use standard web APIs. We do not, and never did, use any web framework, such as React or Vue.js. I feel that going with the standard web APIs gives us the most stable API to work with and does not lock us into a particular, possibly opinionated, tech stack that we constantly have to maintain. One major advantage of using standard web APIs for VS Code is that we can provide VS Code as an online component for GitHub Codespaces with almost the identical code base.

Why did you choose those particular technologies?

We started VS Code (initially called “Monaco”) with the goal of building a world-class code editor for web development that runs in the browser. So, it felt natural that we would use web technologies to build VS Code, to get immediate feedback on how good it is for developing web apps.

TypeScript (initially called “Strada”) started around the same time as we started VS Code, so we became early adopters and were able to provide constant feedback. We still maintain a very good relationship with the TypeScript team and continue to provide feedback after adopting their latest versions (even beta). The TypeScript team not only implements the compiler but also the language service that provides support for Intellisense or refactorings in VS Code. As a consequence we regularly discuss features with them that improve the TypeScript coding experience in VS Code. Check out their roadmap to get an idea of upcoming features!

Picking Electron (at that time still called Atom Shell) as a cross platform framework was a decision that we only made at a later point in the project when we decided to ship a cross-platform text editor that runs on the desktop. We started in October 2014 with the goal to ship a beta of the editor in May 2015 and were able to do so. We briefly looked at nw.js too, but then decided to go with Electron because its development seemed more active at that time. Over the years we have maintained a very good relationship with the Electron team and we’re able to give periodic feedback for the latest versions as we adopt them.

Tell us about the good parts: what’s great about those technologies?

TypeScript is a great language for building any apps that rely on JavaScript, especially given the size of applications like VS Code’s codebase. Without this level of type safety we would have not been able to constantly evolve the product. For example, during the over nine year lifetime of the project, we did several significant code refactorings that we would not have survived without TypeScript. It helped a lot that we have followed a 'no any' rule in the project from the beginning. This rule means that we do not want to have variables that resolve to type Any in our code and thus gave us confidence for refactorings. However, since we were very early adopters of the language, many of the stricter type checks (such as “strict null”) were not enabled right from the beginning. We went through many iterations of adopting the latest features from newer TypeScript versions. Adopting “strict null” was possibly the largest effort, but we are very happy with the result.

Since we started working on Monaco around 2011, many of the currently available JS language features were not available yet. TypeScript allowed us to already benefit from proposed language features before they were shipped in major browsers by allowing us to compile down to ES5 or even ES3 code. For many years, the Monaco editor was supported all the way to IE9, and that was only possible by having this compile option.

Electron is a great framework for cross-platform applications if your application is already using web technologies. Since our product was already quite mature when we decided to ship it as a standalone cross-platform application, it felt only natural to pick Electron for VS Code. Electron allows us to ship on all platforms at the same time and we are doing this monthly. To get confidence into all shipped platforms, we make sure that the team's dogfooding covers all the platforms.

Lately, Electron has adopted a release schedule where the newest Chromium versions are adopted when they are released, allowing us to constantly be on the latest versions of this browser stack. This helps with many aspects, such as performance, security, and being able to use the latest web APIs.

Did you encounter any problems or disadvantages connected to those technologies?

Being an early adopter of TypeScript meant we constantly had to adopt newer features simply because they were not available before. I already talked about the "strict null" checks before and how much we benefit from them but it was also hard for us to enable them because of the maturity of our code base. It took a lot of time and effort but eventually paid off. I would think that today when you start using TypeScript, this is not an issue anymore if you decide to enable the strict checks right from the beginning.

Electron is built on top of large open source projects (Chromium, Node.js) and this sometimes means if a user reports an issue that is originating from an upstream component, we have to rely on those components to fix the issues. And often it takes major debugging skills to understand when an issue does not originate from code we wrote. This greatly improved when we were able to run VS Code in the browser, because this allowed us to report issues against the Chromium project with much less effort to come up with reproducible steps. In an issue we now can just point to a URL that illustrates the problem. We also inherit a ton of the changes that the Chromium team has decided on when we adopt newer Electron versions. For example, I remember a lot of issues around how colors are presented because in one release Chromium decided to pick a different default color scheme on macOS. These kinds of changes only surface when we put out a newer version of VS Code with this Chromium version for people to test and often require a lot of analysis to figure out the root cause.

Along the way, was there anything that took you by surprise - any pros / cons, any insights that you hadn’t been aware of when you started?

Looking back, I find it interesting how we moved from targeting the web first (“Monaco”), to targeting the desktop (“VS Code”), and then back to targeting the web (“GitHub Codespaces”). When we originally started our work, the mission was to build a code editor that can run in the web and our Monaco editor component that is used in many web based applications outside of VS Code is a great outcome of this effort. But we soon realized that both developers and the web platform were not ready for web-only development tools yet. Shipping a cross-platform editor as an application was actually not something we had planned for 2014 or later. And years before that we had shipped a workbench+editor component to edit Azure websites. Now that VS Code on the desktop is mature, we are heavily investing into making VS Code run equally well on the web again within GitHub Codespaces. At the same time, we actively invest into enabling Electron's sandbox and context isolation security features, which will move the native VS Code application closer to the web model again. If I think about this process like an amplitude, we had large swings in either web or desktop direction, but we are getting closer to converging to just web as the solution for everything.

How do you see those technologies in a “broader context”, beyond your particular app?

I think the success of web apps is quite evident. For example, I can run most Microsoft Office apps in the browser today equally well as if I had the apps installed natively. I use Microsoft Teams with screen recording, screen sharing, audio and video calls all from the browser every day and it works great. Similarly, I am running a progressive web app (PWA) version of VS Code for web locally to work on VS Code everyday and I am not missing a lot. The fact that web APIs are constantly improving to enable a better integration with the operating system you are running from helps a lot (and this is indeed a big difference from when we started in 2011). One great example is the recent native file system APIs that allow VS Code to access local files even when running in the browser.

Similarly, TypeScript is there to enable these complex JavaScript-based apps to exist in the first place. It is nice to see that JavaScript and TypeScript are moving closer together (e.g. when classes were introduced to ES6, TypeScript had already been using the same syntax), but the actual benefit of type-safe code and the ability to build tools on top of that (e.g. reliable refactorings) is crucial and I would not want to miss out on that.

What about the development tools that are available? What’s great and what could be better?

Since we build a code editor our main development tool is obviously VS Code itself. Our goal was to develop VS Code with VS Code exclusively once we were two months into the project in 2011. It was bumpy, for example, initially there was no mouse or fulltext search support available. What we ensured from the beginning is that no work can be lost by implementing auto save support right from the beginning. Being able to turn off auto save was actually only added in 2015 as part of our effort to ship VS Code as a desktop application.

Some of us are still using the browser’s own development tools, namely Chrome DevTools, because they ship as part of Chromium and thus as part of Electron. For me personally, having a debugger that ships as part of the application that is integrated into the application window has a lot of appeal and I’ve been a happy user ever since.
Nevertheless, our built in JavaScript debugger has seen some great improvements lately, so make sure to give it a try as well.

One area of development tools that I feel could be better is performance testing tools. The performance tools that ship with Chrome DevTools are a great start, but unfortunately fail to report time spent in native components of the application (such as Electron itself or native Node.js modules). We often then have to rely on other tools to benchmark these pieces and fail to see the entire picture from C++ execution into JavaScript execution and back.

When thinking about the future of those technologies, what do you expect or hope for?

I wish that web APIs would evolve further to enable even better integration into the operating system. This is a process that has already started around progressive web apps (PWA), but I feel a lot is still missing and could be improved.

For Electron I really wish there was a good way to share runtimes across different products. Every Electron based application ships an entire Chromium & Node.js stack which comes at a cost of a large footprint. If this stack could somehow be provided from the OS in a clever way sharing the same Electron components if possible, that would be great.

If people want to start developing with those technologies, what should they look at and what advice would you give them?

All of the technologies we use are very well-documented these days and have lots of resources for getting started. And since many users of these technologies ship them as open source, it can even be helpful to look at existing code and learn from it. Some useful resources to share:

One piece of advice that I would give is to try to use existing web APIs as much as possible before introducing dependencies to Node.js or even Electron. With that decision you are free to ship your application from a website or from a bundled application while reusing the same source code. Once you decide to introduce a dependency on Node.js or Electron make sure that the code is clearly separated as we do it in VS Code.

When using TypeScript (you really should!), come up with a good set of rules right from the beginning. Adopting rules later when lots of code has already been written can be painful. I would strongly suggest enabling the standard strict mode, which enables a lot of rules automatically. Refer to our tsconfig.json as inspiration.

Your Download is in Progress…

Giveaways. Cheat Sheets. eBooks. Discounts. And great content from our blog!