Best Practices & Guidelines for web applications using Angular
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.
Following are the set of patterns and best practices to build Angular applications which helps in write clean code, maintain coding standards & performance.
The Angular CLI is a command-line interface tool that is used to initialize, develop, scaffold, maintain, and even test and debug Angular applications.
ng new- To create an application that already works, right out of the box.
ng generate- To Generate components, routes, services and pipes with a simple command with test shells.
ng serve- To test your app locally while developing.
ng test- To run your unit tests or end-to-end tests
ng lint- To make your code shine
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.
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.
- Names of folders and files should clearly convey their intent.
- Names should be consistent with the same pattern in which we mention the file’s feature first and then the type, dot separated.
If we want to add more descriptive names to our files we should use a dash(-) to separate the words in the name:
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:
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.
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
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:
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.
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
We can import all things by using the source folder name.
Change Detection Optimisations
- 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.
- Move complex calculations into the
ngDoChecklifecycle hook to make your expressions faster.
- Cache complex calculations as long as possible
- 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.
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 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.
Following are some of the best practices to improve the performance of a web app.
All file should be minified (HTML, CSS, JS)
Images, scripts and CSS need to be lazy loaded to improve the response time of the current page
DNS of third-party services that may be needed are resolved in advance during idle time using dns-prefetch.
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
Pre loading & prefetching improves in TTI & FCP
webp format for serving images
CSS bundle optimisation
use purge css to optimise css bundle
JS bundle optimisation
Caching static assets & using CDN with larger expiry
Inlining Critical styles to serve initial content fast
Helps in FCP
Add inline styles for application fonts & Serve fonts from cdn
<link rel="preload" as="font" type="font/woff2" href="assets/fonts/base-fonts/Nunito_600.woff2" crossorigin />
Widely used with PWAs
Regular maintenance of third party or angular libraries
Cross-Origin Resource Sharing (CORS)
Cookie flags set to HttpOnly, Secure and SameSite
Some of the tools we use for Security Auditing
- SecurityHeaders.io to analyse the HTTP response headers
- ZAP for dynamic analysis
- BurpSuite for manual security testing
- 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.
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 firstname.lastname@example.org
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.