Thursday, 21 February 2019

Worked over a year with Angular 2, what bugs me about it

I have stopped using Angular 2 in the beginning of this year – near before the Angular 4 release. I’m not trying to convince anyone about the future but I’d like to express my thoughts after gaining some experience. Not only about syntax and performance but about whole thing as a development tool.

Features and development

Angular constantly lacks corner-case features and you can’t simply develop them on your own. Well, technically you could do that because it’s open-source but it’s pretty hard and complicated (yeah, I tried to do some stuff). As for example, there were 3 completely different versions of Router and the latest one is best but still not done in my opinion:
Many many issues are closed without deep look into the problem. Most often it’s about “it’s old issue”. Some issues about Router v1 are still viable with Router v3 – but if the issue was reported for old router then it’s deprecated issue, no matter what. Sometimes someone will tell you that issue doesn’t not exist even when having a plunker for it:
→ Huge performance impact on Component’s host document click – BTW this issue was locked and only unlocked after I DM-ed some guy from Angular Team on Twitter, I don’t want to judge, judge me, but I’ve felt very bad in this situation. And several others I won’t mention.
However, I must say I understand the problem. There are so many issues reported that Angular Team needs to maintain all of them. For about a year I remember at least over 1000 of them opened at once, constantly. As of writing this, there’s over 1200 of them. And remember – there are still appearing ones and closed ones. Current statistics for all issues till 12 May 2017: 1260 Open, 9071 Closed. It tells me one thing – people have many problems about it.
Lastly, the Angular Team promised nearly everything:
  • fantastic performance
  • tree shaking compiler
  • NativeScript abstraction (for mobiles)
  • Server-Side Rendering
  • Command Line Interface
  • Material Design UI library
  • external components easy to grasp
  • lots of other things
It’s sad to say but most of these parts were full of bugs or not real most of the marketing time.

Out-of-the-box usage?

Well, it hardly exists. Angular CLI was promoted on Angular Conference when it lacked lots of features and was full of bugs. And was really slow. There were and are other unofficial configurations where this one seemed to be the best one – angular-starter from AngularClass.

Architecture

Now, here’s the important part.
If you’re about to choose Angular 2 then think of picking it just because of syntax knowing that it’s not a framework but compiler. Not the best one if you ask me. In terms of compilers there are better things – ElmPureScript or Imba. And several other alternatives appeared lately. Someone told me that for working with outside libraries the PureScript is best here. Elm is worse but much more secure and time-travel feature really works there.
If you think about Angular 2 as about AngularJS then go with Vue.js, you will not be disappointed with syntax, performance, overall architecture and support.
But let’s discuss some parts of it.

Components

Because of components Angular is OOP and you have no choice. And pretty bad OOP where for a long time it had no inheritance (it was introduced in ng 2.3). If it would be a functional vs OOP discussion then custom directives don’t even try to be a function-replacement.
Components are instantiated in a declarative way. Imperative instantiation is possible but it’s pretty hard to do – you need a component factory and a view reference to place the component into. It becomes somewhat harder when your app is multi-module (some component/service definitions are imported lazily).
Last point about component are their practicality. It’s often described that component architecture is about reusability. Especially in terms of external components. As this world is not ideal, it’s pretty often a lie – I lost lots of time to use some libraries that are not compatible with current version of Angular. And I lost lots of time to those libraries which indeed ARE compatible with Angular but are not working properly or lack features. So few times I had to implement my own (reusable or not) custom controls.

Templates

Here’s the main part where we can think about the compiler. Component is a class so it has defined fields and methods. Those fields and methods have types. Template compiler doesn’t use those types and that may be understandable. But I can’t find any explanation for not checking if certain field/method exists or not – when called from the template.
Templates are supposed to be declarative. However, we still need to think in terms of “what’s running first” or how things work. Pretty often we have to cover template with *ngIf  because something is still not instantiated. You might think that Observables with AsyncPipe would fix those situations but it’s not the case in my opinion. Sometimes you have to go imperative or build a very complicated union of observables to just be declarative. And sometimes you’ll still need *ngIf  before *ngFor  instantiates. And last but not least – sometimes you have to understand component lifecycle – for instance ngOnChanges may be called to early for you.
But the worst thing here is that templates are not checked against component properties. You can make a typo and not notice it because compiler won’t tell you. Since there is a compiler I believed it would be supported some day but seems we still have to look forward to it or forget about it.

Dependency injection

You may think that you can inject everything. But you can’t inject host component to your custom directive without knowing component’s type. There is an issue for that:
Dependency seems to work fine though. Only the fact that we have to manually specify which definitions (component and directive types) will be used in apllication harms me. I’m talking about NgModules here. It’s a lot of boilerplate just because of how the TypeScript language works. Maintaining type lists manually is just not nice.

Compiler

The compiler itself is written in TypeScript. I don’t know why but it’s not the fastest language out there.
Tree shaking is an interesting feature because whole app should weigh less. In practice, if after including something like ng2-bootstrap it won’t be true.

Performance vs declarativity

Angular is promoted to be very efficient. That’s in terms of running compiled application. Let’s shortly discuss change detection mechanism which detects when component view should be updated based on component state.
First of all, the most efficient version is available through ChangeDetectionStrategy which is called OnPush. The other one is Default. Normally, every component gain a new hidden method during compilation. This method is being called every time when it’s possible that a component may have changed it’s state. For instance, dispatching events from this component or subcomponents will launch change detection. There is an option to turn that mechanism off and this is exactly OnPush – you have to manually notify when component state is changed. This gives us an option, however it totally loses declarativity. However, I’ve noticed that even having Observables everywhere I was still unable to keep and maintain declarativeness readable. Imperative code will appear anyway, in lots of places.

Performance vs work

What is not efficient enough is work. If your project has lots of component and external libraries it takes time to test every single change because whole project is compiled again. Well, some modules are not recompiled but in my project it still was at least 10 seconds even after lots of optimizations in Anular CLI or Angular Compiler. And here’s the thing – styling. I used to code styles with SCSS and see results as quick as a half of second would pass without any browser refresh during the app development. That was custom gulp + livereload configuration. Angular does not have this. Each style file seems to be compiled as a some kind of module. For many months I couldn’t find any working way to apply styles without refreshing app state. That slowed me down a lot. Probably, it was due to some special syntax about styling host component or deep styling (even though this was removed from standards – What’s the substitute for ::shadow and /deep/?).
Hot Module Replacement seems to be a big promise. However, you need to specifically implement this. It’s easiest to implement with redux-like architecture, for instance with ng2-redux. However, if using redux, then why would you ever bother about Angular? Because of Dependency Injection? Code generation?
I think we should not constrain ourselves in terms of produced code amount but in terms of quick development. Trying changes should not be that hard. Well, you could write very nice unit-testable code. But guess what – testing HTML-based UI is also somewhat slow.

Summary

It’s easy to proof that Angular 2 is cool on simple examples like TodoMVC. In some places it was really well thought. However, it’s built on top of two strong constraints – TypeScript as a language (despite there was Dart and pure JavaScript some time ago) and component architecture. In my opinion those constraints make this framework exactly what it is.
My two projects grew into something bigger and I’ve disliked this framework despite all the promises given by Angular itself. The biggest issue is about work performance. It’s just slow to develop something big. The other big issue is lies. Sorry to tell that but there was too much marketing of things which didn’t (or still don’t) work.

No comments:

Post a Comment