Nowadays the amount of code, which is owned by IT companies grows exponentially. Even companies without focus on IT will ask software vendors, agencies and IT-consultants to cover their special requirements. In this dynamic environment with a significant number of different demands, it is so easy to fall into the “bad code” trap. Just like junk food, it makes you happy for the moment and then hits you with missed deadlines, financial losses, and most importantly, decreasing trust of your partners.
In fact, “bad code” will hurt you in every phase of your project, starting from the very first minute. New developers will experience problems during on-boarding; a team might not be able to deliver a business-critical feature in time; and once a company suffers from unbearable technical debt, all departments can be paralyzed.
“It’s not enough for code to work” – Robert C. Martin
What Is “Bad Code”?
In this complex definition our starting point could be Software quality, which entails that at the end of the day we need to cater to users’ needs and make them happy. Code quality comes very close to the definition for software, but we need to add all engineers participating in the project’s development to the user group, as at this point, they are users too.
To recap: high quality code produces happy end users and engineers. The results contribute positively to a wider business scope and team resource management: ease of getting projects started and drive progress, shorter feature-to-market time, error prevention, less time for code review, natural dissemination of knowledge and even the ability to work in parallel in smaller teams, meaning there are no bottlenecks of minefield “experts”.
As opposed to high quality code, what are the impacts through poor quality code? Long feature development follows a business logic that is difficult to grasp. A system that is error-prone, is likely to create big technical debt. Over time, bad code smells a mile off. Each senior software engineer can tell you exactly when a pull request contains low quality code.
Usually it looks like this:
But “bad code” does not always start off like that. Usually it looks good in the very beginning and over time its quality drops dramatically, somewhere in between of shiny SOLID releases.
Our job here is to follow the black line of the picture.
In Search of the Silver Bullet
Spoiler alert: there is none, but here is a good alternative: your process.
Here is a collection of practices we use at Spryker to define, measure and further grow code quality over time. You probably won’t like all of them and some of the measures will be too complex or time consuming for your liking. But one thing you need to do is to define the border you will never cross. Once you set it, the process and tooling with catch up.
Testing, testing, testing
Companies are divided into two types: the ones that don’t do testing and the ones that do, often following disruptive failure caused by a lack of testing. No matter if you apply agile development or waterfall methodology, there should always be time for writing simple test cases. Unsure how to design a proper testing flow? In the Ministry of Software Testing, you can read more about how to establish a proper software testing process.
Be a professional – grow professionals
In our company we’ve made a cult of SOLID principles and “Clean code”. Most of our decisions are based on them. If you are still not familiar with these two or common terms like KISS, DRY, YAGNI, hitch up and learn more about them. There is always a place for discussing the “when” and “how far”, but in general they will give you a good foundation.
Consider code review not only as a possibility to improve your code base, but as a part of a knowledge sharing and learning process. Usually we forget about last two. If code review is done by only the most experienced team members, you miss out on a great opportunity to find and learn from fresh, interesting ideas.
Also, use code review tools to get rid of the most popular mistakes, which can blur your focus. You should not go through the same issues over and over again. Why not update your code-sniffer/architecture-sniffer (static code analysis) with a new rule? This can save time for team building and grow healthy habits.
Get hold of your dependencies
Firstly, you will need to define your dependencies. In our case it’s simple at first glance, each module is subject to dependencies, which is already complex. It becomes even more complex being a software vendor and committing to delivering a clean modular architecture with more than 300 modules till this point of time. In your case the dependency subject could be different: a service or a micro-service, a module or a package, an application or a domain. Clear definitions of dependency subjects will allow you to define relations between them.
Once you defined your dependency subjects and relations, keep them lean and clean. The fewer dependencies you have and the less your subjects are coupled, the fewer cascading issues you will need to solve.
To dig a bit deeper, I recommend you read up on the following:
Software package metrics: Afferent coupling, Efferent coupling, Abstractness and Instability. You can find more about this in “Agile Software Development: Principles, Patterns and Practices.” by Robert C. Martin.
Then continue with terms like “Coupling” (Software Engineering — Guide to the Software Engineering Body of Knowledge) and Dependency Hell (especially when applying the microservice architecture style).
Measuring helps your code quality
Measure from the very beginning. There are plenty of tools which will help you, including scrutinizer, codacy, blackfire, travis-ci or any CI, and for php: php mess detector, php stan, php code sniffer and even more IDE plugins. You won’t need all of them at once. Choose what suits your requirements best. Set up a threshold and make it as a part of your process. At Spryker, we have 9.62 points on scrutinizer across all our ~300 modules and we agreed to stick to that number for all feature releases and make it higher on cleanups and major releases.
It’s all about teamwork
We already know how important it is to understand coding principles on a personal level, but the game changes when it comes to communication and collaboration. Everybody usually has a reasonable opinion and wants to make their project better. So that’s why we have a solid process to suggest any change: define an issue in an RFC document, propose a solution, by drawing a schema, preparing a “proof of concept”, or writing code examples, and present your point in front of the team. The denied or accepted proposition is then being used right away.
When a suggestion change process is not defined, you lose many opportunities to fix obvious issues and base decisions on real propositions rather than opinions.
Let your code talk
Along with team culture comes code style. For an engineering team, it’s important to find a common code style and write it down. It will save you much time for discussion just by referencing to this document. A big change should follow the RFC process.
Not everything should be defined. The temptation is to put everything down in a shared team document, such as a Wiki page. In practice, however, the bigger a document, the faster it becomes outdated. And there always will be a topic in between concepts, which needs to be discussed. Use your clean and up-to-date code-base to show examples and to on-board new developers. Your code should talk better than your documentation.