Comparison of Angular & React Codebases (part 1 of 2) by Julian Flaks
When the choice of Angular or React arises, development teams often bring up comparative criteria such as what libraries are available, the design ethos of the frameworks, how many people like each one and so on.
I thought it worthwhile to attempt to do something a little different; I'm going to compare them by sharing some real world stereotypes of the situations that might come up while maintaining applications which have been written in each. In truth, the patterns I am going to mention can to some extent be found in either one, and the specifics may seem arbitrary, but hopefully the comments will feel authentic to those with experience in a framework, and be helpful to others. In terms of capability, there are immensely complex and well maintained applications in both, which is why simple comparisons can be less helpful.
Before getting into the descriptive details, I have anecdotally encountered more opinions of people having regretted choosing Angular over React. From an objective viewpoint, I believe it would take less effort for somebody to make a replacement for React than for Angular though I think either would fall definitively out of the reasonable capability of any but the largest scale organizations. At the same time, Angular genuinely can make more sense to people with back-end MVC type experience, and I have had some of my greatest front-end coding joy in Angular codebases which have tackled complex problems in as likely and helpful a way as I think was reasonably possible.
Starting with Angular applications, a initial typical observation is that it will very likely have been created around a very structured code base with services to go along with the component pages at some level. There may or may not be a global state store, but there will probably be some service or grouping of services directed specifically towards interaction with a back-end. There will probably be a pattern that can be followed when creating new screens that will allow the addition to feel homogenous with what is already there. Screens which have been around a while may have become overly large as components, and making sense of how to break this down after the fact may take some detailed analysis. When they are too large, the problem is often more one of code readability rather than performance, but extending behaviors can also get unduly complex.
If there is too much complexity in the overall structure of the application, it has a reasonable probability of being in the form of some asynchronous mechanism written in RXJS, which may have become a central place for complexity to be expressed and distributed across the application. Timing problems like race conditions, stored promises, or hard-to-think-about sequences of operation may be the result of this development. It should be said that this is much more structured than was the case in AngularJS. The most complex timing problems I have ever encountered in any application was an AngularJS front-end whose code innards reminded me of a complex timepiece, async events interconnected like gears so that it became impossible to ask which operation began a sequence.
The usage of TypeScript, whilst a beautiful development in JavaScript at large, may also tend in some cases towards Angular apps overly embracing inheritance. This may give helpful repeatable patterns and has some real benefit to an application. However as with any object oriented code, one runs the risk of making a large amount of the application go through one piece of code, and in turn allowing that to become too complex and hard to evolve. For those who have never been inside elegantly used OO, it's worth saying that overall, well used OO will serve to make code more easy to understand rather than less. The template pattern is a wonderful example of how inheritance can make it easy to understand the relationship between closely related concepts, and can avoid the combinations of cut/paste and if/else that can add hundreds of unwanted lines of code.
One other source of complexity can come in the form of dependency injection. Since stateful components can be freely connected in the form of injectable services, their scope comes into play (which must be watched out for once splitting across modules), and circular dependencies become an issue one has to correct for. In simple cases this is very straightforward, but in a complex project can become another project level like the Webpack configuration file that needs special care.
Refactoring an Angular application will tend to be a structured exercise and may involve moving code up and down levels of components, breaking up components and services, and rationalizing async behaviors. Performance problems may come from too many bindings, just as in classic AngularJS, as well as the interactions between different components and slow rendering portions.
Of course, simple applications might never have any serious maintenance challenges of any kind, and code which has been designed orthogonally to the design of Angular could have any number of complex problems unrelated to the above, and that's without exploring the many possible boundary problems between the front-end and the back-end.
In Part 2, I'll try to cast a similar perspective on React applications and how their maintenance and extensibility can be affected by the framework behind them and its underlying patterns.