Lava Flow.
Lava flow is an antipattern in which obscure, half-finished code hardens in production because nobody understands it well enough to remove it.
In computer programming jargon, lava flow is an anti-pattern that occurs when computer source code written under sub-optimal conditions is deployed into a production environment and subsequently expanded upon while still in a developmental state. The term derives from the natural occurrence of lava which, once cooled, solidifies into rock that is difficult to remove. Similarly, such code becomes difficult to refactor or replace due to dependencies that arise over time, necessitating the maintenance of backward compatibility with the original, incomplete design.
Causes
Lava flow can occur due to a variety of reasons within a software development process:
- Pressure to meet deadlines leading to temporary solutions becoming permanent
- Inadequate documentation which prevents understanding of the code’s purpose
- Lack of automated tests which makes refactoring risky
- Frequent changes in the development team leading to loss of knowledge
Consequences
Unrefined code that becomes part of the software’s infrastructure increases the complexity of the system and the codebase becomes increasingly difficult to understand and maintain. It leads to:
- The need for backward compatibility which can stifle innovation and prevent adoption of newer, more efficient solutions
- Increased technical debt that accumulates over time, resulting in higher costs of change and maintenance
- Obstacles to refactoring or improving the system due to fear of breaking dependent components
Impact on teams
Development teams often experience the impact of lava flow when team members cycle in and out:
- Loss of knowledge about aspects of the system's code when original developers leave
- Reluctance among new developers to refactor unfamiliar code, leading to further complexity as they add rather than clean up
Mitigation strategies
Several practices can mitigate the effects of the lava flow anti-pattern:
- Promoting good documentation practices for clear understanding of code
- Encouraging regular code reviews to catch suboptimal practices early
- Prioritizing refactoring as an integral part of the development lifecycle
- Maintaining a comprehensive suite of automated tests to reduce risk in changes
Example
The legacy branch below survives every cleanup: there is no test, ticket or documentation for it, only a warning comment, so nobody knows what it is for and nobody dares to delete it.
class Pricing {
// nobody remembers who sets this flag; do not remove, something may break
private static final boolean LEGACY_MODE = Boolean.getBoolean("pricing.legacy");
double price(Order order) {
double total = order.total() * 1.1;
if (LEGACY_MODE && order.items() > 10) {
total = legacyAdjust(total); // ???
}
return total;
}
private double legacyAdjust(double total) {
return Math.floor(total * 0.97);
}
}
The way out is to record the current behavior with characterization tests, confirm the branch is unused, and then delete it:
// 1. Characterization tests pin down today’s behavior before anything is touched
@Test
void addsTenPercentMarkup() {
assertEquals(110.0, new Pricing().price(new Order(100.0, 1)), 1e-9);
}
// 2. A code search and the production config show that nobody sets pricing.legacy,
// so the branch is deleted; the tests confirm that nothing else changed
class Pricing {
double price(Order order) {
return order.total() * 1.1;
}
}
Unreachable code is just dead code, and version control keeps it anyway, so there is no reason to keep it “just in case”.
Adapted from the Wikipedia article “Lava flow (programming)” (revision 1309138267), available under the CC BY-SA 4.0 license; the text has been edited and examples added.