Principles of Rich Web Applications
rauchg.com
rauchg.com
My only complaint, if it can be considered a complaint, is that the author doesn't address the real-life costs of implementing all his principles. The main one is the complexity of the server- and client-side architecture required to implement these principles, even for a minimal application like TodoMVC [0].
I agree that user experience is extremely important, and perceived speed is fundamental, but I certainly don't think it's important enough to justify the cost of figuring out how to implement all these principles, especially for a startup or other small team of developers.
Of course, the hope is that tooling will quickly progress to the point that these principles come essentially for free just by following the best practices of whatever libraries/frameworks/architectures you're using. There was probably a time where the basic principles of traditional static web apps (resourceful URLs, correct caching, etc.) also looked daunting for small teams, but that's quite manageable now with Rails/Django/etc. (and maybe earlier frameworks).
Things like stale code on the client is simply not an issue until you've got a lot of people using your app. Don't worry about it.
There is even a TodoMVC example being submitted for Derby: https://github.com/derbyjs/todomvc/tree/master/examples/derb...
Another nice thing is that the templating engine can be used client-side only if you want: https://github.com/derbyjs/todomvc/tree/master/examples/derb...
I see the link to the Stanford course, but I hope it's still in the minority. JS is not a language that I would want to teach to newbies, especially if those newbies are on a path towards a CS degree.
JavaScript is great because it runs in a browser, provides immediate feedback without having to compile/recompile and allows for all the simple flow control concepts mentioned by other posters.
JS shares this property, but also gains the benefits of being widely used in industry and available in every web browser, with a good debugger in Firefox and Chrome. I'd never really considered it as a teaching language, but I think it'd be a great choice.
Why is this important for students, anyway? Shouldn't they be exposed to mutability fairly early on?
http://journal.stuffwithstuff.com/2013/07/18/javascript-isnt...
http://brendaneich.com/2008/04/popularity/
JS certainly does have other influences, notably Self and Java. But from that article:
"As I’ve often said, and as others at Netscape can confirm, I was recruited to Netscape with the promise of “doing Scheme” in the browser....I’m not proud, but I’m happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients. The Java influences, especially y2k Date bugs but also the primitive vs. object distinction (e.g., string vs. String), were unfortunate."
There are plans to switch out Java for Python eventually but no major classes are taught in JavaScript--the closest it gets is a graduate Programming Languages class that spends a few weeks on JavaScript to illustrate closures and first-class functions.
To be fair, though, many undergrads take graduate classes and the department encourages it--the only difference is a 2XX course number instead of 1XX, and sometimes a bit more rigor.
But, like a machine learning course, or a data science course, which also touch on many concepts which are part of a core Computer Science curriculum, it is not a center because the center of a good Computer Science curriculum is the core concepts through which all other ideas flow (algorithms, data structures, message passing and communication, programming language design, compilers and interpreters, object oriented design, functional programming, etc).
Generally all the rest is (and should be) elective. And most curriculums presumably require electives so students can delve into areas they're interested in applying the aforementioned skills to.
I don't need to know in realtime how many people are on my website. I need to know how many there were last week, and compare that with the week before that. Likewise, I don't really need to see a post the instant it was made. At least for me, the internet is great partly because people chose what they do how and at what pace, because it's more like a book and less like TV, and making it more like TV is not improvement.
This is not against the article per se, which I found very very interesting and thorough, just something I have to get off my chest in general. Though I really disagree with the article when it comes to the facebook feed, I think that should serve as an example for what not to do.
Please, think twice, and never be too proud to get rid of a shiny gimmick when it turns out it doesn't actually improve anything. Let's not sleepwalk to a baseline of stuff we just do because everybody does it and because it's technically more complex. As Einstein said, anyone can make stuff more complex :P
Well worth thinking about.
What should happen is the new content is loaded above and instantaneously the document scroll position is updated to preserve the previous position. This will allow new content to come in without disrupting your experience.
While it's certainly true that the first page may load slower, and you'll load a few scripts as well, you never need to reload those again. Frameworks like Angular encourage you to use a "service" mindset that capitalizes on this property.
The longer you use a single page app, the fewer round trips you will have. If you ask me, your communication should only be for raw materials (scripts, templates) that you won't need to validate or request again during the current session, and raw data (json). This is more loosely coupled, more cacheable at all the different levels, and more scalable in large part due to the decoupling.
Once the initial view loads, I totally agree that you should intelligently precache all your resources and data asynchronously in the background to usher in the era of near zero-latency user interactions. Preferably, you do this in an order based off of historical behavior/navigation profiling to best use that time/bandwidth you have before the next click.
I get the impression articles similar to this one that there was once a similar mindset surrounding mainframes and dumb terminals. The future is decentralized, web included.
You could completely eliminate a huge number of round trips by just getting tweet data and user data after the initial pageload. The user data is quite cacheable and you could create a single endpoint that allows the user to precache the required user data (display name, photo url, id, etc) for everyone he follows in one round trip.
They moved away from client-side rendering. Quoting:
There are a variety of options for improving the performance of our JavaScript, but we wanted to do even better. We took the execution of JavaScript completely out of our render path. By rendering our page content on the server and deferring all JavaScript execution until well after that content has been rendered, we’ve dropped the time to first Tweet to one-fifth of what it was.
Decentralized work is a bad thing. Instead of Mozilla/W3C solving some problem for everyone, we get every website coming up with their own clever solutions for nearly everything. (Also, thousands of clients re-doing the work that could be done once and served from cache.)
Also, single-page apps increased reliance of the entire web on the handful of CDNs and framework providers.
I take it you're not a fan of open source, then? :-D I don't think the problem you describe has anything to do with decentralization, though. You can have people thinking they know better than the rest of the world writing COBOL for AS400s.
Further, I don't think you can soundly argue that decentralization is a bad thing. For one, it's the the only way you can scale horizontally whether that's done on the server side or by deferring appropriate work to be done on the client side. For another thing, as OP's article states, round trip time has a theoretical lower bound. The only way to improve performance past some a point is to distribute closer to your users.
I 100% agree by the way that if it can be cached it should be. I don't, however, think that necessarily extends to a SPA. JSON is just as cacheable as HTML if the interface is designed with proper REST semantics.
How are SPAs different from Flash? Sure, you have some remnants of standard HTML there, but with canvas, local storage and JS compilers, those two technologies look remarkably similar both in terms of advantages and problems. You get a blob of non-semantic, imperative code that does non-standard things.
The funny thing is, many of the stated advantages of SPAs can be easily implemented in document-based web apps. A lot of the repetitive info could easily be removed with proper caching, and it would be even more efficient with cacheable client-side includes. Not only it would be roughly as efficient, it would require much less work to implement.
1. Single page apps have many drawbacks that are conveniently not mentioned: Slow load time, memory bloat, persistence of state on reload, corruption of state and others. SPAs are just another misguided attempt at making web apps more like desktop apps. Web apps are network applications - if you remove the network communications portion, what is the point of them?
2. JS is a great tool to make pages more responsive. This has been the case for years and I am lost on why the author writes on and on about it without any poignant observations or facts.
3. Using push (web sockets) is a valuable tool for accomplishing particular features. This does not mean that more is better and we should start using it for everything. Server pull is a strong feature of the web and is arguably a key to much of its success.
4. Ajax is great, no argument.
5. Saving state as a hash value in the URL not only puts JS actions into history, but makes them visible and bookmarkable as well. Push state is a quagmire.
6. The need to push code updates is one of the problems caused by SPAs that is not needed in normal web apps. Even so, this could be solved with a decent, as yet unimplemented, application cache.
7. Predicting actions is overkill. If you focused on doing everything else well, there is no need to add significant amounts of complication. More code = poorer performance and decreased maintainability.
I am working on a legacy SPA now and it's horrible. (Our users have learned to refresh frequently due to uncertainty of state.) I am not sure how much to blame on poor implementation as opposed to inherent weaknesses of SPA architecture.
All of the other things, including history management / back button, are built into GWT.
Yes, GWT can be quick, but it still has to make those extra round trips to get data from the server. If it had an option to render the initial page wholesale on the server, it could get that first impression up faster.
In order to add that to GWT, you'd have to emulate the first few rounds of communication on the server --- incorporate a server-side DOM model & fake communication layer, either using the cross-compiled JS or Java. Maybe leverage the debugging tools?
Also all of these files are cached once loaded, so they don't have to be loaded again until they are changed.
The goal of server-side rendering is to show the user everything he supposed to see when a typical single-page-app loads, shows the a splash page, fetches the initial data and renders it on the page.
From your description, GWT does not offer that.
If you go the splash / loading page route, users will immediately see the loading screen come up.
Plus, with the 'render on server' route, if users visit any other page, they will have to wait for a full page reload, whereas with an SAP, they will just see a loading icon for a second or three before the new page will come up.
Its a lot more responsive overall, and the pros outweigh the cons in my experience.
It adds significant server complexity for what some would call a minimal improvement in setup time. The article is advocating it for exactly that: overcoming that (maybe minimal) setup time on the client, which includes a one or more server round trips.
In many cases, this will undoubtedly be premature optimization. But if the framework made it easy, then it might simplify your transaction model. You could unbundle transactions, knowing that you aren't paying a latency tax on them, at least on startup, since it's all happening on the server.
GWT has always supported minimizing the number of HTTP requests. For example, GWT RPC long supported pre-serializing the first couple of requests into the initial HTML, so that you don't need to do an AJAX request before you can render.
It's also one of the first, if not the first, framework to support automatic asset packaging, sprite-sheeting, etc (since 2009). This inlines all CSS, small images, and other resources into a single chunk, which can be downloaded in one HTTP request.
I am working on an architecture I call POA, Page Oriented Architecture, which combines the best of Web Components, React-JS style binding, and server-side rendering into a single framework for GWT.
Google already uses "patching" to deliver small pieces of JS, we call DeltaJS. So when you visit inbox.google.com, the server computes the diff between what you have in local storage, and what has been recently pushed, and sends down a patch that updates your cached copied. I'm looking at combining this with ServiceWorkers for GWT so that all GWT apps by default can leverage offline and delta-js application. Hopefully I'll have something to show by the GWT.create conference (gwtcreate.com), we'll see.
Does that mean that if a user has an app cached, but something changes within only a single split point in that app, then the user only has to re-download that split point, vs re-downloading the whole app again?
I went looking for Inbox's template engine and came across:
https://code.google.com/p/google-jstemplate/wiki/HowToUseJsT...
The "jstcache" attribute looks the same. Although, looking at the code.google.com project's source, it doesn't look like any code/non-wiki changes since ~2008. So maybe a forgotten attempt at open sourcing the/precursor-to JsLayout?
POA sounds interesting; hopefully it'll have a great unit testing story too?
The central idea of Page Oriented Architecture is to return to the Web 1.0 era of "A URL for everything", where the application consists of a bunch of pages, and if desired, any page can be rendered server-side.
However, what really happens beside the scenes is GWT processes all the pages for the whole site, synthesizes an application, which each page between a GWT "splitpoint". So you have your cake and eat it too. An application that is developed a 'page at a time' with crawlable, indexable, server-side URLs for everything, while at the same time, you get a monolithically compiled, globally optimized, Single-Page-Application out of it.
There's an old set of slides here: https://docs.google.com/presentation/d/1JobclkctBvciYZ8CzHIo...
However, that's since been super-ceded by a system I'm working on that makes everything look like Polymer, only it's monolithically compiled and optimized, and using ReactJS style virtual DOM-diffing.
I want to combine this with DeltaJS techniques and ServiceWorker to produce offline-by-default high performance mobile web apps out of the box.
Server side rendering is on the roadmap tho.
Instead of trying to find one tool that solves all of your problems, try understanding what your problems are. Once you have a list of problems, ask how you can solve each of those problems. When you have solutions, THEN you figure out which technologies are best for implementing those solutions.
After we hit our initial development deadline, we had time to actually research Angular, Ember, and Backbone. We settled on Backbone on the grounds that we could refactor our codebase incrementally, and it seemed to fit our use case best. Unfortunately, this later led to clashes with the "write it myself" dev, as he didn't want to use any outside frameworks beyond jQuery.
Anyway, having used Backbone for the last year, it definitely has a lot of limitations, but it also provides a very definite set of well-tested pieces that clearly help solve common development use cases. So yeah, very much in agreement with you there. Why try to totally build something from scratch if someone else has already done it for you, and it's been battle-tested by other developers?
I personally prefer (and find it more fun and a better learning experience) to build things from scratch with no frameworks at all, but I don't think it's a very savvy business decision unless you're doing something very novel.
It is really different and extreme -- in a good way. It supports both server side or client side rendering. It seems mobile performance and concurrency were the main goals in building it.
But it also takes things to another level, such as it establishes a Websocket connection after it loads the page then, lets you shuffle page data as a binary (!) payload over.
I've only played with it as I am not doing much web related stuff at the moment but it looks pretty cool.
Basically, you build your application frontend with pure Erlang terms to construct server-side templates, then make live changes to the interface over and websockets (as of the soon-to-be-released v2.3), ajax, or comet (if websockets are not available, or the websocket connection crashes, it'll establish reconnection or fallback to comet/ajax).
I recently did a talk at the Chicago Erlang Conference about it: https://www.youtube.com/watch?v=nlV4gm8SpVA
By building on ShareJS, DerbyJS uses Operational Transformation for collaborative editing of data in realtime, which means that users can see data updated immediately in their browser even if other users can edit the same thing at the same time. This is the same approached used by Google Docs.
I can see that in terms of bandwidth, SPA can be more efficient then normal HTML page. But this makes a few assumption. First, that your JS package never changes. As soon as 1 character changes in your package, the cache is invalidated and the whole package needs to be downloaded. Like you said, it's application specific. But if your app has ~3 pageview per session, it becomes very hard to justify the use of a SPA.
As for acting as soon as there's a user input, this can be done with SPA or not. One thing to mention though, is that Pull-to-refresh is something that is gradually falling out of favour.
Besides those 2 things, insightful post.
Sure, but there's strategies against this, right? Generally, vendor and 3rd-party code doesn't change often, so minify and stick all that together. Then, you've got your core application code, which you attempt to keep as small and fast as possible.
I will say, I'm not as experienced as the author of this piece, but at the end of the day I feel like the author is making blanket-statements that honestly don't hold up to the reality of what users actually want. I think it also makes assumptions about your stack, and your resources- yes, if you've got incredibly fast, top of the line servers, server-side rendered pages are probably a better idea, as the time difference between a json payload and the page being rendered by the server is much smaller.
On the other hand, even a cheap rails(ie slow) server with a CDN handing off the client code can shove some JSON out no problem, and it can do it very fast, even the worst-off users usually only at 300ms for total receive time- a time which generally, is 100-200ms slower than your average server's render time of the page alone.
Furthermore, it lets you offload who is delivering said content- if a CDN is giving up all that Javascript, then the initial render times may actually not be that much slower than if it was server rendered.
----
I also get the feeling a lot of people are making the mistake right now of assuming that because there's been a lot of evolution in the frontend framework world in the past 2 years, that it also means we're hitting peak performance, which couldn't be further than the truth. Angular apparently(I'm going off of what I've seen many say about 1.0 in the post-2.0 world) completely bungled performance the first time around, but it'll be better next year. Ember is already well on its way to being fast, and by summer of next year is going to be blazing quick with all of the HTMLBars innovations.
I think we're barely getting started figuring out frontend frameworks. Even if right now it may not be the best idea for your personal use case, I'd check back once a year until the evolution slows down to make sure you don't end up regretting not jumping in.
1) "Server-side rendering can be faster" - the information in this part quietly ignores the fact that:
* even if you have server-side rendering, you are still going to load external javascript/css files
* browsers optimize multiple resource loading by opening multiple concurrent connections and reusing connections
* you can and should use cdn (hence actually lowering the 'theoretical' minimum time)
* browsers cache excessively - and you can make them cache even for longer
* the fact that rendering on the server-side takes a lot of cpu and hence increases response time dramatically the more requests are made
6) while reloading the page when the code changes is a good idea, hot updating javascript is a really bad idea - beyond the fact that it's terribly hard, will most likely result in memory leaks in the end and as far as I know no one is doing it, it'll be extremely hard to maintain or debug.The rest of the principles are quite true, informative and should be practiced more often (assuming you actually have the time to engage in these kind of improvements as opposed to making more features).
1. With server-side rendering browser can get HTML and display it before other resources are downloaded, parsed and executed (for JS).
2. In pre HTTP/2 world resource requests can be expensive, since browsers limit the number of outstanding requests.
3. Some users still use slow phones with slow and laggy network connection. Server-side rendering can improve experience for them a lot.
• you only need html and css loaded to show content to your user, and js loads while the user is watching content, js has some time to be ready on first interaction.
• still feels slower than showing stuff with only html+css
• for pages content that changes a lot, if you rely on cdn for html pages, you need to update content with js on page load and you either ends up with a splash wait-while-we-are-loading or a blinking christmas tree.
• if your html is small enough, the cache checking round-trip is not that faster than loading content, while a JS rendering will need cache round trip AND data loading round trip. You can eliminate some html round trip with cache expiration, but at the expense of reliable deployments.
• still, JS rendering/update can be slower than server side CPU, especially on mobile devices.
The only thing that the browser should always load is your base html, and have a single linked js/css that is concatenated and compressed, whose url changes every deployment - most web frameworks already have a way of doing it (Rails, Django etc...).
Take another look in Canary ;)
It's true to the spirit of the post as well: the count gets rendered on the server, then reactive updates come in realtime and the view is updated.
> Consider the additional roundtrips to get scripts, styles, and subsequent API requests
If you're using a framework like GWT, it compiles all of the relavant css files, javascript, and ui template files, into one .html file. Then there's only one or two http requests to download this html file, and the server only has to handle requests for fetching data, updating or adding stuff, etc. You can also gzip + cache this .html file, to make it even smaller.
It runs lightning fast, too.
Also, GWT has been open source since 2-3 years ago, and its development has been steadily going on. Even if Google was to abandon GWT, it would continue on being used & developed by others.
Have you checked out your average content web site?
Point is, it is ridiculous suffering from client side rendering latency penalties due to an absurd number of round trips loading all these different components. Concatenation is NOT a hard concept folks. Even if you aren't using GWT there is no excuse for letting this drive the problem.
I've used adwords a little bit, and it runs fine for me. The only slowness I can think of is when you request keyword / bid data and it fetches it from the server. That takes a while. But for that, it probably has to query a gigantic dataset on the server, and that's probably the reason for the slowness.
You can contrast that with Angry Birds' html5 version, which was also written with GWT.
You can use split points to divide up your code, only the resources in a given split point are loaded. Example: If the user is viewing your 'Sign up' page, then only the resources for the 'Sign up' page will be loaded.
> Large files like images can block the rendering of your layout
Only small to medium files are inlined. Large files are downloaded as usual.
I pretty much agree with everything except #1. Rendering things on the server has the disadvantage of re-sending the same thing for every window. I am a big fan of caching and patterns like this: http://platform.qbix.com/guide/patterns#getter
You can do caching and batching on the client side, and get a really nice consistent API. If you're worried about the first load, then concatenate all your js and css, or take advantage of app bundles by intercepting stuff in phonegap. Give the platform I built a try, it does all that stuff for you, including code updates when your codebase changes (check out https://github.com/EGreg/Q/blob/master/platform/scripts/urls... which automagically makes it possible)
I would say design for "offline first" and other stuff should fall into place.
Example: Server generates a sine wave which gets displayed as a rolling chart waveform on the client. As client spins a knob to control the amplitude, the server-generated stream should change (sine wave is a trivial example, representative of more complex server-side computation).
I don't imagine he's suggesting we try the same approach where server-side processing is required.
If I have misunderstood you then I apologise.
I find single page applications way too complex. The amount of code duplication is horrific. So everyone ends up building platforms like GWT or Dart in order to hide that overhead. But that does not mean that things get simple.
(Maybe I'm getting old.)
ROCA is an appealing idea, but my concern is that in order for the the-API-is-the-web-client approach (which ROCA as I understand it seems to advocate) to work you end up mixing two entirely separate levels of abstraction: what may be a good abstraction on the API level may not be a good abstraction on the UI level. It's sufficient if your web app is just an API explorer, but not every app lends itself to that.
You could say that then we shouldn't be building those apps, but that's simply not realistic.
But as the article points out is often the case, at github.com the HTML loaded does not include the already rendered resource; it must be pulled in via a separate request.
It still has a few issues though, I work on flakey connections now and again and sometimes it just gets stuck - it would be nice if the request were retried automatically after a few seconds.
I don't get this, in my opinion they are optional, you can show the ios png placeholder (shown in the next item) which is a very static and cacheable content, while fetching your highly dynamic data from a database or somewhere else.
It feels like the first principle contradicts 2, 3 and 4.
Please stop putting loading icons and spinners where your content should be.
Heck, if the concern is really about having lots of round trips, rather than server side rendering, you could have the server side stitch the client side components and still allow client side rendering. In fact, doing it that way makes it a heck of a lot easier to avoid reloading the entire page each time. Some kind of weird disconnect here.
That's obviously an extreme example.
Ultimately it's the engineer's job to make good decisions.
No, I think that's missing the point. Sure it could be larger, but presuming you are trying to optimize the experience, there is nothing that would require doing it server side.
Why would the total payload needed to render the page client side _have to be larger_ than if it were client side? Unless you are talking about rendering an image with client side logic instead of sending a PNG/JPEG (in which case, sure, but that isn't what most people are talking about), I can't quite see it.
First, in your quest to show me the latest info, please please please don't introduce race conditions into my interface. I don't want to go to hit a button or a key or type a command, and have what that means change as I'm trying to do it.
Second, it's often important to me what has happened locally vs. what is reflected on the server (especially if that's public). Please do update the interface optimistically in response to my actions rather than sitting and spinning, but please also give me some indication of when my action is complete.
Is this a joke?
Analizying a trajectory is a completely different thing.
for another useful example of this technique.
The other one will ajax preload a dropdown content when it detects that current mouse trajectory is in line with it. Come on.
Are you using any kind of server side framework (Like Django/Rails)?
Are you using any kind of client side framework (like Angular)?
Are you using any kind of layout framework (like Bootstrap)?