cover of topic

Angular is not for everyone, and probably not for you

How Angular makes you hostage to its approach and prevents you from growing as a developer

Maksim Dolgikh
14 min readJan 10, 2024

--

For 2023, Team Angular has made a lot of important changes which were asked for by the community, and that’s insanely awesome. But it wasn’t always like this.

In this story, I’d like to tell you about my 4 years experience of using Angular.

First of all, I think Angular should not be your first technology to develop applications.

I want to highlight the problems which Angular creates and how they affect developers and team building. I will also try to give solutions to the problems I have identified if you already use Angular or want to use it.

Unfortunately, some teams or even companies are slow to upgrade for one reason or another, and you may find applications on Angular 8, 9 or 12. Therefore some of the issues outlined here can still be relevant.

If you are a junior developer, this article may also be useful for you and will help you with choosing your first development stack.

At the one moment, i may seem criticizing declarative approaches implemented by the Angular team — not at all. I don’t question that over time any low-level code becomes high-level code via abstraction and new features, making our work easier. Each new UI library tries to solve the problems of current libraries and offers improvements, providing growth and quality tools from year to year

The convenience trap

As we know, Angular is a UI framework, not a library. It positions itself as a self-contained solution for front-end application development. You don’t have a “zoo of technology” from app to app, everything is uniform and monotonous, but there is a downside to this. It’s easy to get used to the conveniences that Angular provides, and it gets more difficult to give them up later on.

ReactiveForms

At one time, the approach proposed by the Angular team, the form and its control are separated into an HTML-template and a JS-controller, really proved to be handy for form development.

The problem is different, we are so used to relying on validation from ReactiveForms that we forget that HTML natively provides us with all the attributes we need to validate input fields — min, max, pattern, required, read-only, etc.

What is this problem? — Once you get beyond Angular, it turns out that ReactiveForms doesn’t exist as a standalone library. You’ll have to re-learn how to develop forms — events (submit,change) and input field attributes.

Building an application

You don’t need to customize the application build, just run the “build” or “serve” commands to get started. You do not see the webpack.config.js or vite.config.js configuration files of the builder.

Can you say:

  • How is your application built?
  • Which file is the entry point?
  • Which tool builds your application?
  • How do your SCSS files become CSS?
  • How does HRM work?

If you work only with Angular, you missed the growth stage of gaining the skill to build an application. Webpack, Gulp, Rollup, Vite, Esbuild — you just don’t know it. If you’re asked to create a simple web page in HTML and SCSS without Angular — probably you can’t.

HTTP client

This is a convenient wrapper over XHR using the Observable pattern, given the recent XHR2 feature for SSR. It allows us to make queries with multiple parameters, track the progress of the query, and be able to convert the response to the desired data type with just a single parameter.

But as an angular developer, would you be able to say:

  • When was the last time you used native methods to handle HTTP — fetch or XHR?
  • Can you handle requests and errors globally without using an Interceptor?
  • Can you name ways to undo an HTTP-request without using switchMap?
  • Do you know of axios or node-fetch libraries?

Despite the convenience, we should know how to make queries without Angular. It surprises me when even Senior developers don’t know how to make a request before bootstrap application or about the existence of RxJS.Ajax

We must have an understanding of the essence of the request and response to be able to:

  • Handle errors
  • Parse the response
  • Track the progress of the request
  • Work with FormData

Zone.js

The wonder and trouble of Angular. Zone.js was introduced as a tool to delegate all browser events, and then notify Angular. For what? — To provide the “magic” of updating:

  • Mouse click? — to update
  • Ended request? — to update
  • Ended setTimeout? — to update

We don’t think about how an entire application is updated. But if we don’t understand this process, it will be a big problem for optimization in the future.

So many unthinkable articles have been written about this library and Change Detection:

  • Root and child zones
  • Default and OnPush
  • runOutsideAngular
  • ComponentsTree, ViewTree
  • Async-pipe

And the articles were only needed to understand “how to tame the dragon”. Yes, you can indirectly manage component and application updates via ChangeDetectorRef or NoopZone. But it’s not a convenience, it’s a maintenance chore.

With the introduction of Signals and the vector to ZoneLess, the Angular team is trying to solve this problem, and make updating components fast, frugal, and convenient.

Angular gave you RxJS but didn’t tell you how to use it

What is the most frequent topic about RxJS in Angular? — Managing subscriptions and memory leaks. And it’s not for nothing.

Developers who start working with Angular try to handle Observable as Promise via then(). I’ve seen many beautiful and terrible implementations. I understand why they start creating courses and materials on RxJS because without this knowledge you can’t work with Angular effectively.

Angular as it is commonly thought of is not about a reactive approach, it is about a stream approach. All values are “stream”. If you can’t think stream-wise and know how to work with stream, you will have trouble.

