The Interface With One Implementation
Hollow inside, inflated outside. Every abstraction you built for a second caller that never arrived.
आहांच वाहांच आंत वरी दोन्ही · You Hollow-and-Inflated Ones Cannot Walk With Us
आहांच वाहांच आंत वरी दोन्ही । न लगा गडणी आह्मां तैशा ॥ १ ॥
भेऊं नये तेथें भेडसावूं कोणा । आवरूचन मना बंद द्यावा ॥ २ ॥
तुका ह्मणे कांहीं अभ्यासावांचुनी । नव्हे हे करणी भलतीची ॥ ३ ॥
Hollow within and puffed up without, both at once: such companions are no use to us. Where there is nothing to fear, do not go about frightening people; put a bridle on the mind instead. Tuka says: without practice, this is not a thing just anyone can do.
The file I could not delete
I went to delete a PaymentProviderStrategy interface last month. Four methods, one implementation, written by me, three years old. I had built it the week we integrated Stripe, because obviously we would add a second provider, and when that day came the swap would be a config change instead of a rewrite.
The second provider never came. What came instead was a factory to select between the one strategy, a config key that could hold exactly one value, a test double that existed only because the interface made the real thing awkward to construct, and four years of every engineer who touched checkout following an indirection to its single destination. I could not delete the interface in an afternoon. It had grown a small ecosystem of things that existed to serve it, and none of them served the product.
I had not saved us a rewrite. I had prepaid for one and never collected.
The name for this
This is a catalogued smell, and the name is exact: speculative generality. An abstraction created for speculated requirements. The canonical tell is the interface with a single implementation, the parameter every caller passes the same value for, the hook nobody registers against, the base class with one child.
The cost is not the extra file. It is what the industry has settled on calling the cognitive tax. Indirection is a primary generator of extraneous cognitive load: the reader has to navigate the file structure, trace the execution path through the layers, and mentally reconstruct control flow that has been scattered across classes. Every abstraction charges this tax on every future reader. Genuine reuse pays it back. Speculative reuse never does, and the tax compounds daily against a benefit that arrives never.
Worse is the second-order failure, which is the one that actually costs money. When the real requirement finally arrives, it almost never has the shape you guessed. Now you are not solving the new problem from a clean base. You are forcing it through a generalization built for a different imagined future, and that is usually harder than if you had written the simple thing and refactored honestly when the second caller showed up. The abstraction you built to make change cheap is the thing making change expensive.
Premature optimization at least leaves fast code behind. Premature abstraction leaves scaffolding around a building nobody constructed.
Where Tuka comes in
आहांच वाहांच आंत वरी दोन्ही is the line, and the compression in it is almost unfair. Hollow within, inflated without, both at once. Not a thing that is empty. A thing that is empty and presenting as substantial, where the presentation is what keeps you from noticing the emptiness.
That is the interface with one implementation, described in four words four hundred years before anyone wrote one. वरी (outside) it is architecture: the vocabulary of extensibility, the shape of a system designed for growth, the file that looks like senior work in review. आंत (inside) there is one implementation and there was only ever going to be one. The inflation is not decoration on top of the hollowness. It is the mechanism by which the hollowness survives review, because nobody challenges a Strategy suffix.
Then Tuka names the mechanism directly: भेऊं नये तेथें भेडसावूं कोणा (where there is nothing to fear, do not go about frightening people). I built that interface out of a fear I manufactured. There was no second payment provider on any roadmap. There was no requirement, no customer, no date. I invented a threat, then built defenses against it, then presented the defenses as foresight. आवरूचन मना बंद द्यावा (put a bridle on the mind instead) is the actual correction, and it is an internal one. The discipline is not better patterns. It is refusing to act on a danger you made up.
And the closing line is the part that keeps me honest: कांहीं अभ्यासावांचुनी । नव्हे हे करणी भलतीची (without practice, this is not something just anyone can do). Restraint reads as the easy option, the thing you get by doing less. It is not. Knowing the Strategy pattern is a week's study. Knowing that this Tuesday, in this codebase, with this roadmap, you should write the concrete class and nothing else, is the thing that takes years, and there is no pattern catalogue for it. The patterns are the vocabulary. Knowing when the answer is no pattern is अभ्यास.
What I do now
The rule I actually follow is the rule of three, and I follow it literally rather than as a proverb. One caller: write the concrete thing. Two callers: duplicate it and feel bad. Three callers: now abstract, because now you can see the shape of the variation instead of guessing at it. The third caller is not a threshold for effort. It is the point at which you have enough evidence to know what the abstraction should look like.
In review I ask one question of any new interface, base class, or plugin point: name the second caller. Not a hypothetical one. A real one, in this repository or on this quarter's roadmap, with a name. If the answer is a shape of a future rather than a thing that exists, the pattern comes out and the concrete implementation goes in. Nobody has ever been unable to add the interface later. That refactor is mechanical, and your editor does most of it.
The part that took me longest to accept is that deleting a speculative abstraction is harder than never writing it, by a wide margin, because by then it has dependents. The interface is cheap on the day you write it and expensive on every day after. That asymmetry is the whole argument.
I have never once regretted writing the simple version first. I have a PaymentProviderStrategy I still have not finished deleting.
Sources
- Fowler, M., Speculative Generality, catalogued code smell.
- Sharma, T., Speculative Generality: A Taxonomy of Software Smells.
- Frontend at Scale, Too General Too Soon.
- Wireframe, The Asymmetry of Technical Debt: Premature Abstraction vs Premature Optimization.
- AI Infra Link, How Early Custom Abstractions Hurt Platform Teams.
- The abhanga is Tukaram, आहांच वाहांच आंत वरी दोन्ही, from the Tukaram Gatha.
Chetan Dhandal