To the experienced, our advice may seem obvious. But for beginners, we strongly recommend reading. Take the time to translate these ideas into your projects so that you don’t spend even more on endless refactoring later.
Similar ideas can be expressed in almost any area of development, but we will talk about them using the example of projects in React.
Starting projects from scratch, you think about how best to organize everything so that later it will not be excruciatingly painful due to endless rework. In personal pet projects, you can fence anything you want – then live with it yourself. But in teamwork, you need to act in such a way as to make it easier for colleagues to understand the essence and dive into details. Those. Don’t re-invent development approaches – it’s better to stick to well-established, industry-recognized practices.
Think about typing
Regardless of the liberties in development languages, static typing is very useful on large long-term projects. If for some reason it is impossible to use static typing on such a project, then even JSDoc will significantly help maintain the quality of the code.
But we are not going to strongly advise you to always use typing. It was not in vain that a reservation was made above about large projects, since typing is, first of all, helping the team. But its organization and maintenance (the same type change in accordance with changes on the backend) requires certain resources. In a small short-term project where 1-2 people work, this investment is pointless. But they are necessary when the team is large and will expand.
If the use of typing is justified, we recommend that you create types from the very beginning, otherwise you will have to spend much more time on this work. Even if you don’t have any API ready and you don’t know the data model, at least 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 have been burned out for a long time. Then you will thank yourself for this perfectionism.
Divide the code into blocks, highlight the logic
The code shouldn’t be lumped together. It is worth considering the hierarchy in the project structure – breaking the code into modules and blocks, dividing components into smart and stupid. It is more convenient to maintain and develop such a structure than looking for particles of this 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 was very clearly visible on a project with large forms, which we recently wrote about (https://habr.com/ru/company/maxilect/blog/511322/). In that project, block checks and the visibility of those blocks were initially linked. But when we collected all the communication of fields in one place, it became much easier to track the changes in logic. And most importantly, we were able to reuse the code in a new similar project.
There is no single standard for the structure of a project – here you have to rely on your knowledge. There are two approaches that might work for many projects: File Type First and Feature First.
To find a starting point for finding a structure that is convenient for you, we recommend that you refer to the documentation. Best practice is usually described there. For example, React and Redux in their documentation offer standards for organizing the logic and file structure of a project. With some experience, you can experiment and deviate from the standards if it allows you to work around some restrictions for a particular project, but in most cases this is not necessary. And, of course, do not forget about such “basic” principles as SOLID. Nice article on how to apply these principles in React: https://habr.com/ru/company/ruvds/blog/428079/. Good article on clean architecture: https://habr.com/ru/post/499078/.
On the organization of the codebase in React and related issues, there is a good, albeit not updated for a long time, collection of materials: https://github.com/markerikson/react-redux-links/blob/master/project-structure.md…
Formulate a coding convention
The architect who initially plans the project based on the path along which the application will develop has certain templates 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 separate module (in general, this is a rather philosophical question). Such a “passport” will help developers to determine in advance how to write, so that later not to rewrite. This can reduce review time and help standardize coding approaches, which is especially useful when a project is being worked on by remote workers or distributed teams.
Stick to the chosen paradigm
If at the start you decided to adhere to some approach, for example, atomic web design, you should not abandon it as soon as the deadlines begin to run out. Initiated and abandoned support for a paradigm is sometimes almost worse than its complete absence. If you “give free rein to chaos” a little, it will be extremely difficult to restore order – you will have to spend time returning to the paradigm. And you won’t be able to quickly “jump” to another one, since a shapeless mess has already formed in the project.
Rejection of the paradigm for the sake of timing or other factors is like changing horses at a crossing. It is painful and not recommended. But if there is no way out, you need to cover most of your application with tests. And here we move on to the next tip …
Tests on a project don’t just have to be. They have to work. And it is necessary to include them in the project at the very first stage – as soon as development begins. Otherwise, at a certain point, you can change something in the application, 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 a week later on paying off this “technical debt”. In terms of the amount of time spent, the first approach is more effective. By the way, 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 make sure that the adjustments have not affected anything. On our projects, we try to launch them automatically when we upload the results of our work (we implemented this through a hook). Tests did not work out – you should not upload changes to the project. First you need to fix everything.
But integration tests only concern the frontend. End-to-end tests are slower and test the interaction of the application with all dependent items. These tests simulate user actions. Here we are not mocking anything, but really waiting for backend responses and checking interactions within the entire ecosystem of the project using Cypress (we can also recommend Puppeteer as an analogue).
Think before upgrading, but don’t give up
In the frontend world, everything changes very quickly – framework versions replace each other, more successful libraries appear. It is worth keeping them up-to-date, but not fanatical.
Like typing, which we talked about above, any update costs resources (i.e., indirectly, money). But there are a few things that make it impossible to completely opt out of updates.
First, support for even the most successful libraries sometimes ends. In this situation, you will have to go for more promising analogues.
Second, working with the old technology stack rarely inspires developers. If you realize that the team cannot be kept on some dusty Backbone, or you 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 order of things. But before changing a library or updating a framework, you should always do some research. Is the new version right for you? How to organize the transition in general? And of course, you need to plan everything in advance.
Updates are extremely difficult if there are no tests on the project. Even a small date library can cover many different parts of a project, and updating it leads to full regression testing. With the growth of the project, it will not be possible to do it efficiently, having only manual tests in the arsenal.
Are you doing well now?
The measure of how efficiently you develop your project can be the ratio of the time spent developing new features versus the time spent on bugs. Depending on the approach, this indicator can be more or less, so we will not undertake to set “target” levels. You yourself can compare the situation before and after any transformations.
In addition to non-numerical characteristics, such as “developer satisfaction from the project,” there is such an indicator as the time a new person joins the project. This is a characteristic of how well a project is clearly described through documentation, tests, and coding convention.
And remember, it’s better to leave behind better code than it was before you. Do not generate technical debt, otherwise it will hinder the development of the project later.
Perhaps you have your own tips? Leave in the comments!
PPS The title picture from the recently held Playhouse Competition.