Conflicting Quality Incentives
Better to bend in the wind than to break.
- Chinese proverb
I like the ideal of software developers as a master craftspeople, spending their careers working to create ever simpler and more elegant solutions.
That is to say, I have sometimes found myself very frustrated by the real world. I didn’t get it; I encountered entire teams of smart & capable developers that didn’t seem to care about variable name consistency, or want to spend the time discussing exactly how tightly coupled modules could get before they needed to be refactored. Something was wrong, and it took a while for it to finally click what the problem was.
There are two questions. I was asking “Is this code good”, what I was missing was “Is this code good enough”. To effectively answer that question, it helps to break “quality” into two buckets, structural quality and architectural quality.
Structural quality is to my mind non-negotiable; if it’s missing I assert that the codebase is objectively bad. Structural quality is comprised of things like is the code using the appropriate idioms of the language and do the variable names conform to the problem domain.
Architectural quality is a little softer. This bucket is for things like what design patterns were used and how well do they fit the problem, what testing strategies are in place and how effective are they, and how does the structure of the application affect how easy debugging is. This is the area where “good enough” comes into play the most.
In a healthy organization, the three primary drivers of architectural quality for a project are
- Purpose of Code
- Use Case Evolution
- Business Revenue Model
Purpose of Code
What the code is doing matters. The quality standards of software that runs an imbedded medical device or controls heavy machineary are astronomically higher than the standards for something like Goat Attack. That doesn’t mean a novelty site should skip tests, but when making the business decision on where to invest resources, the margin of acceptable error has to be considered. In a perfect world, there would always be time to write perfect, bug free code. In the real world, you have to consider what the risk tolerance is and what harm potential bugs might cause.
Use Case Evolution
The expected evolution of a project has a large impact on the incentives to add or skimp on quality. If you’re building a SaaS product for example, a sane expectation is that new features will be added and existing features will change over time. It makes sense then to invest the time and effort to add structures and concepts to the codebase that increase modularity and enforce encapsulation, since that investment is directly supporting future work.
If you’re building a one off ETL tool to migrate data to a new database, the same invesment in modularity is much less likely to be an investment in the future through additional quality and much more likely to be wasted effort.
Business Revenue Model
How a codebase creates revenue has the most impact on how an organization views the value of quality code. In a SaaS environment, quality is much more likely to be valued, since quality enables the evolution of the product, and an improving product drives subscription or licence revenue. In an agency model, there’s a perverse incentive invest less in quality. Since revenue is generated by the time spent creating the code, if it comes down to shipping features or improving the design, the features win almost every time.
I still believe that code quality matters, and that there is a level of baseline quality required to be a professional developer. My revelation is that I was creating unnecessary stress trying to throw every Code Complete checklist at every problem regardless of context. Having a sense for what’s negitiable and what’s not when it comes to quality is an important skill to stay balanced in this industry.