“If you want to be successful, learn from other peoples’ mistakes, don’t learn from success stories.” – Jack Ma, Alibaba
Whether you are a frontend developer or a backend developer or even a full stack developer, you need to understand the best practices and the mistakes that you might have made and are making to hone your skills and become the best in your game.
While you may be open to learning and always learn from the mistakes you make, it’s quite likely that while developing you’re making certain errors that you may not even be aware of. So, to help you refine your backend development and develop like a pro, I am enlisting the top 10 backend development mistakes that most of the developers (including me) have made, their solutions and why they are called ‘mistakes’.
So sit back, relax but DON’T ENJOY because after this blog you have to go and fix these mistakes. Also a small disclaimer there might be some other things that I have missed so take it with a pinch of salt. This is not a complete list, just the ones I’ve found to be the most common, and just like you, I too am still learning. Let’s get started then.
Most common mistakes made by backend developers
Here’s a quick list of the mistakes I am planning to discuss and help fix.
- Reinventing the wheel
- Testing after API’s are ready
- Meaningful test cases
- Not automating Schema migrations
- Not using DRY code
- Dashboard to see query usage
- Correct error format
- Hesitation to make technology stack change
Let’s take a deeper look at each of these.
1. Reinventing the wheel
Being in an innovative field, we’re all busy finding our own ways to do things aka reinventing the wheel. However, that’s your first mistake. In software development, it’s better to not reinvent the wheel. That’s because in the past decade the technology has become so advanced that whatever you are trying to implement, it is highly likely that someone else has already implemented it. So, instead of starting from scratch and doing the entire design, development, testing on your own, try to find what other people are doing and learn from their mistakes. If it is open source then that’s even better.
Open-source projects like Loopback have been tried and tested by many developers for creating the API. If you decide to do everything on your own then it is obvious that you will make mistakes that might break the API or you will take more time to develop and ultimately you lose your time to market opportunity. So, try to reuse as much as you can.
Not just that, as serverless is growing there are a lot of options to deploy your own REST or GraphQL API on the cloud in literally 10 minutes. All you have to do is tell the API’s what you want to develop, the database structure, the region in which you want to deploy, the authentication mechanism, and boom–you have a full-fledged, highly scalable, and highly available API on the cloud ready to be used by the users with just a click.
So try to use such amazing features and avoid getting in the habit of having to make everything from scratch. It may help you learn as a beginner, you must not make it a habit. There’s a lot of hidden costs that you as a developer and your organization have to pay if you keep ‘reinventing the wheel’.
2. Testing after API’s are ready
This was probably the biggest mistake that we personally made while we were developing the GraphQL API on AWS. We did not have the test cases ready before making the API and we wrote the tests after the API was developed and were used by the frontend development team. There is no problem with this approach but sometimes as developers, we write test cases as per the API and not the actual requirements. This makes us feel that the API is working and is tested. However, this doesn’t give you the best output.
If we have test cases pre-written and then we start developing the API to pass these test cases, we don’t compromise on the quality of the output. So, write your test cases based on your business requirements, iterate, freeze, and run them. It’s quite likely that the test case will fail on the first run but then you can use it to start writing the API one by one and testing them against the test cases. This in turn would also give you more confidence in your API.
3. Ignoring meaningful test cases
Having tests is not important, having the right tests is. More often than not, we write test cases that come to our minds and run them for any API. However, writing meaningful test cases is crucial, even if that means taking time to write them. The correct test cases will give you speed as you have the confidence that tests will catch the issue if you break something in the API.
So, you can go as fast as you can and not worry about making changes that might break the API. On the other hand, if you have test cases just for the sake of having tests then you will be afraid to make the breaking changes because deep down you know that even though you have written the test cases they might not catch the bug.
Write meaningful test cases, make sure they are capturing everything and every possible case because once you’ve done that properly, it is just a matter of how fast you can write your code and develop an API to pass these tests without having to worry about breaking your code.
4. Not automating Schema migrations
For the unacquainted, the standard definition of Scheme migrations is that it is “the management of incremental, reversible changes and version control to relational database schemas. A schema migration is performed on a database whenever it is necessary to update or revert that database's schema to some newer or older version.”
Some people might confuse Schema migrations as moving databases from one platform to another. However, the truth is that in Schema migration, we basically set up a Schema with certain rows, tables, and columns. Then, if we want to add more tables then rather than doing it from within the database, we do it from a Schema migration tool like Alembic. It is also helpful if you want to quickly migrate to older or newer versions of your database with just one command. Given that it’s a faster option, not considering it is clearly a mistake we all can avoid.
5. Going for Random Access Control
There’s no denying that authentication and authorization are probably the most important parts of any backend development. Authentication means verifying that the person is really who he says he is, and authorization means ensuring that the person has the access he requires. Instead of going on a random spree of access control based on who needs what kind of access when working on the project and releasing access on-demand, the better way is to go with Role-Based Access Control (RBAC).
RBAC means offering authorization mainly based on roles like an employee, admin, approver, and so on. So checking if the person who is accessing the API is authorized to access a certain GET, PUT, POST, or DELETE operation becomes easier based on the role. This is crucial because one wrong check can unleash havoc.
It is important to cover all edge cases like whether the admin who is updating organization details is the admin of the same organization or not and also an employee who is joining the organization is the same employee that you invited and so on. If someone gets access to information that they should not have then it would be a big problem for the organization and for you as a developer because the organization doesn’t trust you any more to secure the API. So, better not to gamble there and simply control access based on roles.
6. Not using the DRY principle
DRY (Don’t Repeat Yourself) and KISS (Keep It Simple Stupid) are two terms you may have often come across in different walks of life. So, how can we apply them in backend development?
Whenever we come across a certain piece of code that is repeated twice, we are often comfortable with that - instead of making it a reusable function. It is only months down the line that we’d realize that it has been repeated over 20 times. An example would be checking if a proper JWT token is present in the header or not. Instead of checking it at an API level i.e. inside the GET, PUT, POST, and DELETE handlers, we should do it separately, and if a valid JWT is present then only send the request to the API. The advantage is that all our APIs won’t get unnecessary requests and the code for checking valid Authorization header is kept separate and is reused.
So, all think of instances where the code you are writing can be reused in the future. It could be small tasks like header validations, database connections, and so on. So try to reuse it and create a function that would do this specific task. Another advantage is that if you want to make any changes then you will have to do it in a single place only.
Over-optimization or premature optimization can lead to a lot of problems and must be avoided. Optimization of your API is good for performance or scalability but if you start optimizing all of your APIs then it will take a lot of time to change the logic that is currently implemented, which will lead to a longer time to market or longer customer feedback loop.
Rather than optimizing all of your APIs to give a better performance, you should only optimize those that are being used the most by your end-users. Follow the 80-20 principle i.e. focus most on the 20% of your code that impacts 80% of your customer experience. In case you are wondering how to determine which APIs are being used the most or which APIs are slow and have to be optimized, hear out the next mistake I am going to cover.
8. Ignoring the need for a dashboard
Dashboards are an important part of any software, whether it be an admin dashboard or a dashboard to track all your customers or a dashboard to see query usage patterns they are important to have. AWS provides Cloudwatch where you can see all of your API usages, which query is taking how much time, which queries are failing most of the time, which queries have higher traffic as compared to others, and so on and you can also tweak the fields as per your needs. Not having a dashboard will lead to a situation where you have no idea what is happening to your backend. You can look at the dashboard and then optimize the queries as per your requirements and avoid over or premature optimization.
9. Overlooking error formats
Have you ever landed in a situation where you as a frontend developer are sure that you are making the right request but somehow you are getting a 404 or a 401 or a 403 from the server? I don’t know about you but I have landed in such situations a lot of times and it gets frustrating to debug what exactly is wrong with my request.
So, when I started to develop backends I made sure that the error formats are correct i.e. the error code, what exactly went wrong, and a small description of the error if possible. The advantage is that the people who are using your API won’t call you again and again and ask for an explanation for the problem. Whatever the error might be it has to be self-explanatory so that even a beginner and a professional developer both can understand it and then take the necessary steps to resolve the issue.
10. Hesitation to make technology stack change
Let me give you a real-life example, we at Udgama were developing our backend on AWS with AppSync as our GraphQL endpoint and Aurora for database but the performance was not as expected so after some months we found that Hasura with Aurora gives way better performance so we decided to switch to Hasura. It was a breaking change but the speed of the API is way faster, the response formats and the error formats are a lot better and the extra efforts that we spent in switching were worth it.
Summing it up!
“Mistakes have the power to turn you into something better than you were before”
We all make mistakes knowingly or unknowingly but what matters the most is we learn from them and make sure we don’t make the same mistakes again. As developers, you might have made one or more mistakes from those listed above when it comes to backend development but I hope that this article helps you in avoiding these mistakes going ahead.