Общо показвания

септември 02, 2014

Riding the Polymer wave

Comparative analysis of the developer experience for creating a multi-screen, single page application complete with persistence, animations and rich client data management w/ filtering. 

This piece will describe the development process of the creation of a single page application that harness multiple rest resources to compile a cohesive user application with multiple views, data filtering and persistence using the Polymer library. The process is describes as direct comparison with the process of building very similar application using the closure library and tools collection. The analysis is made as an effort to point out the pros and cons of migration from closure tools and libraries to the modern Polymer project as framework/tool set for building such applications. 


The post is quite lengthy so if you are looking for a quick summary scroll down to the bottom of it.

I will start with some background information: I have been building large scale single page applications for the last 5 years. I have been using many different tools for that purpose for one reason or another (some of those being: project already have been started using a particular framework that is not playing really well with anything else, the project required really small footprint as it would be server many many times without caching, and other). Some of the tools I have been using extensively include: MooTool (archaic, I know), jQuery (ridiculous, I know!), GWT (Java, I know), Closure library and tools (templates, CSS pre-processing, compiler) (really really verbose, I know), TypeScript (just a toy, I know), Dart (not really what it is portrayed to be, I know) and other - on occasion.

From all of those, the most reliable one and most trustworthy thus far at least in my use cases was Closure. Google really put efforts into it. The main problem with Closure is that it was designed with several things in mind, that kind of trip over JavaScript developers coming from any other framework/library:


  • designed to look and feel a lot like Java
  • designed to be production useful explicitly only with the Closure compiler 
  • designed to be compatible with old old old browsers
  • designed for internal use primarily and thus lacking the polishing of the other competitors out there


From those probably the biggest obstacle one could have with Closure is the fact that it is designed to actually use types, while JavaScript is designed to not use those. This is also the fact why TypeScript is used mostly as a transpiler these days and much less as compiler - types are not something JavaScript developers want/need to deal with.

Assuming the developer has the time and willpower to learn about the basic ideas inside closure (so types, the fact that you actually have to be able to imagine everything the compiler will do to your code in order to write something that will work after compilation, the fact that you actually need to test both source and compiled versions) - those great ideas and the advantages they bring could become part of your daily coding routine.

However even if you become the master of imaginative compilation and you are able to write code without errors (coming from the compiler) - those will never mitigate the fact that to write closure compatible code you have to always be very very verbose. Actually you have to write so much boilerplate code that even Google attempted to come up with some shorthand versions (mostly I am talking about goog.scope). Those efforts are however nothing compared to the amount of code one need to write to get going. This includes the type information (obligatory - the compiler is still not so smart to infer all types from the code directly and probably will never be), the namespaces and the fact that you need to include all used namespaces in the beginning of your file. Also from my experience it is very much possible to often have slightly overlapping functionality just because the design of a super class does not allow for some particular alternations (a property being important for your subclass but it being private in the parent class etc - no, you cannot alter the source code and no, you cannot just access it - the compiler will scream at you and who knows at which point the compiled code will stop working just because you are accessing something that is marked as private and the compiler can remove it as it sees fit...).

Part I: The good

Polymer is a new library from Google, that attempts to provide opinionated but easy to use abstractions over the new Web Components standard. It promotes declarative syntax for creating and using custom elements that should be compatible with all modern browsers (this includes the last two versions of all 'ever green' browsers).

Polymer itself is consisting of a polyfill for the platform features that are missing in some browsers (actually the only browser that has implementation for all web components specs is Chrome) and then the 'opinion' on top of it (polymer itself) on how to utilize those features to create developer friendly, fast to develop and easy to use custom elements and web applications.

First of all - instead of JavaScript you are writing mostly HTML - tags, attributes and properties. What you would write as new namespace with instance properties now you write as an element (polymer element tag) and attributes on a prototype object.

Here comes the first new thing - in Closure we do not write primitive values (nor object types - like NEVER) on the prototype because it alters the hidden class of the instance if altered after the instantiate phase and thus potentially makes your code run slower. In Polymer it is encourages for readability reasons and to easier understand what is going on. You can still write all properties in the 'ready' method, but the polymer implementation encourages you to use the prototype object of the element to define those and they are used for type conversions (for example the attribute will always be written as string, but you can hint polymer that you actually want to use numbers and thus the string will be automatically converted to number). One can also argue that this saves memory, but unless you are creating thousands of elements this is not really important.

Being able to not really care for the type (i.e. if the correct type will be passed to the property as value etc) and instead hint the type and expect the system to convert it for you is really a great developer experience and removes burden from the developer while in the same time proves usefulness (if one binds the input value to a money conversion element there is no need for the developer to manually convert the string to a number). So +1 for Polymer for not caring about the types and another +1 for managing type conversion for you.

After you define your custom element you need to (for UI components at least) make some templates. In Closure this is done with the soy template language, which is not so bad per see, but is cumbersome to work with - it is not possible to render your template in the browser without a) having a consuming element/component and b) valid model if data is used in the template. More over the template cannot be used directly, you always have to compile it before you can see what it looks like. So basically you end up writing HTML like files, but you cannot preview them and you cannot use them without all the boilerplate code around it. You certainly cannot just visualize that particular template with ease - you need to setup a whole new view with imports and more code just to see what you have done. This forces a certain type of workflow that seem a bit unnatural - first the designers write the view as regular HTML and then you (the JavaScript developer) take that piece of HTML and strip it out and turn it into a template and you hope that you did not broke anything.

