How code dies
Code is born, grows, matures, decays and then dies. It may be seen as a living matter, just like the tulips in my garden. It is scary to transpose that image to code: little typo appearing, pieces of code disappearing, constants that changes their value over time (code inflation?), part of code that grow without sense. That way, it would be obvious that code has an expiration date.
Since no one is using FTP for development anymore, and is using a serious VCS, such hallucinations tend stay what they are: hallucinations. Code doesn’t change by itself, it stays just like it was written. Yet, it is going to reach its expiration date and suddenly release a bunch of bugs. How can something change behavior while not changing by itself?
Since the code doesn’t change by itself, it means that its context is changing. Everything that code relies upon is changing, day after day, little by little. “The Only Thing That Is Constant Is Change” says the old Greek philosopher Heraclitus. Think a moment about the number of dependencies that makes your precious code possible:
- PHP itself
- Database, every other piece of architecture and their layout
- Hardware on which it runs
Each of them will require attention as they evolve. And they have impact on the code: it may be adapting, fixing, changing, tweaking or plain rewriting in your own code to keep up with their evolution. This is at that point that code dies, or, simply, spoil and rot.
The new introduction may replace, partially or fully, some previous code. This previous code is usually dubbed ‘old’. Old code tends to say place until it is trimmed, or refactored, in fear that something else is depending on it. The rationale here is that no one notices useless code, while missing code will die spectacularly and, in a word, painfully.
Let’s review the various code expiration cases:
- Constant: they are defined with define() or const, but they are never called.
- Variable: they are written at some point in the code, but they are never read later. The archetype is the used-once-variable, but it also applies to only written variables. Note that read-only variables will yield an error: they don’t die peacefully.
- Function: they are defined, but they are never called. This also applies to closures. In practice, functions are defined with a range of other functions, in a special library file, and included as a whole. It is time to clean that file.
- Classes: they are defined, but they are never instantiated (with new), statically called (for constants, properties or methods) or extended (with extends). Type hinting and instanceof usage won’t keep a class alive, since those instructions only check the existence: in fact, they would rather be a sign of more dead code.
- Exceptions: special case of classes for exceptions: they are never thrown or extended.
- Interfaces: they are defined, but they are never used in an instanceof or typehint instruction. This is almost the contrary to Classes.
- Trait: dead traits are never use’d or extended. “Use’d” means here, combined with the ‘use’ keyword, inside a class.
- Files: not included (directly or via autoload), and not directly called (index.php). When using the one-class-one-file convention with Outlaid, the death of the file is now linked to the death of the class: see above.
The last piece that may be dead is code snippet: an unnamed group of instructions that makes no more sense. Code may be unreachable, after a break, a return or a die, and unless it is a structure definition (class, function, etc) or a label. This also applies to all conditions that is based on the above code: if ($x instanceof \deadClass).
Finding expired code is a simple but laborious task, since it means searching for definitions for everything in the code, and failing. The other lengthy part is trying to figure out if the feature is still useful.