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
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
becomeshigh-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 becomeCSS
? - 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
orXHR
? - Can you handle requests and errors globally without using an
Interceptor
? - Can you name ways to undo an
HTTP-request
without usingswitchMap
? - Do you know of
axios
ornode-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 toZoneLess
, 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
orElf
. 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 checkingimports
. It is due to it that you will optimize the application build. You should also usesecond-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 9
— Please 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 onKarma
?
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 thecss-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 useViewChild
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 ofSSR
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.
But perhaps we will see a solution to all the problems within “Angular Renaissance” soon