In polymer the template is just a regular HTML. Testing it is as simple as including your new element/components in any web page.  +1 for Polymer and easy to use and create templates.

Once you have a basic element (so some template structure and some properties to operate with) you might need some ready to use functionality - you need an import. Unfortunately here polymer and closure, while in totally different in technical perspective ways, operate very similarly from consuming point of view - you have to always include what you need in each and every of your custom elements/components. In closure you do not have to care about file paths so shifting files and directories around is much safer (in Polymer it was a nightmare to have to move some elements to a different directory mid-project - we needed to go and find all the places those were used and update them), but you cannot automatically retrieve needed libraries and elements. In polymer the preferred way to manage dependencies is bower. While it is very nice the installed elements are very volatile to path alterations and once you start it is hard to update those. In both cases however you have a lot to write!


  • In closure - the whole namespace for each used element/utility
  • In Polymer - the full path of every used component (and writing "bower_components" all the time is not fun!)

Because of that no +1 for Polymer in this area.

After all these steps you can potentially have a self-contained ready to be used, re-used and extended/augmented piece of code. As a whole the closure code will be 50% larger (mostly because of the type comments, but if you put comment on your polymer code it would be the same size). The main difference is that in Polymer you end up with a single file (or 3 files, depending on how you structure the component CSS and Script could be separate files, but I tend to keep everything together for more cohesive experience), in Closure you end up with at least 2 files (for UI components) - one with the scripting and logic and one with the markup. The markup of course can live with the markup of other components in a single file under a namespace which makes it even harder to separate your code and distribute it as a single element.

Polymer also provides utilities for documentation and demos of your work (again elements that automatically extract the code comments and construct html in the browser). To do the same with closure code you need jsdoc and templates and basically you are on your own there because the docs of closure library are generated internally by Google and the ones you can generate look and behave nothing like Google's - they are less usable and uglier.

Because of these not so important but kind of not unimportant reasons Polymer gets one more point.

Part II: The bad

Not all is roses in Polymer land either.

First thing one could notice is that because of component reuse and the ease of instantiation basically you are inclined to use lots and lots of elements that provide utilities and no UI. The most notable example for that being core-ajax. In a single view page you can have several core-ajax elements. In Closure land you usually have one 'Loader' class that takes care of all the communication to specific REST endpoints and when an error occurs you decide what to happen next. In Polymer each core-ajax element lives on its own and you might end up having 30+ such elements in your app. You can define error handlers on each of them or listen for core-error globally (on application level). The problem is that you end up with ajax request originating from pretty much anywhere in your application and it makes it harder to manage. Also it is pretty hard to limit what is happening when because you do not have central control over it. Of course you can write such, but it looks unnatural. As a side effect you can load pretty much all your request except one, but it will take time to notice that you failed in that one request.

Another catch you have to deal with is Shadow DOM's CSS rules. Once all browsers implement Shadow DOM it might be great (and it will be in theory) but today you end up with styling that is very volatile under the polyfil. The first time you start styling you are tempted to write rules that are automatically scoped (for example adding classes like '.padded' in several different elements with different values inside of it). Under native Shadow DOM it works fine, but under the polyfil you will get overlapping rules and definitely not what you expect. The main problem with that is that you actually develop in browser that has native Shadow DOM and you do not notice that there is a problem until much later (and of course you are developing with Chrome, right? If you use anything else for development this whole post is probably not for you anyway). Once you realise what is going on you end up writing all rules as if they were un-scoped (so ':host .padded' which defeats the purpose of Shadow DOM - even with Polymer you still have to think about your styles as global - not cool!

Next come RAF and timeouts: The documentation for polymer states: 'async is bound to animation frame'. Well it is, unless you specify timeout in which case setTimeout is used and you loose the RAF timing. To gain that back you end up with atrocities like this:

