Developing Better Node.js Developers
In the last few months we have been experiencing a huge growth in the NodeJS department at Wolox, for that reason we have had a lot of Express.js API training. As a result, we started noticing that there were some recurrent issues that gave the trainees more headaches than the rest.
So we gathered up with all the trainers on the team and started to take note of all these problematic issues. By doing so, we started to pay special attention to all of them with the idea of improving the learning material we had about them and being specially focused on ensuring that the trainees got to understand them correctly.
Therefore I think it’s a good idea to share with all of you some of the more important and recurrent points of our list. So you can have them in mind and give them some special attention even if you are training someone or if you are learning Node.js.
One of the great benefits of using NodeJS is the ability to work so naturally with asynchronous code execution, but it’s also one of the more troublesome issues when you are getting to know Node.js and any JS framework. Either if it’s callbacks, promises or async/await there’s always some trouble to understand how they work, how to use them, what is really going on and why my code isn’t behaving as I expected.
In our training, nowadays we pay special attention to promises and to help our trainees to understand how to use them, how to nest them, how to run concurrent stuff, how to handle errors and not to forget any return statement, we provide them the following article and interactive workshop. With those tools, they get to understand a little bit the basics of how promises work and play along to know how to handle them. Then in further steps of our training, the use of promises is pretty common, so they end up being fairly comfortable around promises.
While making endpoint tests in an API, we can divide the tests into two different types. On one hand we have the type of tests that ensure that our endpoint responds correctly, ergo that our endpoint fulfills a given contract for the successful cases and for the invalid cases, and on the other hand we have the type of tests that ensure that our API does the things we actually want it to do without taking into account the response.
However, many times I’ve seen people make the mistake of only focusing on one of those two types of tests, forgetting completely about the other one. For example, we are testing the creation endpoint of a user controller and we test that when we receive the correct parameters the server returns successfully and when the parameters are invalid or incorrect, the server returns an error. But we forget to check that the user was indeed created in the first case and that no user was created in the second case respectively. Consequently, we could be responding with an error when the parameters are invalid or incorrect and creating an invalid or inconsistent new user anyway and our test would never notice it and pass as if everything is okay.
This is why it’s very important to explain and make clear which is the real reason for making tests and to cover all the expected behavior and not only half of it.
HTTP status codes
As we are developing an API we have to watch out for the declarativity of our endpoint responses since it will be all that a client, either a frontend, other API or some other service, has available to know what happened with his request. For this purpose, we have a very good and sometimes badly used tool in the HTTP status codes.
When starting, a lot of trainees think the HTTP status code is simply a random number, but each code has a meaning that should represent the result of the request. By doing so, we can use the HTTP status code to have a quick idea of what happened with some request.
I think it’s very important to explain and ensure that the difference between each class of status codes it’s well learned. I mean, to understand that 2xx is used for successful cases, 4xx for failures produced by some client error and 5xx for failures produced by some server error. Then if you want to explain or discuss which status code to use in each specific case, I personally would find it very interesting, but it’s a nice to have. Specially because it usually gets a little bit tedious and useless since everybody has his own vision of use cases for each particular status code according to each flavor and knowledge about the given case. That said if you want to learn a little bit more about how to choose an HTTP status code we recommend the following article and the following documentation.
Another very common scenario the trainees get stuck with is once they start finishing new features and moving along the training, they want to start trying out the stuff they developed and they don’t know how to do it or they notice some bugs or unexpected behaviors and they can’t find out where it is coming from. Both of this situations can happen either locally in their computers or remotely in some staging environment.
First of all, we strongly recommend them to use Postman, or similar programs, to be able to make HTTP requests to the API in a fast and simple way. By the way, they can store the requests in there to re-use them later pretty easily and quickly.
We also provide them with a logger and we try to inculcate them to always log what is going on in each function. If they do so, then they will be able to check and analyze the server logs for some weird bug or behavior and easily find out where it was originated. This point takes special relevance once the server is deployed in a remote environment like staging or production.
Furthermore, we have developed an interactive Node.js console that loads all the project (something like the rails console) so you can play along with all the stuff already implemented without actually having to make a full request cycle. For example, if you want to try some new user model function, you can directly use the user model in the console and call the function.
And last but not least, we show and encourage the use of debugging tools like chrome devtools, the visual studio code debugger or whatever other option you like the most. By using them you can replicate some bug and with the help of the debugger check all the variable values and what is going one before the bugs happen. If you want to start using some debugging tool I recommend you the following video on how to use chrome devtools.
Here at Wolox we have developed an Express.js bootstrap to be able to make easy and quick project kickoffs including a basic folder structure and some tools and configurations common to the vast majority of our projects.
Additionally, we have a good practice guide on how to modularize, delegate and divide responsibilities between different entities that live in an application like controllers, middlewares, services, models, serializers, helpers, etc. I don’t want to overextend myself on this particular subject because it’s a very rich, interesting and controversial thing to talk and discuss and we could spend a whole article talking about it.
From this point forward, each project takes this building blocks and modifies them according to the needs and conventions defined by the team. But it’s a nice way of making all of our projects to have the same structure and identity.
With each new training we use the bootstrap to start the project, which sometimes is a big step for the trainee because it adds a lot of new information, files and stuff. That’s why we believe this a good moment to sit with your trainee and explain, from top to bottom, how all the moving parts work together, for example how the server starts, how is the life cycle of a request inside the server, how all the different files start to get together and in which order, what a middleware is, etc. By understanding all those points, the trainee will have a lot more knowledge of what is going on under the hood of the server and will be more conscious of how the new code, will be integrated with the rest of the server, which usually implies better solutions and in a happier trainee because he really understands what he is doing.
Once the trainee starts developing new features and advancing through the training they will face some issues regarding modularization and delegation of code, such as putting all the logic in a controller or delegating the handling of the request or the response to some model.
At that moment it’s crucial to explain the importance and advantages of modularizing, delegating and dividing responsibilities correctly (maintainability, reusability, flexibility to changes, readability, etc). I also think it’s very important to encourage the trainee to question himself and analyze how they think it would be right to proceed without forcing them to follow some convention since there isn’t a unique solution when it comes to this type of discussions. Then I usually show them the good practice guide I mentioned above to compare advantages and disadvantages of each option. Hopefully, this will help to reach a reasonable solution that works for the needed functionality.
Summing up, those are some of the most important issues that we found while training people in Node.js and some tips and material to try to mitigate them. Sometimes they work, sometimes they don’t, there is no silver bullet but we surely can be more aware and pay more attention to certain issues to try to avoid some headaches.
If you think that I missed some classic issue or if you have some more tips, material or comments to add about the issues I mentioned above I am more than willing to receive them.