BLOG
Angular Code Smells: What to Look for During Code Review
Code reviews are not only about finding bugs. They are also a good opportunity to catch patterns that can make an Angular application harder to maintain, test, and understand.
Here are some common Angular code smells worth looking for during a code review.
1. Too Much Logic in Components
A component should mainly handle UI-related responsibilities. If it contains complex business logic, API handling, transformations, and state management all together, it can quickly become difficult to maintain.
Code smell:
save(): void {
// validation
// data transformation
// API call
// state updates
// error handling
}
Better: Move business logic into services, facades, or dedicated state-management layers where appropriate.
2. Large Components
A component with hundreds of lines and many responsibilities is usually a sign that it could be split.
During review, look for:
If a component is difficult to understand as a whole, it is probably doing too much.
3. Repeated Logic
Duplicated logic makes code harder to maintain and increases the risk of inconsistent fixes.
During code review, look for the same business rules, conditions, calculations, or transformations repeated across multiple components or services. When the same logic appears in several places, consider extracting it into a shared utility, service, or reusable method.
The goal is to keep each piece of business logic in one clear, maintainable place.
4. Unnecessary subscribe() for One-Time Actions
Not every observable operation needs to be subscribed to directly in the component.
When a subscription is only used to trigger an operation, consider whether the service can handle the observable internally instead. This keeps subscription logic out of the UI layer and allows components to focus on user interaction and presentation.
The goal is to keep components focused on presentation and user interaction.
5. Overusing subscribe()
Having many manual subscriptions can make components harder to read and can introduce memory-leak risks.
Instead of:
this.service.data$.subscribe(data => {
this.data = data;
});
consider whether Angular's template mechanisms or signals can handle the data directly.
For subscriptions that are necessary, make sure their lifecycle is properly managed.
6. Complex Templates
Templates can become difficult to understand when they contain deeply nested conditions or repeated expressions.
Code smell:
Save
When conditions become complicated, consider exposing a clearly named computed value:
readonly isSaveDisabled = computed(
() => !this.formValid() || this.isReadOnly() || this.loading()
);
This makes the template easier to read and review.
7. Incorrect or Excessive Change Detection Work
Be careful with methods called directly from templates:
If the method performs expensive work, it may run more often than expected.
Depending on the situation, consider:
The important question during review is:
"Does this calculation really need to happen during every change-detection cycle?"
8. Using 'any' Everywhere
'any' can hide problems instead of solving them.
function handleData(data: any) { }
Prefer meaningful types or interfaces:
function handleData(data: Customer) {}
Strong typing makes refactoring safer and makes the code easier to understand.
9. Nested Subscriptions
This pattern is usually a warning sign:
this.userService.getUser().subscribe(user => {
this.orderService.getOrders(user.id).subscribe(orders => {
...
});
});
Nested subscriptions can make asynchronous flows difficult to follow.
Depending on the use case, operators such as switchMap, combineLatest, or forkJoin can make the flow clearer.
10. Forms With Scattered Validation Logic
When form validation is spread across multiple methods and components, it becomes difficult to determine why a form is invalid.
During review, check:
Keep form-related responsibilities predictable and centralized where possible.
11. Magic Strings
Code like this:
if (processType === 'STANDARD') {}
can become error-prone when the same values are used throughout the application.
Consider constants or enums where appropriate:
if (processType === ProcessType.STANDARD) {}
This also makes refactoring easier.
12. Comments Explaining Bad Code
A comment should explain why, not compensate for code that is difficult to understand.
If you find yourself writing:
// We need to do this because...
for a complicated block, first ask whether the code itself can be simplified or extracted into a well-named
method.
Good naming often removes the need for comments.
A Simple Code Review Checklist
When reviewing Angular code, ask:
Final Thought
Not every code smell means the code is wrong. Context matters.
The goal of a code review is not to make every piece of Angular code look identical. It is to identify patterns that could make the code harder to maintain, test, or extend.
A good review asks a simple question:
“Will this code still be easy to understand when we need to change it six months from now?”
A good Angular code review isn't about making the code look cleaner. It's about improving the boundaries, ownership, coupling, and behavior of the system so that the next change is cheaper than the last one.