Since you are privileged not only to code your code but also to make minimal decisions, you are fully responsible for the project’s future. Including for the cost of its support. Having experience with long-term stories, we’ve put together some tips on how not to fail your colleagues and those who come after you.
Our advice may seem obvious to the experienced programmers, but for beginners, we strongly recommend reading. Take the time to adjust these ideas to your projects to spend less time on refactoring. Similar ideas can be expressed in almost any development area, but we will talk about the React project.
Starting projects from scratch, you think about how to organize everything so that later it will not be excruciatingly painful due to endless rework. In personal pet projects, you can do anything you want and live the aftermath. However, in teamwork, you need to make it easier for colleagues to understand the essence and dive into details. Thus, don’t reinvent development approaches — it’s better to stick to well-established, industry-recognized practices.
Regardless of the development language, static typing is beneficial in long-term projects. If it is impossible to use static typing on a project for some reason, then even JSDoc will significantly help maintain the quality of the code.
However, we are not going to advise you to use typing everywhere. Static typing is about large projects since typing helps the team. Typing organization and maintenance requires specific resources. In a small short-term project where 1–2 people work, this investment is pointless.
If the use of typing is justified, we recommend you to create types from the very beginning; otherwise, you will have to spend much more time on this work. Even if you don’t have an API ready and don’t know the data model, make stubs so that the generic type does not appear anywhere.
As the project develops, typing must be adhered to, even if all the deadlines are failing. After all, you will thank yourself for this perfectionism.
Divide the code into blocks, isolate the logic
The code should not be lumped together. It is worth considering the hierarchy in the project structure — breaking the code into modules and blocks, dividing components. It is more convenient to maintain and develop such a structure than search for the logic in one big heap, especially if these particles are interconnected. Perhaps this advice applies not only to the frontend.
The usefulness of this principle is evident in the projects with large and sophisticated forms. Block checks and the visibility of blocks are linked. When we collect all the communication of fields in one place, it becomes much easier to track logic changes. Most importantly, we were able to reuse the code in a new similar project.
There is no universal standard for the structure of a project. You have to rely on your knowledge. Two approaches might work for many projects: File Type First and Feature First.
To find a starting point for finding a suitable structure for you, we recommend that you refer to the documentation. The best practice is usually described there. For example, React offers standards for organizing a project’s logic and file structure in the documentation. With some experience, you can experiment and deviate from the standards if it allows you to work around some restrictions for a particular project. Still, in most cases, this is not necessary.
Do not forget about such “basic” principles as SOLID. Excellent article on how to apply these principles to React: https://www.innofied.com/solid-object-oriented-principles-in-react-native-architecture/. There is also a good collection of materials on React/Redux: https://github.com/markerikson/react-redux-links/blob/master/project-structure.md.
The architect who initially plans the project always has some patterns in mind. It is worth expressing them explicitly in the form of a project passport, supplementing it with the rules for writing code, for example, what can and cannot be included in a specific module. Such “passport” will help developers determine how to write in advance so that later not to rewrite. This can reduce review time and help standardize coding approaches, which is especially useful when remote workers or distributed teams work on a project.
Stick to the chosen paradigm
If you decided to adhere to some approach at the start, for example, atomic web design, you should not abandon it as soon as the deadlines approach. Initiated and abandoned support for a paradigm is sometimes worse than its complete absence. If you “give free rein to chaos,” it will be challenging to restore order — you will have to spend time returning to the paradigm. Moreover, you won’t quickly “jump” to another one, since the project is already a shapeless mess.
Rejection of the paradigm for the sake of timing or other factors is like changing horses in the middle of the stream. It is painful and not recommended. However, if there is no other way, you need to cover most of your application with tests.
Tests have to test the project. It is necessary to include tests in the project at the very first stage — as soon as development begins. Otherwise, you can change something in the application at a certain point and then get out of the release deadlines, restoring the performance of different parts of the project, which are covered only by manual testing.
Let the tests be small at the start and do not check anything at all. But it is much better to develop them as the functionality grows than to spend weeks later paying off this “technical debt.” In terms of the amount of time spent, the first approach is more effective. Many are well aware of this, but still leave the tests for later.
I would also like to mention integration and end-to-end tests.
Integration tests should be run every time you fix bugs or add new features to ensure that the adjustments have not affected anything. On our projects, we try to launch them automatically when we deploy the results of our work. If tests did not work out, you should not upload changes to the project. First, you need to fix everything.
However, integration tests only concern the frontend. End-to-end tests are slower and test the interaction of all project items. These tests simulate user actions. Here we are not mocking anything, but wait for the backend’s responses and check interactions within the entire ecosystem of the project using Cypress.
Think before updating, but do not give up on it
In the front end world, everything changes very quickly — versions of frameworks replace each other, more successful libraries appear. It is worth keeping them up-to-date, but do not be fanatical.
Like typing any update costs resources (i.e., indirectly, money). However, there are a few things that make it impossible to opt-out updates completely.
First, support for even the most successful libraries sometimes ends. In this situation, you will have to go for more promising analogs.
Second, working with the old technology stack rarely inspires developers. If you realize that the team cannot be kept on some dusty Backbone or notice that an outdated stack is critically affecting the influx of new developers, you will have to update.
Refreshments should be taken as part of the natural project evolution. Still, before changing a library or updating a framework, you should always do some research.
Is the new version ok for you? How to organize the transition in general? Of course, you need to plan everything.
Updates are complicated if there are no tests on the project. Even a small date library can interact with many different parts of a project, and its updating leads to full regression testing. With the project’s growth, it will not be possible to do it efficiently, having only manual tests in the arsenal.
Are you doing well now?
The ratio of the time spent on developing new features versus the time spent on bugs can be a good efficiency measure of your project.
In addition to non-numerical characteristics, such as “developer satisfaction from the project,” there is such an indicator as the time for a new person to join the project. This is a characteristic of how well a project is described in the documentation, tests, and coding conventions.
Remember, it’s better to leave behind better code than it was before you. Do not generate technical debt; otherwise, it will spoil the development of the project later.