Scale By The Bay 2018: Ramnivas Laddad, Scala.js in production
Recording: Scale By The Bay 2018: Ramnivas Laddad, Scala.js in production
I'm Romani vas I'm going to talk about putting colleges in production so for last three three and a half years back since last three and three and a half years we have been working on a product which is completely written in Scala front end and back end and I want to talk about experience making such a large application work and we launched it last week so what what was that journey like what lessons we learn and what lessons you can apply in your own product so before a jump the product itself is called learn raga so raga is basic melodic form in Indian classical music so it basically describes what notes you can use what are the ways you can use notes to create music but the ultimate goal is to create a particular emotion so the ragas for happy joy there are ragas for melancholy mood there are August for just being a bit and also raagas for different time and I am a student of Indian classical music I I play flute the the issues I faced during trying to learn this music is what motivated us to bring this bring bring bring this product so you can visit today at HTTP learn yoga calm and it's you can sign up and try to understand what what we do about it alright so let me before I jump into it kind of I want to bring out the relevance of this product so I just looked at our github commits and we got like almost 3,000 commits it's very small team three people team and lot of refactoring happened as we learn more about how to model this music and this is where Scala came into picture quite a bit so our 90 percent almost 90 percent of code is in Scala other is HTML little bit of JavaScript and actual JavaScript it's just half a percent and I don't think we could have succeeded without actually Scala G as especially static typing a part of Scala GS and functional programming part of colleges being available to us so before I jump into other part of presentation let me give a quick demo of the product itself so this is the site it's a let me login so it basically gives you like the front page as you expect use overview of what you can do with this thing what are the possible possibilities here let me login so the one of the core idea in Indian classical music and many other music actually is a composition it's a pre-written form of basic melodic pattern which you are supposed to play grasp it and then improvise on top of it so what I'm going to do is I'm going to actually find a an interesting composition in drug called Kalavati this is actually a more an evening melody but never mind so this is the composition how it looks it's a spreadsheet like environment and these notes you see are like essentially are telling you which which note to play the so these are like I won't go into detail like this doremi there is a similar system in Indian classical music but entering this notes can be very hard that you can see like there are something underscore here sometimes can be a dot above which represents higher octave it can be dot below which represents lower octave and so on so what we offer first thing is we offer a simple interface where you can type in notes as simple as by going to piano and typing in notes so that's the first one of the things we do second given this it's an editor like environment we want people to allow undo/redo they're sort of things so I will undo this and we those notes are gone you can also like select a particular nodes move them delete them copy cut paste etc etcetera so I won't go into all the features but essentially it gives you all the things that you would expect from an editor it's a composition so you can actually play it so let me try to play it [Music] that sound is because of I'm playing through a sound flower it's originally placed purifying [Music] so you can play compositions and general idea is you will go take this composition and put it into a repeat mode and keep playing it as you need so next one we do is we also allow people to practice this session this this particular pattern so for example in this case this pattern is like 1 2 3 so if you play this can I get the microphone in here and this idea is you know this many people learn this music you take this pattern and practice them for like hours at a time and you keep changing patterns the idea is to really understand and learn how to switch from nodes one node to other node in a in a in a correct form and finally and in this case for example one thing we offer is pitch detection and other things but I will come to that part bit later we also offer this something called practice room which basically gives you a accompaniment instrument so now you can play it improvise it without actually having a melody in front of you so let me switch back to presentation and talk about it and we'll come back to this demo bit later as as the things go along so why did we use Scala GS first of all it's Scala I'm comfortable with Scala and all our founders for this product they are all comfortable with Scala we like it for because it's statically typed it offers functional programming it's a fairly mature ecosystem so pretty much any task you want it is a library for that you can use that library it's also good intro it has it offers good interoperability with GS so let's face it write lot of components I'm using those components aren't like something that I need to write it from scratch there are already somebody has written there's a library out there I want to use it as is so I can just take those library libraries and use it from Scala Cheers so I don't have to like write every single line in Scala it also supports isomorphism so isomorphism is essentially ability to write it do it something on server or client so people use it for example to render web pages on server-side and then give it to or render pages on the web in browser we use it in a slightly different manner we do it to share code between JavaScript and JVM so by actually having most of the model that is workable in both JVM Magius we can really gain good productivity by running most of the time developing debugging everything in JVM and then when time comes we test it in the browser and all most always everything just works we have not found any problem where code worked in JVM but did not work in JS we also use react the react is a framework for writing web applications and it's very popular pretty much these days default framework to do any web development it's my facebook it's pretty mature it's very widely used like Scala it promotes imitability so most of the time you will be writing code such that you will be feel very familiar you are essentially making transition from one set of application state to other state and you don't go and change and then expect view to reflect that also react works on multiple platforms we right now deploying on web but it also works with native and if we ever want it maybe are you wanna virtual reality roughly the variac works is you have application state s0 and you have some function transformation on that state to get a view v-0 so and now if you want to go and make some changes so basically a network call comms or user interacts with user types it in then you are making a transition from state 0 to state 1 and now you apply the same function which is state to view transformation and then you get a new new new view but if you do it very naively then you will be rendering page and then something changes and you are going to render the whole page again and that's going to be pretty bad user experience so what react does is it finds out the difference between these two views so in fact those the wait renders it is actually renders into something called virtual Dom and those virtual Dom it finds our differencing ok maybe I need to update only this text field and it only updates that particular field and it does through Dom mutations so it basically gives you that experience of going from one state to other state and overall feel of immutably in immutable application state but without actually associated performance penalty if you were to do it naively reacts component model has two parts one is properties another state so if you look at a top level component like this it will have a property PA a and state and then you will have a component tree with sub components that will take its own properties and it will assume that that property will be typically passed down from its parent component and so on and each of the component may have a state there are two kinds of components take full component and stateless component so they have state they will have it within themselves so states are is not something exposed to the outside property is a way for parent to communicate to the child and state is a way to keep some internal state without exposing it to the world and then so on so every time you go you you keep passing it along those properties some transformation of properties and state to your children and children in turn may have their own state so the coming to the production we want to utilize developer skillset we want to basically do such that you know if you want to work with interns or hire people then we want them to be productive immediately so what does that entail one is writing web application requires a combination of skills it's actually pretty hard writing web application is these days especially with a single page application is is as hard or even harder sometimes than backend application in our case it requires knowing Scala knowing functional programming knowing JavaScript knowing CSS and all the ecosystem around all these tools so what we do is solution is to use the tools and frameworks that match JavaScript world that way if somebody understands the JavaScript world because most of the UI developers are going to be familiar with JavaScript so use those skills and by using the same or similar frameworks so what do we use tools wise with web pack so if these day if you want to take JavaScript your javascript code and deploy it on a to a browser web pack is the way to go it's another de facto standard that everybody uses so it basically allows you to essentially gather all the assets a CSS files images HTML files JavaScript everything and puts it into a bundle form essentially a JavaScript package that serves everything force from Scala perspective obviously we want to use tools that are familiar to Java scholar developers which is Scala just plug in because we want to compile Scala code into JavaScript and Scala is bundler plug-in which essentially drives web back so rather than using webpack directly which we used to do earlier but since colleges bundler is available we switch to using it through that so you don't actually have to start two to one web pack and one SBT you just start it from SBT itself we use a framework or slinky and slink is developed by Shailaja who is here is actually is my son and so therefore it's like homegrown framework for us we can't say not invented here at all all right so the idea behind slinky is if you know react and a bit of JavaScript and Scala you can be effective with slinky you can get started in a in a few minutes in fact slinky offers template so you can do SBT new and the template name for slinky and within a couple of minutes you will have a slinky application running or same same thing for react native so you can have iOS application or Android application running in a few minutes it offers direct mapping of react concepts if you know react and and you look at a stinky code Scala code based on slinky and react code based on JavaScript you will almost feel like this it's almost looks the same with of course obvious differences between the two languages which are not many actually if you look at Java modern JavaScript except for types it basically looks the same as as Scala it also offers great support for external components as I mentioned we want to leverage the ecosystem as much as possible so there is a component out in the wild we want to just take it in use it from our application so slinky using external component is very easy it also keeps up with new features in react react if we look at like last four years how react evolved it's pretty dramatic they starts with they started without actually having react class so you define in a kind of different way then they started to use es6 classes and now they are kind of going with functional components and now they are going back to something called react hooks so it's like it's changes every year basically if you if you did not pay attention to react in a year react will look very different conceptually same so idea is exactly same the picture I showed you earlier of taking component tree and having state property is getting mapped down to children that remain the same but the flavor the syntax that changes every so often so slinky keeps up with these features and that's that's that's awesome because that then we get to leverage whatever is new coming in react so this is how a typical component will look in slinky and react style so in this case I'm showing a volume meter when I'm detecting uses microphone I want to show what is the volume like so I declare here what are the properties I want and so I have amplitude I'm actually not using max amplitude in this case I cut down code and forgot to take it out but anyway I have amplitude at max amplitude and I want to render how should a bar volume bar should look like so I basically put two divs and the width of the inner div is proportionate to the amplitude a sudden I give you the percentage so basically wall you meter will show I'll show that when we give the pitch detection demo and this one was a stateless component there is no internal state so essentially the component is essentially transformation of the property amplitude to a virtual Dom if you want to hold on to some state so in this case the only state is is in is it initialized so in this case the Google button that I logged in at the very beginning of my presentation produced how the Google button you need to first communicate initialize the book the Google GPG API so all that thing you need to manage it so you need to know that when to enable the button therefore we have this initial initialized State this need not be known to outside world so we keep it as an internal state and then we have the remaining code similar this one is similar code in the sense it's rendering a button but the disable state depends on is it initialized or not external component so if I go let me log out so I have this here carousel so these carousel surprisingly takes kind of quite a bit of it becomes pretty tricky to do it right do the right animation everything so rather than writing it yourself I want to use a library called slick karisoke it has nothing to do with scala slick library it just called slick it's looks slick I suppose so anyway so using that component is very very easy so firstly statements are essentially you are saying I want to import those assets the JavaScript CSS etcetera and the last is the only and then there is you want to define some behavior usually react components don't have any behavior because you are not expected to use imperatively you are not expected to say go to the next slide or anything like that you simply say what is the current slide and then it will figure it out how to get to that slide but this particular component happened to how behavior so in this case I am specifying that there is a behavior called go to particular index once you define all that thing then you simply oops then you simply define here slick aerosol and then all you define is a properties essentially you this original component okay tits properties and map it into a type safe form and that's it basically you are essentially saying how is the how should it look from the Scala world and that means it should should it should art should show arrows is it an infinite list scrollable component and so on and on and then you are done you don't actually write a whole lot of Scala essentially you're writing a binding between Scala and the underlying component and that's about it the next thing I want to talk about is extracting performance so in Scala we love actually let me talk about the problem itself force or you know what let me give a demo that will make the problem very obvious so I am going to play a recording and ask I'm going to record player recording actually one of our music advisor Nagaraja Crane it that's his recording commercial recording and I'm going to see how what kind of pitches he's playing let's do it and then we'll talk about it [Music] so this is afternoon melody it's called should the saran rug so these are the the highlighted lines represents the node that are allowed in this raga to be held so you will see that he will never ever stand on a node that is not one of those lines there are actually faint lines in between you probably are not able to see it I will let let it play for a minute [Music] so here I'm drawing detecting pitch drawing everything at 60fps because if I do anything less than 60 frame per seconds which is essentially update every 16 milliseconds the chart looks very irritated it looks like a jumping at you and it's like you can't watch it for any more than few minutes so that becomes a challenging because you want to do whole and record the pitch detect the pitch plotted keep moving this chart at that particular speed alright so I will as interesting as the music is I will stop and talk about how we do it [Music] so our task is to take microphone input connect it to audio source detect pitch and then update the chart and do it at 60 FPS so immutability is wonderful right you know we we will like Scala often because it supports you immutability to the extent you want it's very easy to reason about it because you don't have to say well I don't know if somebody modified my structure in between you don't know you know that you know okay and see how created a mutable object there is no way to modify if you if you wrote it correctly and things like implementing undo redo functionality is like trivial so initially I showed you like I typed in some notes and I just did undo because the way we do it is we when we write we modify composition we put a new composition and in undo stack so we basically take the composition update whatever it needs to be updated as in a copy in a copy put it onto stack and keep doing this so when it comes to undo all we do is go back to the stack and we're done so it's not really a whole lot in it's like a 10-15 line of code and you got under it or functionality so it's all great but if you overuse it or if you don't use it right then it affects performance in certain critical part of code and in our case it has been pitch detection and plotting and everything so initially we did it without you know we try to do as essentially everything immutable and it was a horrible performance and it's wasn't I should correct myself it wasn't that performance was bad it was the garbage collection I would produce so much garbage because essentially we take a four kilobytes of frame every 16 milliseconds we take four kilobyte of frame we do DSPs in was FFT autocorrelation lot of other magic going on there but fundamentally and in that process if we do that transformation saying ok every step takes signal calculate FFT take it square then calculate this power spectrum then take its inverse FFT to other magic if we are if I keep in immutability every step of the way I'm going to create a lot of garbage very fast so what we do is we use immutability mutability as needed so keep it local or to a small subsystem so when we for example in case of our FFT buffer we just keep overriding it we don't once we allocate it we just keep using it as needed now we don't leak it out of that subsystem so we don't have to worry about it reasoning outside of the system but within the subsystem we just say okay you need to know that this is mutable data structure use it carefully and it works beautifully that said don't go and saying okay if something is slower just let's go and make it mutable it's not going to help you major major major major so we basically went to chrome profiled it and understood that okay this is actually problem garbage collection is a problem we can see actually where it's collecting garbage and that time the charge just suddenly stops and then say okay that seems to be a problem then you say okay what is what must be producing garbage you measure that part and then you go from there second part is if I use JavaScript C all this thing is running in JavaScript it's running in browser there's no backend involved when I when I was playing it in fact current out because I the network latency will just kill it so I want to make JavaScript as performant as possible and if I do again digital signal processing in JavaScript without thinking too much I am NOT going to get 60fps so for example if I do FFT in Scala in JVM I get it within three milliseconds if I do the same FFT in Scala but turn it on Jas it's about 14 milliseconds and 14 millisecond is already cutting it I want every whole thing to happen at 16 within 16 milliseconds so if I give 14 milliseconds to one FFT and then not even talking about in words and all those things I'm already out of budget I can't update anything more than 20 fps so what we do is we do FFT in ASM dot J's ASM not Jas is essentially javis is a restricted JavaScript format which can be generated typically by combining C into JavaScript we are considering switching to web assembly which essentially has the same idea you write it in a language such as C or C++ or rust essentially right now it doesn't support garbage collection so as long as you write in language that that does not require garbage collection you are good then second is as I mentioned react tree whenever you change something it goes and modifies the modifies all the sub components it keeps passing on properties and state as needed and the components are updated now if you do it naively then if you change something at the top level potentially you are affecting the whole component tree and yes react will eventually find that okay the v1 and we v-0 and v1 happen to be the same so there are no Dom mutation that are needed needed to run but you want to before get to that point it has to calculate v-0 and v1 and then say okay that's the same same text no no need to do anything we want to avoid even that so react offers something called should component update it essentially says here are old properties here are new properties and state and do you think it's even worth calculating the view so typically you would go and check if there are relevant changes that have happened so when I'm for example in a composition and if I'm selecting this text for example I want this pretty smooth right so I don't want to go and affect every single component okay could even see actually no it's a washed-out okay you can't see it but essentially it's showing in a blue text things it's it's selected elements and to do this you have to be careful because in theory your change selection that means you are affected this part you affected this part you are affected this part and this eventually answer will be well there is no real change needed here but what we do is therefore we write components such as this to basically say has selection change for this particular section or particular line or particular cluster and then we minimize calculation calculating those virtual Dom's as much as possible the next challenge that you face is yes all right the question is should you have to do you have to override should have component update for all components or the one that actually you think performance sensitive yes only for performance sensitive so typically you get a lot more benefit for doing at a higher level components and the tree I mean the reason is you know if you could short-circuit there then the tree below that is not going to get evaluated at all so then you get a lot of benefit out there so what we do is we try to do as much work short circuiting as possible at the higher level component but at lower level you get a little bit more benefit by default it will say shoot yes - it should update because it doesn't know any better now with react hooks another thing there are like more interesting way to express this idea saying okay essentially have effects system where you can say okay these are the property that are important for this particular update and only if these properties have changed then registered as they change and there's a whole system for that but core idea still remains the same so next thing you want to know is how to make work with build time so I'll go little bit fast because I think I'm running out of time so if you were to run test in JavaScript you will see that even if I make a small change in this case actually made no change but even going through the whole build process building javascript bundling it with web pack it took about 13 seconds same thing if I do with JVM the same code then it takes about one second so when I'm developing especially when I'm developing digital display algorithms I'm making lots of lots of small changes experimenting right I don't want to spend 13 milliseconds every single time it gets boring really right you know so what I what we do is we exploit this JVM JavaScript our isomorphism to the maximum possible way so we write most of a code such that it is shareable between JVM and GS and normally during development we work exclusively on JVM environment it's fast we get it working so even like some of the things that doesn't exactly make sense we don't expect it to work from JVM ever for example selection we are no no business of doing selection from JVM we don't expect it to be deployed in the JVM yet we write code such that it is testable from JVM and we share it and that really really has been very helpful that would be one reason I would say Scala J's you should use because you can develop in Jake Scala run it in JVM and once you are once you feel good about it you can use it also of course you get advantage where like for example is pitch detection algorithms we often end in back-end same code no change whatsoever run in JV and Jas when we want to give real-time feedback when we were to do analysis we do it in the backend and there's a full isomorphism you have to of course deal with some JVM GS differences there are not many but all are related to things like HTTP so you want to have if your HTTP or to abstract it out so we can do it ajax HTTP in J's world ok HTTP for example or some other implementation of HTTP in JVM world not nothing no no no deep ideas here is simply like have abstraction have a treat and have implementation that work the right in each environment file system similar thing little bit more interesting this one because there is no file system and GS ID so you kind of have to like create a URL base file system serve it from a local HTTP server and use it the last thing I want to talk about a bundle size so when you compile all this code user has to download this so in our case let me actually show in our case the overall bundle is about 780 kilobytes so most of it is what is this is compiled with Scala GS and these are external libraries that we use react brain dot brain tree dot s FFT and so on it can be a problematic because user has to download this is gzipped eight almost eight hundred kilobytes so user has to download it browser has to unzip it parse it and run it and it takes time if especially if you are on a slow machine or slow network so what do we do but there's another - let me show you that so this is basically tells you nothing about how javascript code is coming scholar J's code is coming from so there's another tool which is called source code mapper it gives you a lot more detail doesn't look as fancy but it tells you like okay my recorder model is overall contributing about 7% of the code if I look at my pitch it's about 1.5 percent and if I look at transcriber it's 0.1 percent so I can look if there are any hotspots unfortunately for our case there are none so this is this is the size we are going to get this is something that I think scholars should colleges could could could help us so one of the things that we do right now before without Scala has changes is we use long term caching of all assets so once you download it javascript or css then we do content based hashing and therefore you don't have to download it ever again until which in the version of course trivial to implement using webpack and we are working on pre-rendering some of the pages it does help with first meaningful paint though there are essentially two measures first meaningful paint and first meaningful interaction user interaction so this will help with first meaningful paint so as you will see the javascript HTML loaded because would have pre-rendered it rather than you getting a javascript that will be the rendering though all components you will get an HTML it won't help however with first meaningful interaction this is where we hope scholar Jase is going to help us with something called code splitting so the way people work in JavaScript is they write JavaScript and each of the module require other module essentially dependency declaration and then some of the declaration can be saying it can be loaded asynchronously and webpack for example or any other bundle a bundling software will take that as a clue and saying those are the boundaries I can cut my bundle sack so for example when I load front page I can declare that all other pitch detection and Composition editor and everything does not need to be loaded just yet however when I switch the route when I go to composition I can say okay now it's time that you should load it load it and then render the page so essentially you are distributing this cost a bundle over each in each view and that's a much better user experience right now Scala gist does not support it it does not support code splitting on neither asynchronous loading this is something hopefully we'll address soon there are actually issues open and that would be that would be really really awesome I'm going to skip this part essentially the point there is don't try to bundle JVM and GS code in one thing and serve it from serve it from one process we deploy separately one is JVM process which does API and there's a J's process nor J's in fact which just serves the static assets that's very very nice we don't in fact sometimes start restart the server for weeks at a time because we are either working in a UI modification more or changing servers so we only deploy that much part last thing I want to say is if you deploy as is this is the kind of errors you are going to get in production if you get a stack trace it means nothing actually to anyone it's basically what the weeks colleges does is it Agnetha is it then Google closure compiler by the time you see a code it's all bubbly goop however you can actually enable sourcemap in production and then you get nice contact which are like essentially giving the scala line numbers and when you click on it actually you show the Scala code and this is really awesome but you don't want to necessarily expose your source code so the trick to do is you in your source map you rather than serving it as is you share it you give some local URL or server that you control and in this case I am saying look loaded from local server 10000 URL so that means somebody people are in out in the wild won't get access to my source map and giving access to source map is essentially giving access to source alright that's about it I don't know if you have any time for question all right shoot yeah [Music] yeah the question is was any difficulty in taking libraries such as Redux in which is popular in react and expressing that in Scala we don't use Redux we did try experimenting with something called diode which is essentially equivalent of Scala equivalent of Redux I don't there was no difficulty really just that you know we didn't really necessarily want it to express our state money in that particular fashion but as I mentioned expressing you know interfacing with external libraries very easy with slinky it's really like you know you analytics will be the same thing so for example we use a graph QL and we use apollo and apollo client is written in javascript but we can just write a facade for that express our types in Scala manner so when we use it once we carefully describe that those types in facade then using it from Scala is very type safe as type safe as we made our facade to be sometimes we get lazy and C J's dynamic which is essentially saying anything goes and that works then to that level yeah any other question all right thank you [Applause] [Music]