When and Why Your Code Starts to Smell Bad – Tufano et al. 2015
Yesterday we saw that maintaining project quality is a key issue for integrators (maintainers). So it seems appropriate that my third choice from the recent ICSE ’15 conference papers examines the question of when quality starts to slip at the code level, and what causes it.
Bad code smells (shortly “code smells” or “smells”), i.e., symptoms of poor design and implementation choices, represent one important factor contributing to technical debt, and possibly affecting the maintainability of a software system… to the best of our knowledge, there is no comprehensive empirical investigation into when and why code smells are introduced in software projects. Common wisdom suggests that urgent maintenance activities and pressure to deliver features while prioritizing time-to-market over code quality are often the causes of such smells. Generally speaking, software evolution has always been considered as one of the reasons behind “software ageing” or “increasing complexity.”
Tufano et al. studied 200 open source projects from the Apache, Eclipse, and Android ecosystems to understand when and why smells were introduced. Over half a million commits were analysed. The study was based on a mix of smells related to large and complex components as well as smells related to lack of adoption of good practices, so as to be representative of the different categories of smells.
We focus our study on the following types of smells: 1) Blob Class: a large class with different responsibilities that monopolizes most of the system’s processing; 2) Class Data Should be Private: a class exposing its attributes, violating the information hiding principle; 3) Complex Class: a class having a high cyclomatic complexity; 4) Functional Decomposition: a class where inheritance and polymorphism are poorly used, declaring many private fields and implementing few methods; 5) Spaghetti Code: a class without structure that declares long methods without parameters.
I’m going to skip over the methodology and jump straight to the findings – see the full paper for more details on how the analysis was conducted.
When are code smells introduced?
The surprising finding in the light of the software ageing theory is that most of the smell instances are introduced when a code entity is first added to the versioning system. When a smell does appear at a later point, “its symptoms (metric value increases) occur very fast, and not gradually.” In the case of Blobs for example:
For the overall dataset, the slope for classes that will become Blobs is 849.90 as compared to the 0.25 of clean classes. Thus, while the cohesion of classes generally decreases over time, classes destined to become Blobs exhibit cohesion metric loss orders of magnitude faster than clean classes. In general, the results in Table V show strong differences in the metrics’ slope between clean and smelly files, indicating that it could be possible to create recommenders warning developers when the changes performed on a specific code component show a dangerous trend that could lead to the introduction of a bad smell.
Why are code smells introduced?
Among the three different ecosystems analyzed, results show that smell instances are mainly introduced when developers perform enhancement operations on the system. When considering the three ecosystems altogether, for all the considered types of smells the percentage of smell-introducing commits tagged as enhancement ranges between 60% and 66%. Note that by enhancement we mean changes applied by developers on existing features aimed at improving them.
If you consider both enhancements and new features the percentage rises to over 80%. Another endorsement for the theory that most smells are introduced on day one.
Bug-fixes do of course introduce smells as well (between 6-16% of smells are introduced during bug fixing). Finally, refactoring – which is supposed to clean up the code and reduce smells, is also a source of smell introduction!
While refactoring is the principal treatment to remove smells, we found 394 cases in which developers introduced new smells when performing refactoring operations.
What I can’t easily see in the figures is a comparison to baseline activity. For example, 6-16% of smells may be introduced during bug fixing, but does that make bug fixing more or less smelly than other activities? We don’t know unless we also know what overall % of activity is devoted to bug fixing…
More smells are also introduced in the run-up to a release (but again, we don’t know if more work overall is also done in the run-up to a release – often yes in my experience):
As expected, most of the smells are introduced the last month before issuing a release. Indeed, the percentage of smells introduced more than one month prior to issuing a release is really low (ranging between 0% and 11%). This consideration holds for all the ecosystems and for all the bad smells analyzed, thus confirming the common wisdom that the deadline pressure on developers can be one of the main causes for smell introduction.
And those smells are often introduced by the files’ owners, not by newcomers:
We can also observe that generally the developers who introduce a smell are not newcomers while often they are owners of the files. At the first glance, this could look like an unexpected result. The owner of the file—one of the most experienced developers of the file—is the one that has the higher likelihood of introducing a smell. However, as also discussed by Zeller in his book Why programs fail, more experienced developers tend to perform more complex and critical tasks. Thus, it is likely that their commits are more prone to introducing design problems.
(It’s also the case that the owner of the file is just more likely to do more work in the file, and hence have more chance of introducing smells. Plus, we’ve already been told that many smells are introduced when an entity is first created, and by definition that is by the owner of the file).