Common mistakes that backend programmers make in Angular
Tags: angular, beginners, webdev, learning
Published: 2023-09-03 16:29:05

Recently, I found myself in charge of upgrading our Angular application from version 12 to 15. In my current role, which I've held for just over a year, most of my focus has been on the backend, and I hadn't even touched the frontend apps until now. I've got to be honest; this upgrade process turned out to be a tougher nut to crack than I initially thought. It's not a knock on the Angular team's [Angular Update Guide](https://update.angular.io/), though; they did a solid job. The real challenge came from the fact that we had a bunch of custom-made components that didn't quite follow the best practices playbook.

To provide a bit more context, our entire team primarily comprises .NET developers who possess some familiarity with HTML, CSS, and basic JavaScript. The rationale behind this choice stemmed from the belief that transitioning to Angular would be smoother for backend developers, possibly owing to the familiarity with the MVC (Model-View-Controller) pattern. At the time of this decision, Angular was also considered the go-to standard choice for frontend development.

Although I did manage to pull it off eventually, here's what tripped me up along the way and some insights into areas where improvements could be made. Here are the key points I'd like to highlight:


## 1. ESLint
While reviewing the codebase, I couldn't help but notice numerous inconsistencies and common mistakes that would have been easily caught by `ESLint`. After setting up and configuring it with recommended rules for `Angular`, `TypeScript`, and `RxJS`, I ran the linter. To my surprise, it uncovered over 5000 linting issues, with approximately 2000 of them being automatically fixable.

![Linting meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rr0pw74jn9kpvswlgs5w.png)

