We moved from Angular to React (Part 1)

By
on

"True wisdom is knowing that you know nothing—except how to pretend you know everything." — Nonsense Quote by ChatGPT (Irrelevant Picture by DALL-E 3)

The Beginning with Angular

In December 2022, we made our first commit to the open-source automation software. We had been working on something else before, but that's a story for another time. Abdul (the first employee at Activepieces) and me decided, "Let's go with Angular because that's what we know!"—and we didn’t think twice.

Abdul and I have been friends since high school, and we both started learning Angular in 2017, using it for our hobby projects (which was fairly popular JS framework at that time). Although I knew a little Vue, we were confident in our Angular expertise. We just wanted to release quickly, as we weren’t sure if this project would be a success or a failure.

Our Journey with Angular and Its Ecosystem

When we launched Activepieces in early 2023, users reported bugs that were difficult to reproduce. For example, when they selected a step in their workflow, it would automatically get deleted.

I'll share a few examples of these issues, with more on their side effects to come later.

Steep Learning Curve

Angular can be pretty overwhelming at first. It has a lot of rules and concepts like Modules, Components, Pipes, Directives, Guards, and Services. On top of that, there's RXJS, which can be confusing. Some of these complexities are there for good reasons, but many seem unjustified and outdated approach.

Note: Angular 17 and 18 addressed some concerns, we couldn't afford to fall behind and must stay up to date with the latest technology.

RXJS

We had a "Delete Step" button that removed the selected step in the workflow from the ngrx store using an observable. The code looked like this:

this.store.select(selectedStep)
  .pipe(take(1))  // Ensures only the current selection is taken
  .subscribe(step => {
    if (step) {
      this.deleteStep(step);
    }
  });

This approach can cause a few issues. If we forgot to unsubscribe when the delete dialog closed, the RxJS observable could run indefinitely in the background, deleting steps whenever one was selected. or If we forgot to add take(1), the observable would stay open, leading to repeated actions.

To fix this, we enforced a lint rule that banned direct subscriptions inside components. Instead, we used the async pipe in the HTML template, which prevents these mistakes.

<ng-container *ngIf="deleteStep$ | async as step"></ng-container>

With this approach, the async pipe automatically unsubscribes when the component is destroyed, and the deleteStep function just defines the deleteStep$ variable.

This approach, recommended by the Angular ESLint plugin, uses the async pipe to automatically handle unsubscription when the component is destroyed. The deleteStep function simply defines the deleteStep$ variable.

Later, we will discuss how these techniques led to a codebase that was unintuitive and full of boilerplate.

Angular Material UI

We chose Angular Material for its established reputation, which was ideal for quickly setting up our user interface. However, a couple of things went wrong.

  1. Customization Trouble: Overriding Angular Material’s default styles proved difficult. For example, changing a button’s color involved dealing with CSS conflicts, so we applied custom styles using ::ng-deep.

    ::ng-deep .mat-button.custom {
      background-color: #4CAF50;
      color: white;
    }
    
  2. Missing Components: Angular Material didn’t provide everything we needed, such as advanced data tables or custom date pickers. To address this, we integrated components from other sources and then attempted to match the style.

Ecosystem

The Angular ecosystem is smaller than React's, so we encountered more niche problems. In React, you can often find existing solutions on GitHub. For example, when building our own graph editor in Angular, we had to address issues like zooming, panning, and touchpad handling ourselves. In contrast, libraries like React Flow handle these challenges out of the box.

Take another example: Angular was tied to Webpack for a long time, which meant we missed out on hot module reloading. The compiler took about 30 seconds to update and refresh the page, causing us to lose the page state. This meant we had to redo all our actions just to get back to where we were.

Side Effects

Due to these issues, we experienced multiple effects, including slower development velocity and having Abdul and me as bottlenecks to team growth.

Lack of Learning Resource

We searched for open-source Angular projects comparable to GitLab, Metabase, and Posthog to learn about best practices for linting rules, authorization, folder structure, and separation of concerns. Unfortunately, we couldn't find open-source Angular projects that matched this level of complexity and detail. When we asked ChatGPT for top Angular SaaS projects, we mostly encountered basic tutorials or abandoned projects.

Hiring

We struggled to find talented Angular developers who were available and reachable. On the other hand, most candidates knew React and expressed little interest in working with Angular.

Codebase

Onboarding new engineers was challenging. We had to explain unconventional best practices, like creating logic as an observable but avoiding subscription in the component due to an ESLint rule. These unusual methods began to compound.

We experimented with different folder structures, but without example projects or learning resources, we had to figure things out on our own. This trial-and-error approach led to significant technical debt, which began to overwhelm us and took a toll on the codebase.

Decision to Move to Something Else

We were drained and knew that many companies fail when they try to rewrite their code. The process can be brutal—morale drops, customers get upset, and there's a lot of stress in keeping everything on track.

Despite these risks, we realized we had to make a change. The technical debt was piling up, onboarding new engineers was tough, and there were no good resources to guide us. It became clear that sticking with our current setup wasn’t working.

So, after much deliberation, we decided to explore other options. It wasn’t an easy choice, but we believed it was necessary for the future of the project. We’ll discuss what comes next and our new direction in Part 2, where we will discuss the differences between React and Angular.

Here's a JoJo meme for a dramatic cliffhanger: https://www.youtube.com/watch?v=7uBqNgxAuBA. Enjoy!

to-be-continued.jpg