A software engineer day to day work usually focused on some specific application or even piece of that software. When a long period of time developer works on only one program he may experience difficulties in the case he needs to start with a new project.
In this article, I would like to share my findings which come from frontend applications built by me in recent years.
First, a walk when planning a new application it has to be decided what is the main purpose.
There are two main directions for a web-based application:
A website with a public content
For content websites, it’s highly suggested to use server-side rendering such as Next.Js, Angular SSR, Gatsby, or similar. These technologies will provide a better performance as well as better Search engine optimization.
On the other hand, web or native applications are used when it’s required a high level of in-app interactions.
In addition, it has to be defined whether the application will have offline mode or features. In that case, the application is considered a Progressive Web App and the usage of service workers will be required.
State — Redux
UI — MUI or Bootstrap
Linting — Husky, eslint, prettier
Testing — Jest
CI/CD — Gitlab
The following folder structure can be used for the medium as well as small apps.
Components — all components. Each can have input/output
Containers — components defining specific layout
Pages — A page will use one of the containers and contains components.
Routes — contains route declarations
Config — Constants
Api specific files
General services — such as traces/logs, system notifications, etc
Store — Redux’s store files. Such as reducers
The root folder will contain package.json, eslint, etc.
.ENV — environment-specific constants
For large and multi-application projects consider reading the article “Semantic Grouping Folders with Nx”.
Authorization: Send credentials -> receive token. All manipulations with sensitive data should be working via the Authorization header.
Centralized system notifications
Generic pop-ups: Confirmation popup
User activity statistics: Backend should save each user action/request for further analysis or an external service can be used.
Modularity is achieved by separating functionality into components. Each of them should have one responsibility. Components will have Input/Output data.
Redux is one of the suggested options for state management. State flow in react app unidirectional **and **immutable. Immutability allows stability and debugging features such as states history. Where it’s possible to go “back in time” analyzing all the state changes.
There are two types of components:
*Stateless *— has I/O data and does not manage states
*Stateful *— manages states and transfers into the components. Also are dividing when to transfer state to regular components in order to reduce component rerendering
APi calls In browser *caching *— rarely updated data should be stored in Browser’s cache. It is achieved by setting cache headers for HTTP responses.
App files caching — images, fonts, js bundles should be cached in the browser.
Reduce components re-rendering by limiting states flow.
Lazy loading — the application will load only necessary bundle files. Achieved by code splitting techniques.
In general, the code has to obey industry best practices. The code has to be easily readable and built of small components/methods/classes where each has one responsibility focused.
However, each developer may have his own code style preferences. Therefore it’s strongly suggested to force project-specific code standards. Alignment with code standards can be achieved by using eslint library. Pre-commit hooks will secure that no non-standard code arrives in the Git repository.
In addition code formatting could be done by Prettier. This operation can be attached to the pre-commit hook as well.
Code readability can be achieved with the help of the CodeMetrics, SonarLint, SonarQube vscode plugins or similar. The tool will analyze the cognitive complexity of the code and will suggest improvements. In general functions/methods should be short and avoid multilevel nested loops or conditionals.
Type declarations are another important point in frontend development. Static type definition provides greater reliability as well as readability. The application should be written on TypeScript which has wide support and community.
Each component has to be test covered by at least 70%. Jest is one of the well-supported libraries for that purpose.
Git is the most preferred option for version control.
Each commit has to obey commit message standards. This link gives a good explanation. Following the standard will provide good readability of the app development history.
Gitlab can be used for managing deployments and Continous Integration. Repository updates have to be pushed as new branches. On each commit Gitlab will run unit tests.
After the code review and pipeline passes, a merge request can be created. After the MR is approved commits will become a part of the master/main branch and the original branch will be erased.
The application should have multiple deployment environments such as Stage, Dev, Production. The stage will have the latest master version. After it passes QA tests it can be promoted to Production.
The application has to be accessible to people with all, abilities. It has to support screen readers, color schemes, and font size adaptability.
Chrome Lighthouse dev tool can be used to analyze the level of accessibility covered by an application.
Multiple themes support. At least two: light and dark modes should be
Responsive — mobile-first approach. Guarantees the app will not lack functionality on all devices.
At least the following points should be considered when creating a Frontend application.
User-generated data sanitation. React and Angular natively support sanitation.
Auth token secure storage in HttpOnly only cookies. Refer to the explanation on the OWASP page.
Limit number of HTTP requests per user in order to avoid DDOS attacks
Limit login attempts
Styles separation — when creating custom styles separate a set of SCSS files containing all the common styles. In the case of migration to another SPA library, the styles can be reused.
It is always difficult to migrate a large codebase. For instance, an Angular app migrates to React. In the major part of the cases, each SPA library has its own architecture and it will not be possible to copy the components.
The current article discussed the main best practices which should be considered when planning a new frontend application. All the mentioned techniques may seem like an exaggeration. Though each of them increases the maintainability and reliability of an application.
Let’s recap what we learned:
Define the type of the project if it’s content-based or app
Code quality: Typescript, linting
Stability: Unit tests
UI: MUI, Bootstrap