Please Go Listen to DotNetRocks Episode 1075 SPA Using Knockout With Steve Sanderson
Please Go Listen to DotNetRocks Episode 1075 SPA Using Knockout With Steve Sanderson
Christmas break is a great time of year for me. I get some time to separate myself from client work and try to relax and catch up on things like podcasts that slip past me during my normal week. Thanks to the magic of noise canceling ear buds I used the drive to Aunt Rachel's house to catch up on some DotNetRocks episodes. The first one I listened to (no not the fantastic episode 1024!) Steve Sanderson talk about building Single Page Web Applications (SPA). I was very excited to hear the topics Carl, Richard and Steve covered because they reflect my experience and philosophy so well.
Carl and Richard interviewed Steve at the New Developer Conference in London and parlayed his experience working on the Azure portal into a fantastic discussion about building single page applications. Every developer and architect should invest an hour listening to the interview. For the most part Steve's comments, positions and experiences reflect mine. At this point I have several dozen single page applications I have worked on, advised or analyzed for myself and clients. They have ranged from something simple like this Blog to large scale, extensible applications with hundreds and hundreds of views. The largest to this point had over 800 views. I started my formal SPA commitment around 5 years ago, but the journey started way back in 2008. What I would not give now to have access to an interview with someone like Steve Sanderson on SPAs back in 2008.
The guys started the interview talking about one of these most important topics when it comes to SPAs, memory leaks. I have a rule, the application should be able to be open and be used for at least an entire week without needing a reload (hitting F5 or Ctrl+F5). Back in 2010 as I was building a very large SPA I found myself becoming very frustrated with browsers crashing after a few minutes. This was a chronic problem on iOS and Android. After some research I learned the browsers were crashing because the application's memory footprint was expanding too much. At the time there were not tools available to profile application memory. Today that has changed as each major browser has rich memory profiling tools.
There are two parts to memory you must monitor, memory leaks and overall footprint. Steve talked about how he and his team on the Azure portal have learned to track down memory leaks, but also talked about how tricky the effort is. My personal favorite tool is the Internet Explorer memory profiler because it attempts to visually identify leaks with a numeric balloon. You can use this to drill into the memory structure to identify the code causing the leak. However I find it difficult to isolate these causes in source code I did not write. I think there is still some work yet to be done in the browser developer tool space to help developers find offending code to fix. Because chasing leaks is still rather complicated I have chosen not to publish any of my feeble attempts to write an article on memory leaks.
Steve talks about this too, pointing out the Chrome team is constantly coming out with new ways to visualize memory leaks, but it is still difficult to track them down with ease. As the interview progresses he talks about some common memory leak causes, but never goes into detail.
I have found closures, variables defined within an immediately invoked function (IIF) that reference objects, like elements, outside the IIF to be a common leak cause. A common example is setting a variable's value to a DOM element. Once the element is removed from the DOM the variable still references the node, keeping the garbage collector from disposing the node. To combat this I try not to set any variable outside a function to a DOM node. Calling querySelector and other selector functions are very efficient these days, so the overhead is minimal, but the memory savings big.
In this image you can see I have a memory leak in my application. This one is particularly bad because the DOM element contains references to 40 images, causing a very large chunk of memory to not be disposable.
Part of my normal development work flow is to include a memory profiling step. Once I isolate any leaks I work to remove them. Sometimes I ship with them included, but add them to the debt list to be fixed.
The Memory Footprint
Another common memory hog is images. We tend to forget to consider graphics when considering an application's memory footprint. Images are bitmaps in a highly compressed format. When the browser renders an image it decompresses the bytes into memory. I found it interesting when I profiled the movie application I use in my SPA book to see how much larger the memory footprint is for the home view than any other page. The home view contains 40 movie posters, while a single movie view has just one. The memory required ranges from 2MB to 150MB depending on the images.
Why is the Memory Footprint Important? Desktops and laptops have large amounts of memory and processor capacity available, so you typically do not feel memory issues. This is not true on mobile devices, including tablets. When building that large SPA back in 2010 I had a problem with browsers crashing on the iPad and SmartPhones. Memory leaks were a problem, but the initial problem I isolated was memory footprint. Large amounts of markup, CSS and images were the cause. I learned to manage dynamically loading and caching various aspects of the applications to fix my issues. I use the same techniques today, only more refined.
Loading Application Assets
In the time since 2010 RequireJS has gained traction and I have crossed paths with this library many times. I really like how it works in the context of NodeJS and I think the concept can be leveraged to defined a dependency chain for the client, but must be paired with a tool like Browserfy. There is a big difference between NodeJS and the browser. For node the files come from the local file system, which mean minimal if any latency. The browser must load the files across the wire, which has a lot of latency. This is why bundling and minifying is so important.
Every time I have been called into to fix SPA problems using RequireJS, the library is one of the main problems. The crown jewel so far was a SPA with 800 HTTP requests being made at startup due to using Require in ways it was not designed to be used. For the record a fundamental web development rule is to reduce HTTP requests. Most of my applications have 3-6 HTTP requests for non-images.
My latest goal is to make my applications' initial load use only 14kb. This has to do with the way HTTP works. You can read more about this in Ilya Grigorik's book High Performance Browser Networking book.
I was thrilled to hear Steve talking about dynamic resource loading for a SPA. This is a big issue, but rarely practiced. And for the record this is one of the reasons I do not recommend Angular. The framework's architecture does not afford a proper way to accomplish dynamic loading or one that allows proper caching. OK that is something that will irritate a few of you, so let's save that for another post.
Steve is Wrong About SEO
Search Engine Optimization or SEO is very important for any public facing application. Steve says that SPAs should be used for applications behind a login screen, but not for applications like an e-commerce site where SEO is important. In my SPA book I spend 2 chapters defining a dance between the server and client. In those chapters I talk about the concept of a core site.
While this is useful for legacy browsers, they are a dying breed and the need for the core site is diminishing. However search engines still crave this legacy site. As Steve says in the DotNetRocks interview spiders can't click your buttons and drive your application. Fortunately Google has publish a specification to deal with this for SPAs. I have implemented it on this Blog and a few other public facing applications over the past couple of years. I have not seen any loss of search engine rankings and placement (SERPS). I still see my posts showing up under targeted keywords just like they did before I upgraded to a SPA.
Steve and I differ here. Maybe he was not aware of the core site concept or Google's SEO specification. The SEO question is brought up in discussions I have had with developers and clients many times over the past few years, and it is important. But you can have confidence in your SPA not being penalized. Of course you can poorly execute your SPA SEO strategy and fail miserably, just like you can with a traditional web site.
SPAs Require a Different Kind of Discipline
At one point in the DotNetRocks conversation Richard and Steve have an interaction where they agree SPA development requires something different than server-side development. I like how Steve categorized it as a discipline similar to game development. He is right, developing a SPA is very, very different than building a traditional web site and common server-side or service oriented architecture. I cannot stress how important this point is because failure to recognize this is causing development teams to fail left and right.
My personal experience has taken me into at least a dozen different projects over the past year alone. Most are lost or failing already at building a SPA. Everyone knows it is the cool new thing in town, but that does not mean it is easy. I want to point out Steve comments you can probably be somewhat productive in a week, but it is going to take you about a year to become good at SPA development.
The teams I am ask to consult and help with SPA development tend to have a few common issues, one is the assumption that 'all my developers need to know how to do this stuff'. I chose to put that in quotes because I have heard it several times. It is wrong and will cause you to fail. I have heard this where 'all my developers' might mean 5, 100 or ever 1500. The reality is you need 1-5 developers and user experience specialists in most shops that focus on modern, front-end development. The rest of your team can stick with that they excel doing, building back-end code.
The front-end has several distinct differences, artistic value, psychology and a different operating system (the browser). Just my discussion of memory management alone should clue you into the vast difference the browser is from the server operating system. But the front-end is about user interaction and satisfaction. This is NOT something you can unit test. You must have a feel and be willing to have real, live subjects using your application under your observation. You need to be willing to try different user experiences and even more willing to throw out work and replace it with something better. For example the Hamburger icon is not as good as you might think.
This year I was asked to interview many developers for front-end development positions. Almost everyone failed miserably. Most of the developers I worked with for client teams were not qualified for the front-end work. They were good at the back-end, but forced to do something they were not ready, willing or able to do.
This affected me first-hand back on that 2010 project. That project pushed me to the limits to accomplish things that were not yet thought to be doable. I had to hack and whack my way through the application's architecture. Toward the end the stakeholder wanted to make a big push and the back-end was just about ready for the MVP. He made the common mistake of having 9 pregnant women will give you a baby in a month choice and threw some very highly skilled back-end developers in my lap. I was given about an hour to 'get them up to speed'. It resulted in a disaster.
The front-end was fragile to say the least, the concepts very foreign to a team of developers that collectively had maybe a week of traditional web development experience. Plus these were all highly motivated, type super A developers that were used to trying things out on the bleeding edge. Within a week I had this horrible mess on my hands with code that was a gigantic fail by my standards. It was slow, clumsy and extremely verbose. It also created a very fragmented code base. It was a major mistake. I see development teams making the same mistake all the time.
Management, senior architects and business decision makers need to hire qualified developers and UX experts to build the front-end. Understanding how to vet them is the problem right now. Knowing how to ask the questions is tough. There are not many with real SPA experience available today. The ones that are qualified are typically in a very lucrative and happy work situation, meaning they are going to be difficult to poach. Trust me I am looking for folks to join my team and it is very difficult.
Steve also alluded to the concept of a modular architecture. I cannot emphasize this enough. He talked about how his team chose to use Knockout, but if they found it did not meet there needs after 6 months they could easily replace it. I did something like that on this Blog. I launched using an AJAX library, Reqwest, that I use on many projects. However I realized the Blog only uses simple AJAX GET requests. I removed reqwest and replaced it with about 20 lines of code. My architecture utilizes a concept leveraging a provider or interface model. So swapping out library took about 30 minutes, that included writing and testing the AJAX call. Of course this was a simple scenario, but one I can share since it is one of my personal applications and not a client application (stuck under NDA).
This is another area I find Angular gets it wrong. It is a large inflexible and opinionated framework. You cannot remove or replace the components you do not like or don't use. You are stuck with the framework's implementation and only have an option of adding to the bloated code base.
If you look at other popular frameworks, libraries and applications you will see this modular concept being used. Take ASP.NET vNext for example, it is completely modular. Its modularlity is one of its key selling points. Node is the same way. I have been doing some node and express work over the past few months, all completely modular. Your client-side code needs to be modular not only for the developer, but it also enables you to have a better performing application because you only load code you use.
While I was glad to hear the Azure portal team has 'a performance guy' every developer on the team is a 'performance guy' whether they realize it or not. If you do not approach development with a performance first mentality you are not going to succeed. And for the record when I am wedged in a team that does not care about performance I do not thrive. Those applications typically don't either.
When you approach development, especially front-end, with a performance first mentality your code will be better. It is easier to maintain, modular, testable and above all fast. This does not mean you need to be a rock star with pointers and heap management. But it means you need to care about making too many HTTP requests, refuse to load code that is not used, run your images through an optimizing and hundreds of other simple low hanging fruits that collectively make your application load respond instantly.
Performance is one of the main reasons why a single page application is valuable. Without a performance first architecture you are throwing away any advantage a SPA gives you.
Steve's interview is a must listen to podcast. I listened twice in a row I found it so good and I will probably listen to it another time or two over the next couple of weeks. Steve, Carl and Richard make some excellent points and cover some very important topics. I only disagree slightly with some of Steve's positions. As I listened to the episode I could picture the code in the Azure portal and feel the pain his team went through to get it launched. SPAs are a completely different kind of application and they require a new, different developer discipline to achieve. I am glad Steve represented the realities well, I hope you find his interview helpful and stimulating.
For a limited time I have cut the price of my High Performance Single Page Web Applications book by 25% to $6.99.