GOTO 2016 • Visual Studio Code: Shipping One of the Largest Microsoft JavaScript Apps • Alex Dima
Articles, Blog

GOTO 2016 • Visual Studio Code: Shipping One of the Largest Microsoft JavaScript Apps • Alex Dima

January 13, 2020


(uplifting music) – Hello everybody, thank
you for joining me today. My name is Alex Dima, I’m
a senior software engineer on the Visual Studio
Code team at Microsoft. We’re working out of Zurich,
and today I would like to tell you a little bit about some of the lessons we’ve
learned while developing and shipping Visual Studio code. And I will try to give some insights into what we’ve been doing
and to what we’ve learned, and hopefully this would also help you in what you’re doing as well. Yeah, first a reminder to rate the talk. So this is basically Visual Studio code. I’m sorry if you cannot
really see it that well. This is what we ship every month, and I hope you know actually
quite a lot about it from Eric’s talk earlier. In reality, we actually
ship two different things. So we Visual Studio code, which
is more or less a web app. So it’s an app built using JavaScript, built using no Node.js API
built on top of a browser API, and they all come in
the shape of Electron, Electron basically consisting of a merger between Chromium and Node.js. But at the same time, we ship a library which we call the Monaco Editor,
which more or less contains the thing in red over there. We take that thing out of our product and we ship it separately
as a JavaScript library that can run in any modern web browser. So if you have an app
where you’re showing code, you can actually, even
today, go on NPM and fetch the Monaco editor node module. I’m still working on the documentation, so it’s still a work
in progress, but yeah. So that’s what we do today,
but we didn’t start doing that, and we actually started since
quite a number of years. Actually, since the autumn of 2011. And back at the time, as Eric mentioned, we were given the mission
to redefine and create a developer experience
or development tools that work in the browser. So at the time, we actually
started in JavaScript. Our patterns were consisting more or less of trying to create classes
where in JavaScript, you would use prototypical
inheritance and so on. So we would literally
say function my class, myclass.prototype.foo equals something. So we would all right idiomatic JavaScript to try to create some sort
of classes and patterns and types in our source code. One thing we got right from the beginning is that we jumped straight on promises as a way to abstract away
asynchronicity, right? So whenever we built something in the UI, whenever we had to compute something, so for example you move your
mouse over a piece of text and we have to show you a hover, so whenever we do that, we
built the UI code in such a way that in the end, the UI code makes a call, and that call returns a promise with the actual thing to show. So this was actually a
big win for us because whenever we changed architectures,
whenever we moved away from running only in
the browser to running in this Electron environment, we could just substitute
the implementation of the hover providers. And instead of executing
them in a web worker, all of a sudden we could
just spawn a process and do it in a separate process. And we had literally zero
changes to our UI code. So I was actually, from the beginning, I’m a hardcore JavaScript programmer so I actually love JavaScript. And any project I start, I
immediately start in JavaScript. But at the same time we
were starting at Microsoft, what became to be the TypeScript compiler, this little team, they
also started something. They called it at the time,
they called it Strada, and it was still an experiment as far as they were concerned. And they were also
looking for early adopters of their compiler. I was a big skeptic, so
actually I tried to stay away from it for as long as I could. But then at one point I actually
understood their promise, which is that of basically giving you the necessary tooling to be
able to write and maintain a large application that,
at the end of the day, executes in JavaScript. JavaScript, as far as I’m concerned, and you guys know as well,
actually fulfills the promise made by Java, if you would like. You really write your code once and it really executes everywhere. However, if you keep on
adding people to a team, or if your code is growing and growing and you have multiple pieces that you need to stitch together, at one point, you’re gonna be a little bit scared to make changes. So I personally wrote
lots and lots of tests to cover my code, but
then every now and then I will need to change
something in someone else’s, like I just need oh, now they’re
calling me in a weird way, let me fix it up a little bit. And every time I would do this I would get a little bit nervous because
I was never really confident that the changes I do are
really gonna work in the end. So that’s actually one of the things that TypeScript tries to help with, which is it’s actually trying to give you an extra layer of security. More specifically, it protects
you against your own stupid. So how should I say this? It’s more like a safety net. I see it as a safety net
and as a set of tools that allow me to be more productive. TypeScript, unlike many other languages, does eventually compile
down to JavaScript. But it is a superset of JavaScript. So what does that mean? That means that any
JavaScript you can write is actually TypeScript as well. So TypeScript just adds some
optional syntactical constructs such as type annotations and interfaces, and generally makes authoring
JavaScript code more pleasant. All of these things, they
mostly come only with benefits as they allow you to have
rich tooling at compile time. And all the TypeScript
at the end of the day compiles down to JavaScript. So at runtime, there’s
zero cost associated with writing your code in TypeScript. Okay, so I would introduce a little bit how TypeScript works, and I hope you get to see my screen. So here I will just
launch Visual Studio code over a simple folder, which
contains just a single file. I hope you can read it. And this one simply contains
a method that sorts an array. And for each member of the
array, it does a compare on that array name. Now as I said, any
JavaScript is a TypeScript. So if I just go ahead and
rename this .js to .ts, besides the thing in the
corner here that tells me that this is now TypeScript, nothing really has changed, right? Because this is all valid TypeScript. However, you see at this
point that if you just keep only the JavaScript subset
of the TypeScript language, you don’t really get a
lot out of it, right? So TypeScript is good, and they know that there’s a method, sortByName. And they know that it takes an argument, but they have no clue what
this method is supposed to take and what this method
is supposed to return. So I can just pass in five,
and they will be happy with it. Now the true value of TypeScript comes in once you begin a little
bit to decorate your code with a little bit of explanation, which would also help you in the future. So for example I have here a snippet that actually the sortByName function is supposed to take an
array of person, right? So I will just add it over here. And all of a sudden, not
only did I communicate this to myself in the future or my teammates, that this function should be called with an array of persons,
but all of a sudden you see TypeScript lighting up and it’s already beginning
to show us some problems with our code, right, which otherwise, as far as we were concerned,
looked perfectly reasonable and it would just fail at runtime. Now what’s interesting about
this is that you don’t have to pollute your code and right
at each and every location the type of your variables. So for example, result is
inferred to be a person array. You don’t need to say it again,
oh result is a person array. Or basically, here, x and
y, when you call sort, they already know that x
and y should be a person so you don’t need to do
anything there either. So all of a sudden you
get all these rich typing only by just, more or less, explaining what the argument is passed it. Even better, they already know
what your function returns, which is, given a person array,
it returns a person array. Now this is basically
keeping safe from stupid, like you would just run the
compiler and it has errors. So you would not ship your code. But the true power of TypeScript comes in when you wanna work, right? So when you would just
type now, they already know that x has a member called
name, and name is a string. So I can do localCompare, and
I can compare with y.name. So if you do this from the beginning, this mistake wouldn’t even have happened because you would just
autocomplete localeCompare, and it’s not localCompare, right? It’s just a typo. So I can fix this, and now it’s happy. Of course it’s not entirely
happy as I give it a five. So I can, for example,
give it an empty array. And yeah, basically
this looks a lot better. Now what we can do is
we can try to generate. I mean, so far all you’ve
seen, all these squiggles, they were just inside the editor, right? Nothing was really happening. You still cannot see
the result of your work. But if you just, for example,
run the TypeScript compiler and I will just launch
it to watch this file, it will automatically
generate next to this ts file the JavaScript file, right? And as I said before, this is
a hundred percent your code. The only thing that happens is that these type annotations are dropped. So there’s no more colon, person array. Also, the interface we
use to describe this just disappears as
JavaScript does not have such a construct. Now the nice thing about
this is that you can use, for example, constructs
which may be not all JavaScript runtimes have. So if I now use a lambda function here, TypeScript will down level compile it to a normal function. So you can make your
code nice and beautiful and use new, ECMAScript 6 or maybe ECMAScript 7 already features and still down level compile it to run in any environment that
JavaScript runs in. Another interesting thing,
which I said about it, so let me for example in here create something. Now as you see, when I do Control + Space, they know I’m supposed to give
in an age because a person has to have an age according
to my definition of it. Let me make this even more complicated. So this is just, for example, how a class gets down
level compiled, right? So since ECMAScript 5, it
only came out in ECMAScript 6 that you can actually use the
class syntax in JavaScript. They down level compile it
to the idiomatic JavaScript you would have written by
making function Greeter, Greeter.prototype.greet and so on. Something which is very
important to recognize, and here let me just stage these files that you get to see the diff, is that right now I can just,
for example, go on this name and I can just say, right click, rename. I can call it full name. And all of a sudden,
TypeScript knows to change exactly the locations it must. So if you look at the diff,
it basically left alone this name over here because
it knew that this name is not having anything to
do with the interface person or with the rest of the code, right? So it’s pretty cool that you
can now make a refactoring in your code and have the
confidence that your code is still executing the correct way. One last thing I wanted
to show you on this sample is so if I now run TSE minus in it, this basically creates
a ts conflict.json file. And more or less, this
contains instructions for the compiler, right? It tells it the module
system I’m using for my code is CommonJS, and it says
exclude some folders from it, which in this case they
don’t really matter. But now if I run the
watch, I can for example, say oh look, I know for a
fact that I’m not targeting any ECMAScript 5, right? I know for a fact I’m not
just targeting any browser. I know I’m targeting
node version six and up. So I can just say node
version six supports ECMAScript 6, I can
just change this to a 6. And what will happen is
that, oh sorry I mistyped, let me do it again, is that
now they generate the code by keeping the native syntax
that ECMAScript 6 brings in. So now they do use a
class when you write it because node actually supports
it starting version six. They do use lambda functions and so on. So all the time, your code can be nice and can be already up to
the latest ECMAScript spec, and you can still have it run
in any environment you have. Now let me close this
for a second and maybe show you something a little
bit more interesting. Here I also have the same sort of idea. I mean, the idea behind it is the same. Can we just try to wake this guy up. Yeah okay, light looks more happy. So for example here, this code is using destructuring, right? So there’s a function
called point that it gets passed in two numbers. This is the new, this is
new ECMAScript 6 syntax that you can just create a literal. So that actually means what
you see here on the right. And you can also use the
structuring to extract the individual properties of an object. And TypeScript also down
level compiles that for you. To actually run it, I have to do the same. Should result in the same diff. Now what’s interesting and what
TypeScript already supports is basically async and await. So if you do use promises, you can now write your code a lot nicer. So the idea behind this example
is that there’s a function called delay, and that
returns a new promise. I’ll get in a second to why
the squiggles are there. This one returns a new promise, and this promise would just resolve itself after a number of milliseconds. Now what’s nice about
this is that for example the function ping inside a
loop calls 10 times the delay and waits each time for
this delay to occur. And after each of those,
it basically logs the ping. And then the function
main is just awaiting the whole thing to happen. Now the squiggles you get here are because of a missing feature that
TypeScript has on the road map. So right now they cannot
down level compile async and await to ECMAScript 5. Because if you look at the generated code, so let me fix it and let
it know that I am in fact targeting ECMAScript 6, so now the errors should
go away eventually. Okay, there they go. So the reason they cannot
downloadable compile yet to ECMAScript 5 is
because they actually use generator functions behind the scenes together with the new yield keyword to implement this behavior
and leave your code more or less top-down, right? You write it top-down, they have to mangle it a little bit so they wrap it inside a
helper called a waiter. But at the end of the day, your code stays the same way you wrote it. Okay, so that’s that. I hope that was a really brief
introduction to TypeScript. So okay. Now one of the things
which we did not get right from the beginning were
basically dependencies. So we failed, we utterly
failed to keep track of what each JavaScript file needs and what it provides, right? So that’s the basic rule. If you wanna have a good
system, you should have a way to know what each of your
file needs to run correctly and what it can offer to
anyone else built on top of it. And we totally sucked and
we totally failed at this at the beginning. And one of the reasons for that is because we were using Namespaces, right? So you were using, well
Namespaces being a very fancy word for globals. So for example, in a
certain file we would just reach onto this, I call
it Namespace.Util.Strings and defined a new method
on this Namespace. However, the problem here is
that there’s no correlation between the file that actually does this and the Namespace that’s
actually being defined, right? So here I could call the file Strings.js. I could call the file Utils.js, but the problem is that nobody in my team would know where to actually
find the implementation of the trim function, right? They might be using it everywhere, but they wouldn’t necessarily know unless you’re really careful about it. They wouldn’t necessarily
know which file on disk they should go open and
change so that they can change this method. So renaming files was a huge pain. Renaming Namespaces was
something we never did because we wouldn’t know
where they are all used. And of course if you just
tried to do a string search, as you saw before with
the name case, right, you would just hit so
many different locations you wouldn’t know which one
is a false positive or not. So yeah, I guess this
is a fun way to depict, depict what I think was our
dependency graph at the time. But regardless, so back
then we had our source split up into client and server. So we would have this
tiny little Node.js server that would serve up all the contents and it would know how
to read files from disk, write files to disk and so on, but most of the code was client side. So most of the code was
running inside the browser. Now for Node.js, it is a given, right? Everybody that does Node.js uses CommonJS as a module system. And this is great. I mean, it’s great that Node came out with something like
this from the beginning. However, there is a little subtle catch which many people are not aware of. So for example, how do you
think Node evaluates this code that you’ve written, right? So what they do is when
they load up my module.js is they will more or less
read this file from disk. Yeah, they do a little bit
of wrapping where they add the dir name and so on, but then they will go on and evaluate. And as part of evaluation,
you see the problematic line over here is that they
basically have to make an implementation of
this require function. And if you step in and step
in and keep on stepping, like until you really reach
the meat of this require, you will see at the end of the day it does an FS read file
sync or equivalent, right? So it will literally
block the VM execution, the JavaScript VM will be blocked until it goes to disk and
tries to read this file out. And why does it do that? Because that’s how the
code is written, right? So the code at this point, when this require function returns, it should very well return
whatever is exported from that other file. So CommonJS is great because
you get to tell exactly in a file this is what I need
and this is what I give to you in the form of the export. But basically, the problem with it is that it’s very suitable for
running on a server maybe where you have local disk access, right? You cannot do this in a browser. You cannot expect to ship this code up and then in here do basically
asynchronous XHR request to try to fetch your dependency. So that’s basically at the time, when we looked into it,
that’s also when AMD came out. So AMD stands for asynchronous
module definition. And the only real difference in the code is that all the dependencies
of the actual code are in a way extracted
outside in an array. So what this means is
when you actually look at and run this code, you can
capture, yeah, this is the code I need to execute inside this function, and these are the
dependencies I need to fulfill before executing that code. So basically, if you
write a loader like that you can then asynchronously
fetch this dependency ID. And once you have that and
once you have resolved that, that’s when you can actually
run the actual code. So we actually stuck up with AMD, and I will give some more
examples why we find that useful. So this is from a time when there was no other module system, right? There was CommonJS and basically AMD. Okay, yeah before that, the
good thing about TypeScript is that it actually supports
that you can compile your JavaScript to either one of the two, or even something they call UMD, universal module definitions. So in TypeScript, you
just use the new format for dependency that will come
up in ECMAScript 7, I think. So you just write your code normally, there’s no spaghetti,
there’s no define function, there’s no nastiness. And then you just, by
the flick of an option as I showed you there
in the TS config.json, you make it compile and it
generates CommonJS looking code or AMD looking code. One of the advantages of
AMD, which we began to benefit from early on, is the lazy code loading. So one of the things
we do is, for example, if you open this up and
you know we ship with like 30 languages, right? We have PHP, C#, I don’t even
know them, Ruby and so on. Like, there’s just so many of them. And you know what would
be really, really, stupid is that if you would open a txt file, all of a sudden you will
load those 30 languages just because they’re there, and try to start them up
just in case they’re needed. So what we’re actually doing is we split our code up or
we have a sort of like a contribution system
where on the one hand, something which is most
like, I don’t know, 30 bytes or whatever, when
you minify it and so on, this just describes to the system that there is a language called PHP, which should kick in
once you open a PHP file or a PHP4 file or a PHP5 file. And once you do that, once
you actually do open it, then you should load the
module at blah, blah, blah and instantiate the class
called blah, blah, blah. So basically, when you open a text file, the only thing that is loaded
is this piece over here. And then only later,
when you open a PHP file does the entire PHP thing get loaded. And this, yeah, okay, sorry. So it might look that
it’s smaller but in fact this is lots of code over
there to actually implement PHP and all the semantics and
all the coloring and so on. Yeah. So yeah, we like to think of
it like an iceberg, right? Like we have a little part
which gets synchronously loaded that describes, that the
other part should kick in at one point or another. And then hopefully,
the large piece of code that actually knows how to
do PHP is only lazily loaded. Another thing which we do
is we take advantage of AMD loader plugins. So whenever we write our code, by now it’s all done in TypeScript, we can just have like, oh sorry, besides the regular
dependencies, we can just write a dependency to the CSS
that this file needs. So for example, this is just like, I just took some lines
from the code that renders the current line highlight. So when you move around in the editor, we render a little highlight
of the current line. Now how this highlight works
is that in the end of the day, it creates a dom node and
it sets a certain class name on this dom node, and then
it maintains it, right? If you move your cursor up and
down, it moves up and down. But essentially, it’s just a
dom node with the class name. And of course to make
things nice, you wanna put the styling of your app
outside of the JavaScript or TypeScript code, so
you put in a CSS file. So now, whenever we end
up loading this file called currentLineHighlight.ts, this dependency will show up. We will actually invoke the AMD CSS plugin which is indicated by this bang over here. So we will load first
the plugin called vs css and then pass to it as an
argument the rest of the thing. And the rest of the thing
is more or less the path how to find the CSS file. So we actually make use of
this both at development time when you wanna run something. And we also make use of it when we bundle. So that’s one thing we do and we actually keep on
doing since a very long time. And if you wanna take away
something with you today is maybe you should
really look into bundling and minifying your code, even
if it runs on the server side, which sounds so stupid but it’s true. So for example, this is the startup time of Visual Studio code. This is split up into three. So the first piece is
basically just a given. That’s just Electron. It takes a little while to start up, it needs to load 40 megabytes of goodness. We love that, we wanna
have all the APIs of HTML5. We wanna have all the APIs of Node. It’s what it is, right? It’s a given cost start. It’s basically the same cost when you open Chrome, for example. There is a certain cost until
JavaScript finally kicks in. The one at the end is just logic, is just some code which
renders the first time things start up. And the thing in the middle
is the actual time spent in loading and evaluating the code. The first one is when we run with maybe a thousand different files. So this is all locally, right? Everybody says you
should bundle and minify because each bite you
sent over the wire cost. Each request you make to the
server has a certain latency, and you should try to minimize those. But this is all running locally, it’s all running off of my SSD. So what I found very interesting
is that simply by bundling all the files into one, you
actually get quite a benefit. So this is all the same code. You end up shipping the same thing, you don’t even change
anything in your code. It’s just a matter of how you package it. And then we thought oh
yeah, let’s just put it all in one bundle and not minify it. Why minify it, right? Maybe people want to edit and
look and debug it and so on. But then even minifying helps, and I left it here as a
link at the end as a hint. I don’t know if you knew
this but for example, V8, if you have a function like at one point you can go on a function
and say function.toString, right, and this shows
you more or less the body of the function. Now if the body of the function
is over 600 characters, that function will never
get in line anywhere regardless how many times you call it, like maybe you have a big fat
loop of going to 10 million and you call the single
function that adds two numbers. That function will never get in line if it’s over 600 characters in size. It’s just a number. The V8 guys came up with
it and they ship with it and they don’t care. But basically, if you minify your code, and it sounds so stupid, right? You just minify your
code, it will more or less extract the comments, it will make all the identifiers smaller and you might have a very good chance that all of a sudden, a function which was not in
line before, is now in line. So yeah. Yeah, after we finished
and after we actually moved all our code to AMD, we
were very happy about it. Okay, so this is more or
less the basics, right? Like, we finally, after a while,
we figured out the basics, which is like yeah, manager
dependency, duh, of course. I don’t know, use good
abstractions like promises and classes and so on, of course. And at one point, our code just
kept on growing and growing. I mean, you’ll notice in
the end, I don’t even know because at this point we were split into so many repositories that I
don’t even know, I just gave up. I didn’t feel like
writing a script that goes and pull sources from different repos and tries to eliminate the tests and so on to figure that out. So as we kept on growing, I actually gave up my huge anti-TypeScript opinion. And the reason for that, so
this is actually a funny story. So I don’t know if you
were paying attention in Eric’s talk this morning,
but we basically now ship with Internet Explorer,
starting with IE 11 and what they now call Edge. So when you do F12, if
you’ve ever have done that, you can debug your
JavaScript in there, right? So that’s actually our component. And one of the things they
asked for is that people, when they do that, sometimes people, they end up maybe stepping by accident inside jQuery.min, right? Now jQuery.min is not the worst file, but it’s a file that contains everything on the first line, right? So we were like yeah,
yeah, just take our code, our code is awesome. The first thing they
tried, it failed miserably. And then I was going home crying. But basically, what I realized
is I had all of a sudden, between my view code and
between my model code, so the editor is split
up into sort of like a viewmodel controller sort of thing where in the model,
which keeps the buffer, I just have like an array of lines. And in this case, for
jQuery min, I had an array of one line. It was just like a very long string. And I realized I
actually, all of a sudden, needed to plug in something
in between the view and the model, right? I needed to plug in what
is now in the source, and it’s called the view model. And now, what this view
model does is it basically transforms the model and
split this huge one line into multiple lines, which
okay, so in plain people talk it’s called word wrapping. So I was just building it up, right? But it’s more or less when
you have a long line, right? And you’ve seen this in
all the editors you use. At one point, the code will
wrap if you wanted to, right? Or it could wrap at the
level of the viewport or it basically maybe wraps
after a certain number and so on. But basically, I had to
introduce all of a sudden in all this view code,
which was already like tinkered and optimized, I
had to completely replace the data structures that it
no longer goes to the model but it goes to a view
model which has the chance to do something with the actual
text to make things better. So instead of spending
three weeks doing that, I spent three weeks going to TypeScript and more or less two,
three days doing that because it was a lot easier
once I was converting my code to TypeScript, it
was a lot easier to see what sort of data
structures my view needs. And then I would just
implement them and provide them through this view model. Yeah, I think this is my speed. I don’t know if this is
really important like yeah. And okay, we also have
some team-specific rules. So for example, we try
to put return types. Like as I showed earlier,
that TypeScript can guess it. But at one point, you’re
actually gonna burn a little bit of CPU. Like if you have a large,
large project and you let it infer everything, it’s
gonna like take a while to infer everything. So we try to help it
out by actually putting the return types on
public methods and so on. And yeah, just doc the
staff and TS link it. Yeah, this is an example
also that basically, this is from my code. So this is supposed to say statMarkerId, but in JavaScript it’s
perfectly fine to say delete the key undefined, right? So I had a range, and statMarkerId did not exist on this range. So this would evaluate to undefined. And then the code would just
say delete object of undefined, and that’s perfectly fine. As far as JavaScript is
concerned, that’s perfectly fine. And this was causing
like quite a memory leak because every time you
would type, I would like make a little marker
to remember something. I don’t exactly know what
but every time you type, I would have one of these
guys leaking, right, and I had no clue about it
until I converted my code. Another thing, which I
found interesting, is that what you can do with TypeScript is you can basically compile, so
imagine now you’re two teams or maybe you’re two different projects. So for example we consume,
from the TypeScript guys, we consume this typescriptServices.js
that they produce. That’s just like a big fat JavaScript file and you can throw to it TypeScript, you can throw to a JavaScript
and it will just tell you all sorts of goodness about it. That’s basically how the
demos I showed before in the editor work. We more or less take the
buffer that you’re typing, throw it inside this smartness. They’re really smart guys,
they know what they’re doing, and they just come back
and offer suggestions. They offer hovers and so on based on it. And we do that by them
basically shipping to us a bundled minified JavaScript file accompanied by a DTS, which
more or less describes what is inside that JavaScript. And conceptually, this DTS, that’s the API that we, between our projects, have. So if they make up a new version,
we will get both new files and we will just compile
against the new DTS and we’ll just get errors
wherever they have decided to drop a certain method
or to rename it or so on. We do the same when we
ship the Monaco editor, this thing that is a library
and runs in a browser. And we also do the same
whenever you’re in an extension, you get a vscode.d.ts that
describes to you the API of TS code on top of which you can build. There’s something also cool
that TypeScript supports. They’re called decorators. In this case, they’re
constructor decorators and we use them to
actually get really nice service injection. So what they do in this case, this is basically an action in the editor. It’s the action that, if
you press F12 at one point, it asks someone smarter than it. And after the promise
returns, it can actually jump to the definition of a certain symbol. So you would do this on a
function, it would jump you to the place where the
function is implemented. And then we actually use these decorators to signalize that we
should inject some services to this action. In this case, I will just
focus on the message service. So later in this code, it
did not all fit in here. This action, if something
bad happens like a bubu, like an error, it actually calls
inside this message service and says show error. Now what this means is
when you run VS code, we have an implementation
of the message service that shows a little drop
down at the top, right? So here, imagine I was
writing my cool Fibonacci code and then I was saying go to
definition, it didn’t work. It always works by this, which is fake. And then it would just
show a message at the top. Now the same code that I’ve shown, which is this go to definition action, is also shipping with the Monaco editor, which is the library that
runs in the browser, right? All of a sudden here,
we do not own the UI. We do not own the exterior of the editor. So we don’t know how this
message should be shown in the app. So the default implementation we ship with of the message service is
just this stupid console log. But of course we have ways
for people that integrate us to substitute that
implementation with their own. And finally, inside the
test, here I had to do a little bit of paint art to
cut them out so that they fit, we basically say if the
message service inside the test ever gets to show an error
message, let the test fail. So yeah, okay. Two more recent times. So we are actually built
on top of Electron, and I would like to show you a little bit, have any of you heard before of Electron? Okay, cool, nice. So I hope you will not
find it too boring then, but it’s easy. Okay, let me stop the other things. Okay, so here I have a
minimal Electron app. So what is Electron? Electron is basically a very nice marriage of Chromium and Node.js. And how they work is that
you give them a script, in this case it’s this main.js,
I will show you in a second. Oh, okay. Sorry about that. Hey, okay, sorry about that. So this is main.js. This is basically where
I define the entry point to this example. This is a minimal Electron example. So what it does is it uses their API, which comes in form of this
module called Electron. And then once the app is
ready, so usually you would say document on ready, but
in this case it’s more complicated, right? It basically creates a new browser window. It says that’s your
width, that’s your height. And in this browser window, it will load the file index.html. And then once it’s closed, it’s closed; and once it’s closed, it
quits or something like that. So that’s how you should read that. Inside the index.html, that’s where you actually have, that’s where you finally
get both the browser API and the Node API. This main.js is basically
a driver of these windows. So you know how Chrome,
if you launch Chrome and you open a new tab,
they make a new process? And you open a new tab,
they make a new process? So how this all works is that
they have a main process, which is basically an Electron
driven by this main.js; and that process is always
there and that’s the process that communicates to the OS. And that’s the process
that owns like the buttons, the menus and so on. And then you have each render process, which is more or less
what it gets rendered inside a document. So let me just try to run it. I would even show you here. Hopefully that works. So this is a really simple example. It just says hello world. But I wanted to show that
already, when I launch it, right, there’s a main process and then, and this you can recognize like it has… Okay, it’s hard to read, sorry about that. It basically says main.js. And then this one is a renderer process, which is the marriage
of the HTML and Node. Now to better prove that
this is really a marriage made in heaven, inside this renderer.js I’ve let some code in. And I hope your eyes are
not bleeding right now because yeah, on this line,
we are actually making a dom element. And on the next line,
we are using Node API to fill that element in, right? So you should never do this at home. But that’s actually one of the cool stuff, oh why did I kill it, of Electron. So now this thing runs and it just prints its own source code. But this just shows how they allow you to work together and have
something which is both HTML and Node.js. So this is great if you’re
like if you know JavaScript, you know HTML5, you know Node, all of a sudden you can build a rich app and ship it very easily. One thing I wanted to point out is that this main.js is
a very important process. So inside this main.js, you should not do what I’m about to do now,
which is more or less it’s a set interval. And what it’s doing is
its computing Fibonacci, which is always good to compute. Everybody loves that. And the number its
computing on grows, right? So this is more or less exponential in big o notation and so on. So if you now run this, what will happen is, you
see as I’m moving the mouse, at one point you see how the
thing, okay, it’s hard to see. Maybe we already lost it. But at this point the
whole thing goes down because how Electron inherits
the process architecture of Chromium, so this main
process, if it’s now super busy, it will basically bring
down everything with it because even a mouse move actually goes to the main process, and then the mouse move is sent over to the renderer process where
you actually get a chance to do something. And if your main process is
busy, your app is sort of dead. So this is just a little bit to motivate. Okay, thank you. I know why I did this. I deserve. This is just a little bit to motivate what we do in Visual Studio code. So as I said before, this
is basically what we get from Electron, Electron
gets this from Chromium. And once you run it, you get
a main process over here. And for each window you
open, similar how in Chrome you open a new tab you get
a new renderer process. Now what we’ve done is that
as I’ve shown you before, it’s very dangerous to do
actually anything in that one because if you, by
accident, have something which is a little bit
complex or a little bit maybe takes two seconds, all
of a sudden all your windows might not get the UI events and so on. So we actually create a new process where we do things such
as checking for updates, because this is something that
impacts the entire product. Or we do things such as
managing extensions and so on. We have other processes. So for each renderer process,
we create an extension host. And we also launch on-demand processes. Like if you do a search,
we will launch a process, it will quickly search on
the folder you have open and give back the results and so on. Yeah, we also have web workers. So this is all making the
thing very complicated because each one of these processes, they have a different
execution environment. So the blue ones are Node. The dark blue one is Node
and a little bit of Electron. The purple one is Node
and Electron and HTML5. The green one is just
JavaScript and web worker API. So today we don’t have a
great solution for this. So it’s still something
we’re trying to figure out. But we do use a folder
path name-based convention sort of thing. So we have code under Common,
under Node, under browser and so on. Another quick demo, I’ll try to hurry up. This is basically how
do we actually develop Visual Studio code. So okay, I hope you get
to see it this time. So basically, to develop
Visual Studio code, you more or less clone the VS called repo. You do npm install conceptionally, and then just run gulp watch,
which is just something I’ve already done before
because I don’t want you to be bored today. And one of the things
we’ve invested in is that once you launch the sculpt
watch, it takes a little bit to warm up. So it’s like takes maybe 30, 40 seconds. But then every little change
you do becomes incremental. So if I do changes inside
the VS code TypeScript code, this guy to the bottom kicks in. And just like that, like
in under a second or so, it basically has compiled
and generated the JavaScript. This is something which
we found very valuable. Like this cycle of typing
something and then being able to run it the next second
is very important for us. So to actually run it, we just
have in here a script/code. And then this runs Visual Studio code in development mode, right? It runs out of the sources. It’s not package, it’s not bundled, it allows us to do nice
debugging and so on. One of the things I wanted to do here is, and it’s of course motivated
by an interesting thing… So I don’t know if you’ve seen this page. So at one point, when I
registered for the go-to thing, they asked me to give a
fun tagline about myself. So I thought okay, what’s
cooler than drinking beer, which I love to drink, right? And apparently, in the
meantime, they renamed the field to say what company you work for. So that’s why I’m the guy that
says my job is to drink beer, which is a great job. So I thought then I just do a demo which actually shows I’m a
beer drinker professionally. So here I will just make a little change to some CSS of course, and
I’ll just do the right thing such that when you refresh, now, all of a sudden, you get beer in your editor background
everywhere, right? So this is just the best. Who needs to read code? You can just say oh I could not read it because of the beer. So yeah, that’s a tiny change. Something else, which I wanted to show, and that’s something we care and we do. So let me just bind these keys again. So this just adds some
commands, which I have locally. We don’t ship this because it’s
just some performance test. And now it just bounds them to F2, F3, F4. So what they do is they do
lots of stuff like the F3. Okay, I have to refresh to get it, sorry. So if I now press F3, it’s
just a performance test that I run every now and
then, which is just acting as if you would just
hold down the cursor down and just like scroll down, right? And this is one of the things
that we use to profile, and we try to really put an emphasis on making our editor performance. So let me remove a little
bit the background. But basically, what we do, and this is the one I
actually like the most, is the F4 one. And I hope (mumbles) see that. So this is me usually working, right? So you see my productivity
going up like crazy? But this is just a test
which is more or less typing out an entire file. And usually, how I run
this of course is I open up developer tools and I
profile it and then I try to dig into what’s happening. So that being said, this
is actually something which I wanted to share. It might or might not help
you in your day-to-day life, but I wanted to share this
technique that we end up doing in the editor. So this is actually
called virtual scrolling. And what I captured here
is, on the one side, I’m scrolling inside the
editor; and on the other side, I have shown the developer
tools, the Dom nodes, right? And you see, okay, I try
to do it very slowly that as you scroll the lines
down, the old lines go up. They get pushed and
then eventually removed. So you get to see that they
get removed at the top. And new ones come in,
and you see they have this nice animation which
shows you which ones are fresh and so on. So this is all very
good, this is of course something which we had. Of course this doesn’t really help you if you have a really, really long line, like the F12 guys asked us to fix. But it does have some
interesting problems of its own. So maybe the guys in the
first row can read it. Do you actually know what this is? This is jQuery, but it’s not
here because it’s jQuery. This is jQuery minified, right? So as I was saying, this is actually, if you show this to any guy
who’s written an editor, they will tell you to
go out please because this is just the worst
you could do, right? Because at one point,
if you think about it, when we have to render
this; and we render it as each little different color text, we make a span out of it. So we do create a Dom
node, and it’s a span. And for example, jQuery
min in the first 42 lines, it generates, we need to
render like 3,500 tokens. Meanwhile, a normal file, like
this is jQuery non-minified, is yeah, only 280 tokens because mostly they have a lot of comments, which I love, because they’re all green, which
means performance is great. So write comments in your code. But basically, the problem we were having, and something which I was
doing, is I was profiling this and trying to see how it goes. So even though the frame rate was up, the CPU time was quite high as well. And one thing I would
recommend is to get to know the tools you have. So I don’t know if you ever used this one, this enabled pane flashing. This one gets to show to you what actually Chrome paints on each frame. And after enabling this, I
could see that on each time you were scrolling, Chrome
would paint the whole thing. And one of the reason it
was doing that is because as part of scrolling, I was
just moving the position of the Dom nodes, right? I was just saying position
is now minus five. Okay, we have to cut it short. Anyhow we used, in the end, translate3d. Yeah, these are some links. And yeah, thank you for
listening to me today. Yeah.
(audience applause)

5 Comments

  • Reply Gulshanur Rahman December 3, 2016 at 7:19 am

    Make speed 1.5

  • Reply JaysonSunshine December 11, 2016 at 11:14 pm

    Thank you for the talk, Alex.

  • Reply Andrei Simionescu April 8, 2017 at 12:09 pm

    Great talk!

  • Reply Andrea M April 8, 2017 at 2:32 pm

    Really interesting talk, although I wish you had spent more time on the last 5 minutes which were super interesting.

  • Reply Jan-Stefan Janetzky April 10, 2017 at 8:57 am

    he's ranting about commonjs / require like he's believing node processes do more requires than they do during startup.

  • Leave a Reply