However, my primary goal wasn't to fix all these linting errors, as I couldn't guarantee that doing so wouldn't introduce unforeseen issues. Instead, I chose to install [husky](https://www.npmjs.com/package/husky) and set up linting and automatic fixing exclusively for committed files. In cases where automatic fixing wasn't feasible, I encouraged the developers to follow the "boy scout rule" and address the errors they encountered. As I delved into these improvements, I also took the opportunity to seamlessly integrate and configure [prettier](https://www.npmjs.com/package/prettier) alongside `ESLint`. After all, who enjoys scrolling through diffs or pull requests with only formatting changes? It just makes the code review process smoother and more pleasant for everyone involved.


## 2. TypeScript
![any meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpfxndu2u4pjtv2bafjy.png)

I found myself inspecting the codebase in sheer horror as I discovered that practically everything was marked as `any`. The most significant advantage of **Type**Script seems to have been overlooked. 

Picture writing C# code where everything is either an anonymous type or simply of type `object` – it's akin to a maintenance nightmare.

This is one of the factors that made me hesitate to automatically fix all the linting issues and approach the upgrade with caution.


## 3. Unit tests

![testing meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5vcm1q02c1twae257do.png)

This brings me to the second reason why I hesitated to automatically fix all the linting issues. There are numerous factors contributing to the absence of unit tests in our app, ranging from time constraints and knowledge gaps to a perceived low value, lack of a testing culture, and motivation. I won't delve too deep into each of these reasons, but I can say that the lack of unit tests significantly undermined my confidence in the app's stability following the upgrade. As it turned out, my concerns were justified, as various issues did arise and required subsequent fixes.


## 4. RxJS: observables and higher-order observables


![observables meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y5gl1gfqhnzgmildz47s.png)



I've noticed that our codebase includes a fair share of `Promises`, `async/await`, and `RxJS` usage. Let's establish one thing: `RxJS` not only covers all the functionality of `Promises ` and `async/await` but does so with superior flexibility and maintainability. One of the standout features of `RxJS` is its inherent support for cancellation and resource cleanup. `Promises` and `async/await` don't provide these capabilities out of the box; you'd often need to resort to workarounds or third-party libraries to achieve similar functionality.

I've also come across a common mistake in the codebase where developers tend to nest subscriptions. This practice can introduce a lot of problems, including memory leaks, unpredictable behavior, challenges in error handling, code that becomes increasingly complex and hard to maintain, and a loss of control over the execution flow.

Adding to the mix, I also stumbled upon `Promises` and `async/await` nested within `RxJS` subscriptions. This not only heightened the complexity of the code but also made it significantly harder to understand and read. What's more, there's the looming threat of potential deadlocks because `RxJS` is tailored to function seamlessly with non-blocking, asynchronous operations, and introducing `async/await` or `Promises` into the mix can disrupt this delicate balance.

To steer clear of all these potential issues, I strongly recommend sticking to observables and higher-order observables as the primary means of chaining or flattening them.


## 5. Composition over inheritance


![composition over inheritance meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kab1jd06ziu1u9kscdtu.png)



This one is probably my favorite. Angular favors composition over inheritance and it's easy to get caught up in the inheritance trap.

Let me illustrate this with an example. Imagine the business comes to me and says, "Hey, we need two forms for our new app." And I'm like, "Sure thing, here they are."

```typescript
export class ContactComponent {
  errors: string[] = [];
  formData: FormGroup = new FormGroup()

  displayErrors() {}

  submitForm() {}
}

export class RegistrationComponent {
  errors: string[] = [];
  formData: FormGroup = new FormGroup();

  displayErrors() {}

  submitForm() {}
}
```

Later on, they throw in a request for a new login form. That's when I begin to notice some code repetition. So, I decide to follow the DRY principle and create a base class to handle all this shared functionality.

```typescript
export class BaseFormComponent {
  errors: string[] = [];
  formData: FormGroup = new FormGroup();

  displayErrors() {}

  submitForm() {}
}

export class ContactComponent extends BaseFormComponent {}
export class RegistrationComponent extends BaseFormComponent {}
export class LoginComponent extends BaseFormComponent {}
```

Everything seems to be shaping up nicely. I've gone ahead and refactored the code, successfully eliminating those pesky code duplications.

Now, here comes the business again, this time requesting a rating widget to be added. "No worries," I think, "I've got this covered."

```typescript
export class RatingComponent extends BaseFormComponent {}
```

Somewhere in the back of my mind, I can't help but think that the rating form doesn't need any validations. But hey, for the sake of consistency, I'll stick to using that trusty base class. Who knows, even if it doesn't need those features right now, it might come in handy down the road, right? Better safe than sorry.

And then, the business strikes once more, this time requesting a new email notification feature. They want users to receive a thank-you email after every contact, registration, or rating submission.

This seems like a straightforward task. I'll just inject an `SMTPClient` into the base class, and then I can easily share the email notification logic with all its subclasses.

```typescript
export class BaseFormComponent {
  errors: string[] = [];
  formData: FormGroup = new FormGroup();

  constructor(private smtpClient: SMTPClient) {}

  displayErrors() {}

  submitForm() {}
}

export class ContactComponent extends BaseFormComponent {
  constructor(private smtpClient: SMTPClient) {
    super(smtpClient);
  }
}
export class RegistrationComponent extends BaseFormComponent {
  constructor(private smtpClient: SMTPClient) {
    super(smtpClient);
  }
}
export class LoginComponent extends BaseFormComponent {
  constructor(private smtpClient: SMTPClient) {
    super(smtpClient);
  }
}

export class RatingComponent extends BaseFormComponent {
  constructor(private smtpClient: SMTPClient) {
    super(smtpClient);
  }
}
```

After going through my changes, something just doesn't sit right. Why on earth should my `LoginComponent` have to deal with injecting the `SMTPClient`? This is a significant code smell, no doubt about it. 

In a nutshell, here's what I've managed to accomplish so far:
- Created code that's less flexible and harder to maintain.
- Introduced tight coupling between components.
- Achieved a delightful level of code duplication.
- Made writing tests feel like an Olympic event.
- And let's not forget the introduction of the notorious fragile base class problem.

Quite the list, isn't it? 😅

Sadly, this is precisely the situation I'm grappling with in the codebase I'm currently working on.

To address these issues effectively, we need to shift our focus to composition instead. Remember the `is-a` vs `has-a`?
- A `RatingComponent` has an `SMTPClientService`
- A `LoginComponent` has a `ValidationEngineService`
- A `RegistrationComponent` has a `ValidationEngineService` and an `SMTPClientService`

By using services in this manner, we can craft **reusable code** that's not only **easier to maintain** but also **decoupled**. Plus, writing unit tests becomes a breeze. This approach offers a fresh perspective and a cleaner path forward.

---


That's a wrap for my rant. Time to go brew some coffee. If any of you have questions or want more examples, feel free to ask in the comments – I'm here to help and answer them all! ☕😊

_If you liked my article and would like to buy me a coffee, you can do it [here](https://www.buymeacoffee.com/vixero)._