Best Practices & Guidelines for web applications using Angular

Angular Apr 9, 2021

At Halodoc, we do have plenty of web applications with continuous releases and feature improvements. With such feature velocity, it would become a challenge for the team to do things in the best way. As time to market ends up with priority, we want to introduce a set of guidelines that represents the most efficient ways to approach a problem in a given business situation.

One of the highest priorities of Halodoc is to enable best practices and to follow Angular guidelines from the start.

Considering that Halodoc has been using Angular for the past 5 years or so, I find that Angular is one of the best front-end frameworks out on the web today.

Angular, developed by Google as a re-write of AngularJS, is the most powerful framework for building dynamic programming structures. The main building blocks of Angular are modules, components, metadata, templates, data binding, services, directives, and dependency injection.

Without further delay, let’s list some of the best practices we have followed to build web applications.

Best Practices

Following are the set of patterns and best practices to build Angular applications which helps in write clean code, maintain coding standards & performance.

Angular CLI
The Angular CLI is a command-line interface tool that is used to initialize, develop, scaffold, maintain, and even test and debug Angular applications.

  1. ng new - To create an application that already works, right out of the box.
  2. ng generate - To Generate components, routes, services and pipes with a simple command with test shells.
  3. ng serve - To test your app locally while developing.
  4. ng test - To run your unit tests or end-to-end tests
  5. ng lint - To make your code shine
  6. ng add @angular/pwa - To set up the Angular service worker

This interface can be used to create an initial-level structure for the application. It’d be much easier for developers to understand the folder structure and app flow using Angular CLI. Ultimately, it saves hours of developers time.

Module Structure

The modular structure of Angular arranges the code into different modules so all services and components are divided into different groups when you construct them. In Angular coding, you can separate functionality into reusable pieces of code.

Files and Folders

Naming conventions are hugely important to maintainability and readability and help to provide a consistent way to find the content at a glance. Consistency within the project is vital which help in tremendous efficiency.

Let’s see how to name our files and classes and how to organize the folder structure in a Web App.

1. File Naming

While creating files, we should pay attention to the file names.

  1. Names of folders and files should clearly convey their intent.
  2. Names should be consistent with the same pattern in which we mention the file’s feature first and then the type, dot separated.

For example consultation.component.ts or home.component.html or auth.service.ts

If we want to add more descriptive names to our files we should use a dash(-) to separate the words in the name: tc-home.component.ts book-appointment.component.ts

2. Class Names

When we add names to classes, we should use upper camel case style with the added suffix that represents the type of our file: DatepickerDirective TcHomeComponent AuthService

3. Folder Structure

Angular Coding Practices

Coding standards are the ways of programming the software. In Angular applications, certain coding styles can be  followed for the best  user experience.

Developers usually find it difficult to fix bugs and reflect on immediate issues when dealing with complex code structures.

Here’re some set of rules that you need to follow to make your project comply with the standard Angular style guide

  • Per file, the code must not exceed from 400 lines limit
  • Per function, the code must not exceed from 75 lines
  • Utilise custom prefix to prevent element name collisions with components in other apps and with native HTML elements.
  • If the values of the variables are intact, declare it with const
  • Names of properties and methods should be in lower camel case
  • Always leave one empty line between imports and module such as third party and application imports and third-party module and custom module
  • We shouldn’t name our interfaces with the starting capital I letter as we do in some programming languages.

Single Responsibility Principle

It is very important not to create more than one component, service, directive… inside a single file. Every file should be responsible for a single functionality. By doing this, we are keeping our files clean, readable, and maintainable.

Breaking down Components

This might be an extension of the single responsibility principle not just to the code files or the methods, but to components as well. The larger the component is, the harder it becomes to debug, maintain and test.

If a component is growing big, break it down into multiple, more manageable, smaller components, dedicating each one to an atomic task.

Using Interfaces

If we want to create a contract for our class we should always use interfaces. By using them we can force the class to implement functions and properties declared inside the interface.

The best example for this is to have angular life cycle hooks in your component: class HomeComponent implements OnInit, OnDestroy

Using interfaces is a perfect way of describing our object literals. If our object is an interface type, it is obligated to implement all of the interface’s properties.

TypeScript will show an error if an object doesn’t contain all of the interface’s properties, and light up intellisense for us while populating that object:

Using Immutability

Objects and arrays are the reference types in javascript. If we want to copy them into another object or an array and to modify them, the best practice is to do that in an immutable way using es6 spread operator(…)

This way, we are deep copying the user object and then just overriding the status property. Now, the original article is going to be preserved with all of its values.

Safe Navigation Operator (?)

To be on the safe side we should always use the safe navigation operator while accessing a property from an object in a component’s template. If the object is null and we try to access a property, we are going to get an exception. But if we use the save navigation (?) operator, the template will ignore the null value and will access the property once the object is not null anymore.

Prevent Memory Leaks in Angular Observable

Observable memory leaks are very common and found in every programming language, library, or framework. Angular is no exception to that. Observables in Angular are very useful as it streamlines your data, but memory leak is one of the very serious issues that might occur if you are not focused. It can create the worst situation in mid of development. Here’re some of the tips which follow to avoid leaks.

  1. Using async pipe
  2. Using take(1)
  3. Using takeUntil()

Using index.ts

index.ts helps us to keep all related things together so that we don’t have to be bothered about the source file name. This helps reduce the size of the import statement.

For example, we have article/index.ts as

We can import all things by using the source folder name.