Even now for 2024, these topics are being checked in interviews:

  • Higher order operators — concatMap, mergeMap, switchMap, exhaustMap
  • Combine operators — zip, forkJoin, combineLatest
  • Memory Leak Monitoring
  • “Hot” and “cold” Observables
  • takeUntil, takeWhile, takeUntilDesctroyed(new)

A hostage of the RxJS

And even if you manage to shift your mindset to stream and functional programming in Angular, be careful, not every value can be a stream.

What’s the problem? — You forget how to work with primitives and objects, now you have any value in the stream when you don’t need it. You’ll have the urge to create every class field via BehaviorSubject, and that’s a big problem. Because of this, you may see many implementations of Stateful Services with 10+ BehaviourSubjects for each field with multiple combinations and inner subscriptions.

If you have similar implementations, look at libraries that offer Local State Management, such as Akita or Elf. Thanks to them, implementation via service will remain and you won’t have to make major changes.

DI

A feature that makes many developers choose Angular. But you need to know how not to “shoot yourself in the foot”

ProvidedIn and dead-code

The ProvidedIn function in the @Injectable decorator was created to mark Angular where a service should be registered. This allowed us to optimize the size of initial scripts and, if necessary, remove services from the build if they were not used. This approach can be used for library development where you don’t know how many services from your library would be used by other developers.

However, this approach doesn’t work for monolithic repositories with a single app. The fact that Angular removed dead-code for you is good, but you shifted the responsibility for keeping your codebase up to date to Angular.

You have dead-code in your repository that you need to be able to identify and remove in time.

Magic of providing

ProvidedIn registers all services for you, and it is partly convenient. But the problems start when using providedIn:root.

Developers rely on the tree-shaking mechanism so heavily that they register any services globally from the deepest point in the repository, even though they are used in a limited portion of the application. By doing this, you break your repository structure by creating global things and not having them in the right directory for such entities.

Also, if you are developing some functionality and you want to register services globally, you should use the forRoot() pattern. This way you will explicitly specify which services should be global and which ones should not.

Tree-shaking of services is equal tree-shaking of code

Many people mistakenly think that tree-shaking of services applies to the whole code, but this is not true.

ProvidedIn affects only the creation of the Tree of Providers and how many services will be in it. At best, it will save 10–15 kb from the total build. That’s a very small value.

No one has cancelled native tree-shaking, which is based on checking imports. It is due to it that you will optimize the application build. You should also use second-entry points for the libraries you create. This is how you will provide the tree-shaking effect you want.

You will always be behind current frontend features

Angular should be thought of as an Apple product in “the world of front-end”. While other brands combine and develop new features every few months — Apple is not rushing to add new features and prefers to improve the product a little bit once a year.

You have a closed ecosystem of Angular dependencies, you can hardly replace any dependency with a newer one. What becomes critical is the need to keep your Angular version up to date. For example, you can’t use Typescript 5 or Webpack 5 with Angular 9Please upgrade to the correct version. If you fail to upgrade or delay this process, all the features will pass you by.

If the Angular team has decided not to add the feature or tool support you expect, you’ll have to work with what you have indefinitely.

Yes, there are plenty of “customization” guides on the web, but most of those solutions will only work for the current problem. No one can guarantee that everything will work as before, especially after a version update.

More people — more ideas — more solutions

Angular is currently one of the top three popular technologies for app development.

If we want to implement a copy of an application with React or Vue, it will turn out that not all dependencies of this application have analogues in Angular. Either they don’t exist, or there is no Ng-wrapper for the js-library, or there is, but with a different functionality.

You will need time to implement your own solutions because the community is smaller than React or Vue and has not created these tools.

Developer convenience comes before business.

All articles, and discussions comparing popular tools with Angular talk about internal implementation, forgetting one detail — they forget that you should only compare tools when they produce the same result in terms of performance, SEO, build size and how much time was spent to achieve that result.

First of all, you are working for a business that gives you money to develop their product. The end user doesn’t know who made the product, how many people made it, and what technology it’s made on. What he cares about is a fast site that is responsive and works as he expects.

When you choose Angular out of a “it has it all” mindset, you are only providing a quick entry point into the initial development phase. You are knowingly narrowing down your development options, and now you have to depend on what Angular can give you. You’re not in 2018 where you have a limited pool of tools. Experienced developers can create a project from scratch in 1–2 hours, and choose a stack of tools that reflect the needs of the future product. If something changes, you can replace any part with a better one.

It is also worth keeping in mind that if you choose Angular, finding new people will be more difficult, as there is a direct correlation between the popularity of the tool and the number of developers. If your business strategy involves rapid growth, look at more popular tools that will increase your chances of finding a candidate

Angular suggests using what you don’t need

Protractor

If you don’t know what it is, that’s fine. This is a tool for e2e-tests, installed when creating angular-workspace until recently.

It’s not hard to guess that very few developers have used this solution, and even less documentation, guides and instructions about this tool can be found.

