Developers, are you chasing timelines?
How often have you heard the following:
- If I write more tests, it will impact the timelines.
- If I refactor the code, it will impact the timelines.
- I have checked the code, it will work as expected. Tests are not required.
- It’s fine to not write more tests, the code looks fine and will not break in production.
- I have tested happy flow in unit tests and it is working fine.
- The tests were not written before, so why should I write now? Someone else should do it.
- P1 cases have been tested and that’s sufficient.
- It’s fine since there are just 2 if conditions.
- This condition will never occur so no need to write tests for that.
- The conditions are already covered in tests. Why should I write more tests?
- … and many more
While working on projects and tight timelines, more often than not these thoughts come into our mind and we try to negotiate to meet the timelines. The negotiation more often than not moves towards compromising on tests and code design which instinctively seem easier to compromise. This works when we are working towards preparing for a demo/sales pitch but doesn’t work if we are building production-ready code.
Impact of cutting corners on code design and tests
- Code quality keeps deteriorating and becomes unmanageable and unexplainable.
- Multiple QA cycles are required leading to more time taken in signing off features and feature release.
- Regression suite coverage might be limited to P1/P2 cases and may not cover breadth and depth possible in functional and unit tests.
- Defect leakages on production which can cause bugs and incidents causing customer disservice or even financial loss for the issuer or customers.
Why quality of code and tests are important?
- Maintainable and self-explainable code is easily referenceable.
- Humans are creatures of habit. If you follow standard design patterns and practices, it is easier to reach the code in which you are interested and want to change. It’s like organizing items in your home. Organizing items(classes) in the right place allows for faster access.
- Good test cases make it easier for developers to not only assert and make new changes safely but also leverage it to understand code flow and be more confident in making changes.
My Past Experience
When I joined Zeta in 2016 as a fresher, I was not fully sure of how I would work and make good contributions. I had done competitive programming in college and was aware that not handling edge cases could impact a solution. As I started my journey, the first thing I was encouraged to do was to take time and write tests. The focus on tests developed a good habit of writing comprehensive tests and covering different cases. This really helped me in gaining confidence and avoiding obvious mistakes. As I started contributing to multiple modules, I started getting feedback that the tests written in previous modules were helpful. This gave me more confidence and made me further realize the importance of writing tests.
What should you aim for?
- > 90% code and 100% condition coverage in tests.
- The tests should be functional in nature testing the modules functionality end to end.
- Go beyond the sonar coverage report. Use domain knowledge to capture more scenarios that may be covering already covered lines of code but not behave as expected.
- Reduce manual integration testing and move towards automation. CCE and SCE adoption are the next steps which are being driven by Dev World team.
- Take extra precautions and cover more ground in tests when dealing with calculations as they are more prone to mistakes.
- Write self-explanatory and extensible code using SOLID principles and design patterns.
- Foresee future requirements based on domain and factor into the design before jumping to code.
- Look for elegant design and solutions.
- Raise the bar in code reviews. Always explore how the code can be improved. Never shy away from giving comments and having a debate on code.
Why you should aim for above?
- We are humans and we will make mistakes. Writing good quality code and tests helps us avoid issues as much as possible. People who are familiar with competitive programming know how small edge cases can make a significant impact in their rankings in contests when their submissions fail.
- Reduces regression chances if there is any code change in the existing path.
- Measurement of coverage and other code quality metrics gives confidence in the delivered features and modules containing those features.
How to manage timelines without compromising quality and tests?
- Negotiate on the scope of features. Deliver less but properly working features. Take the help of leadership in doing so.
- Avoid complexity in feature design. An example of this is to keep the number of interactions between different microservices to be minimum.
- Avoid complexity in calculations. Keep the calculations and the involved variables to be minimal. Make sure conditions that are not explicitly visible in calculations are asserted with comprehensive test cases.
Author: Shubham Jha