this.async(function() { this.async(function() { _doSomethingToTheUI() }), null, 500);
I know, it is not that bad, but still looks a bit unnatural. Also guess what if you need to cancel this delayed work... you need to handle the case where the first async is already executed but the second one is not... this makes async programming fun, doesn't it!

Another async related code - 'job' is working around a common pattern and is designed much better and actually reflects real pattern. The implementation for it (as in polymer dev) is actually a bit different than what is expressed in the docs, but still is a nice enough utility.

Next comes the scroll handling: in Chrome scroll events are synchronized to the RAF, but in all other browsers they are not. However guess how scroll events are handled in Polymer...

Same goes for touch events. I know that it is best to think forward, to innovate and blah blah blah, but when you are pressed to deliver working code for those old phones Polymer is not not your friend.

This is especially true when it comes to the animations: the design and the looks are really really great - for the first time you get a framework that allows you to implement very complex and very impressive transitions and animation with so little effort. They are amazing when demoed and when they work. Now try to run the exactly same code under Firefox... or under iOS safari.. Hero transitions do not work as expected, sliding transitions are braking the view completely in Safari... and in Internet Explorer ... there is a bug that prevents platform polyfil from loading... fun huh?

Basically polymer teams says that they support and want to support all major browsers, but the reality is that once you go beyond the simplest demos it only works reliably in Chrome. This is not so bad if you are targeting Chrome users, Chrome OS users and Android users with sort of new and fancy phones. Unfortunately most paying users are actually using iOS for mobile. In theory if your product is successful you should rewrite in Native for iOS for best user experience, but in reality complex polymer applications work ONLY on most modern phones. For example equal by complexity and code size apps when written with Closure work perfectly on iOS 7 on iPhone 4S and iPad 2, but when written with Polymer the app is completely unusable on iPhone 4s and barely running on iPad 2. On iPhone 5 it works in parity with desktop Firefox for example (i.e. there are delays here and there and some things are broken but most of the app is fine). Now compare this to the Chrome browser on the oldest Nexus 7 (also called original Nexus 7). It works perfectly smooth! Nexus 4 - butter smooth, Nexus 5 and Chrome - again buttery smooth experience, all animations look exactly as they should be, all transitions complete nicely and in sync...

I am sure Polymer team wants to support all major browsers, it is simply the fact that they don't. So does not your application. And this means you can be in big trouble with your manager...

When you debug your application in Chrome everything makes sense, you see the shadow root, you see where elements got inserted and so on. Try the same in Safari debugger... or in Firefox.. what you see is very very different from what you expect. This is again related to the fact that there is no native shadow DOM support. Apart from that other things seem to work just fine. The problem I have with those is that I cannot figure out where the slowness comes from, initially I believed it was a rough code path - someone somewhere was making something that was not really needed. However test after test after test proved to be 'accumulation' problem - if I isolate a component and nest it deep deep into lots of other components it still works fine. But when I start adding siblings on all levels the slowness accumulates progressively and an app with only 3 levels of nested animated pages with 4-10 siblings in each level results in apps that run only in Chrome, crawl on any other browser on Desktop and kill performance on mobile safari and old PCs.... There are some 'offenders' that are worse than others, but one after another they bring their 'slow' and it all results in intolerable application.

Once again - I am NOT sure what is going on there (mainly because debug tools on most other browsers are really years behind what we have in Chrome), also another 'nice' reason is that when you attach iOS device to debug web page on a Mac it auto-magically becomes 20 times slower. I do not kid you - the model observing digest all of a sudden runs for 25-28 milliseconds and the touch events take 50 ms to be processed.... close the debugged window and MAGIC - you again have something that looks like an app and not like still images... this terrible experience proves to me that Apple is strictly inhibiting any and all web advances they could think of and wants you to use their insane languages (I am sorry but Objective C looks like someone killed a dragon and used the body parts to compose a language).

Part III: The ugly


The ugly part is short: the ugly part is the controversion this whole Polymer thing creates: on one hand the managers want things to come into existence fast - the faster the better, and love to talk about things like 'time to market' etc. On the other hand they really really want to be able to tap into the paying market of Apple store. 

And indeed developing with Polymer will make you 10 to 20 times faster (depending on what you have been doing before that, I have been doing Closure and Polymer gave me a lot of speed). It makes so many things easier taking care of many things for you: type hinting, data binding, resource loading (hello, HTML imports) and ready to use components that are beautiful and usable (paper-elements). 

However as the 'bad' part explains there is a price to be payed and it is not really developer's choice to make: once you know what to expect it is not really something you would go and fix even if you are a 'javascript ninja' - it is just the way it works, it utilizes very new things that are simply not there in most browsers. Even if you are a ninja, you could fix this or that, but the amount of work you have to do is much much more than if you have just used something that is known to work already. Polymer is being worked on all the time. But Apple is not showing any interest in developing for those new standards and we know iOS is away from this new goodness. 

Conclusion


If you are lucky enough to be developing a Chrome packaged app or an Android only app - consider yourself really lucky, because you can jump right on top of Polymer and ride it like a champ. You can do crazy things like declarative data processing (yes, you describe the data processing and filtering as nested html elements and it works!!!), ultra cool lively animations and transitions and you can write truly reusable, configurable components that you can reuse from project to project regardless of the used library and/or framework - something we have not been able to do - never, until now. Even with closure components you were still bound to closure tool-chain. With polymer you can include (one day only) polymer and do not care who uses what else to construct custom elements that you might use.

If you are not one of the lucky you better watch out: Polymer is really fast to develop with and very gratifying up until the moment you start to test compatibility: forget about IE < 10, forget about complex apps on older phones, forget about really clean fluid animations on anything else than Chrome... at least for now. Polymer is said to be early preview and alpha and not ready for production. If you are like me, if you want to developer faster and more pleasantly Polymer might lure you in, but then you have to pay the price: days, even weeks after your application development is ready and stable you might still be fighting with Mobile Safari and Firefox and IE... and even if you are not actually fighting bugs in your own code you are still days away from releasing.. you have been warned!
Публикуване на коментар