As a rule, if you needed e2e-tests in a repository, you went looking for ways to migrate to the familiar Cypress or Puppertier tools.

Thankfully, Angular has abandoned it, and support for Protractor itself has been discontinued in August 2023

Karma + jasmine

This is another tool that comes by default from the Angular package. Unlike Protractor, it is not deprecated and is in demand among developers.

Jest is often contrasted with this combination. They have no special differences in writing tests but will have different ways of configuration and work in general.

Before working with Karma + Jasmine, answer a few questions:

  • Do you know Jest and are you ready to switch to an alternative tool?
  • Do you really need a browser-based environment to run your tests?
  • Are you ready to customize Karma and the ecosystem?
  • Did you know that in CI/CD you will have to install browsers on the agent to run tests on Karma?

If you’ve migrated to Angular16+, there’s good news, the new versions support Jest installation

Angular Material

It’s hard not to bypass this library, even though it’s not a built-in dependency by default.

One of the main reasons why developers and teams choose Angular Material over other UI libraries is the official Angular team support. While other UI libraries wait for a new version of Angular to be released and then updated, Angular Material is released almost in sync with Angular. And at this point, people run out of any arguments to defend Angular Material.

It’s an extremely development-heavy tool that takes some getting used to:

  • Stylization. This is a separate issue with this library if you don’t follow the recommended way to style components. Be prepared for Angular Material to change the css-selectors or nesting order of elements unilaterally, without migration methods
  • Upgrading Angular. You will never be able to upgrade to a new version of Angular for the first time in an Angular Material bundle. Even if you have a build, there is no guarantee that the interface retains the original rendering. As a consequence, you need regression or QA. If you don’t have the resources for this, be prepared to check everything personally by hand.
  • Reduced Customization of Components. While other libraries like Taiga-UI provide a huge list of tools and ways to customize each entity, you will need to either implement your own component from scratch or use ViewChild to manage the component to your needs.

If you have a project starting from scratch, spend some time and choose a UI library for your task and scalability conditions, before installing Angular Material. This will save you more time in the future for developing and maintaining the application

Angular Universal

For me, using SSR for Angular is a lazy way to fix component rendering bugs and the initial build size.

Before version 16, where Angular Hydration was added, you can consider using SSR in Angular as pointless.

First of all, Angular is a tool for big enterprise applications. It’s not for creating landing pages, portfolio pages or other “lightweight” apps. If you have chosen Angular, you hardly proceed from the point of view of creating an application with the best SEO or FCP. You a priori assume that the final Angular build will be larger than the other libraries.

You never know when you will need SSR

The unobvious fact is that even if your Angular application doesn’t have an SSR, you have to develop it as for SSR. We’re talking about tokenizing global APIs, including window, which doesn’t exist in a server environment. There is nothing wrong with this approach, but how many Angular developers know about this and stick to it?

You won’t be able to add SSR as instructed and run your application immediately if you use features or tools that only work in the browser. You will waste time, and resources, and possibly create a few bugs in the process of supporting SSR for the current application.

Zero flexibility

Now you have Angular Hydration which was introduced very recently, but you have learned about Qwik and Resumability. Can you use this knowledge and replicate it within Angular? — No. With all your experience, you can’t use it until the Angular team decides to add its support for Angular Universal.

If you have requirements for SEO or other critical metrics, choose an alternative technology that gives you new and better practices of SSR

Conclusion

Each developer is a consumer using a different technology created by another consumer developer. The most important thing is to find a place where you feel comfortable.

If you are a novice front-end developer looking for a vector to form your first stack, it’s better to give up Angular. It’s better to choose something lightweight — React, Vue, Svelte or other UI libraries that are based on native JS and HTML features. Since they don’t have strictly regulated dependencies, you will decide for yourself which tool to apply for a particular task. Thanks to this you will develop the skills of “Tool evaluation” and “Formation of requirements to the tool” within the framework of the assigned tasks.

It’s hard to say that the same React has a “zoo of technology”. It also has already formed 2–3 recommended tools around it for each type of task. From project to project, you will change their combination, but not the very basis of building an application. And you will quickly get into the project structure as well as in Angular.

The logical question. “Is Angular necessary”? — Strangely enough, yes.

What Angular was once able to offer for SPA, no one could repeat for a long time yet.

Despite lagging behind other tools and problems over the years, developers chose it to get a robust solution out of the box. You always worked with the technology you know in any project, it allowed you to build cross-functional teams without wasting resources on learning.

If you worked with RxJS before Angular, this will be a nice transition bonus for you. You couldn’t add RxJS to every application you created, because it requires team approval and justification for use. Angular encourages the use of RxJS and even gives you its own operators to make development easier.

You should have some experience with mistakes and skills in developing reusable solutions before migrating to Angular and getting a feel for usability.

--

--