Change Detection Optimisations

  1. Use NgIf and not CSS - If DOM elements aren’t visible, instead of hiding them with CSS, it's a good practice to remove them from the DOM by using *ngIf.
  2. Move complex calculations into the ngDoCheck lifecycle hook to make your expressions faster.
  3. Cache complex calculations as long as possible
  4. Use the OnPush change detection strategy to tell Angular there have been no changes. This lets you skip the entire change detection step.

Build Reusable Components

If there is a piece of UI that you need in many places in your application, build a component out of it and use the component. This will save you a lot of trouble if, for some reason, the UI has to be changed. In that case, you do not go around changing the UI code in all the places. Instead, you can change the code in the component and that is it.

For this, you may have to use property and event bindings to pass inputs to the components and receive output events from the components, respectively.

Using trackBy in NgFor

When using ngFor to loop over an array in templates, use it with a trackBy function which will return a unique identifier for each DOM item.

When an array changes, Angular re-renders the whole DOM tree. But when you use trackBy, Angular will know which element has changed and will only make DOM changes only for that element.

Using Smart - Dumb components

This pattern helps to use OnPush change detection strategy to tell Angular there have been no changes in the dumb components.

Smart components are used in manipulating data, calling the APIs, focussing more on functionalities, and managing states. While dumb components are all about cosmetics, they focus more on how they look.

Using strict types instead of "any"

While working on an Angular project, developers, generally end up typing ‘any’ to declare variables. If you are not specifying the variables and constants, they will be assumed by the value and as a result, will be assigned to it. If it happens, you are now in trouble as it will create some unintended issues, anytime.

For example;

If you code like this;

And, if you code like this;

you may not get all you wanted. In short, you can prevent this by typing the number instead of typing ‘any’.

Module Organisation and Lazy Loading

Utilizing lazy load the modules can enhance productivity. Lazy Load is a built-in feature in Angular which helps us with loading the things on demand. When have used LazyLoad it helps in reducing the size of the application by abstaining from unnecessary file from loading. Following are the modules, which are been used in angular apps widely

  • Multi Modules
  • Routing Modules
  • Shared Modules
  • Lazy Load Modules

Use lint rules for Typescript and SCSS

Linting forces the program to be cleaner and more consistent. It is widely supported across all modern editors and can be customized with your own lint rules and configurations.

Always Document

Always document the code as much as possible. It will help the new developer involved in a project to understand its logic and readability.

It is a good practice to document each variable and method. For methods, we need to define it using multi-line comments on what task the method performs and all parameters should be explained.

Cache API Calls

Caching the API calls especially on the website limits the number of server requests to fetch redundant information thus saving time and reduce the load on the server.

To utilize the caching, one needs to make an HTTP request and then store the results of that request in memory, which can be served once again whenever required without requesting to the server. This helps in user to make fewer HTTP requests to the server and on return had to wait less for the response every time it required.

Performance

Following are some of the best practices to improve the performance of a web app.

Name

Priority

Comments

Minification

High

All file should be minified (HTML, CSS, JS)

Lazy loading

High

Images, scripts and CSS need to be lazy loaded to improve the response time of the current page

DNS resolution

High

DNS of third-party services that may be needed are resolved in advance during idle time using dns-prefetch.

Pre-connection

Medium

DNS lookup, TCP handshake and TLS negotiation with services that will be needed soon is done in advance during idle time using preconnect.

pre load & pre fetch

Medium

Pre loading & prefetching improves in TTI & FCP

webp format for serving images

High

 

CSS bundle optimisation

High

use purge css to optimise css bundle

JS bundle optimisation

High

 

Compression

High

brotli

Caching static assets & using CDN with larger expiry

High

cache-control: max-age=2592000

Inlining Critical styles to serve initial content fast

Medium

Helps in FCP

Add inline styles for application fonts & Serve fonts from cdn

High

<link rel="preload" as="font" type="font/woff2" href="assets/fonts/base-fonts/Nunito_600.woff2" crossorigin />

<style>
@font-face {
font-family: Nunito; src: url(assets/fonts/Material_icon_font.woff) format('woff');
}
</style>

Service Workers

Low

Widely used with PWAs


Security

Name

Priority

Regular maintenance of third party or angular libraries

Medium

x-xss-protection

High

Content-Security-Policy

High

X-Content-Type-Options "nosniff"

High

Cross-Origin Resource Sharing  (CORS)

High

CSRF

High

strict-transport-security (HSTS)

High

x-frame-options

High

Referrer-Policy "no-referrer-when-downgrade";

High

Feature-Policy (Permissions-Policy)

High

Cookie Scope

High

Cookie flags set to HttpOnlySecure and SameSite

High

Rate limiting

High


Some of the tools we use for Security Auditing

  1. SecurityHeaders.io to analyse the HTTP response headers
  2. ZAP for dynamic analysis
  3. BurpSuite for manual security testing
  4. SonarQube for Code Quality & Code Security

These are the best practices that a developer should keep in mind to keep the project efficient and the code easier to manage and debug.

Conclusion

Building web applications and scaling them is a continuous exercise, and there’s always scope to improve the way we write code and build apps. With this comprehensive, opinionated blog on the best practices, I tried to cover all the major aspects and best practices that can be used by businesses in their development process and reap maximum benefits from it.

This list of best practices is a great place to start and applying these things to your project will make your application clean, less buggy and enhance the application performance.

Interesting in exploring with us

We are always looking out for top engineering talent across all roles for our tech team. If challenging problems that drive big impact enthral you, do reach out to us at careers.india@halodoc.com

About Halodoc

Halodoc is the number 1 all around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke.
We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 2500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allows patients to book a doctor appointment inside our application.
We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates foundation, Singtel, UOB Ventures, Allianz, Gojek and many more. We recently closed our Series B round and In total have raised USD$100million for our mission.
Our team work tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.