7. Adhyasa

There is a function in your codebase that everyone changes and no one trusts. It began honestly. Two callers shared a dozen lines, so someone lifted the lines into a function and gave them a name. A third caller almost fit, and got a boolean. A fourth needed a small variation, and got another flag, and a branch inside the branch. It takes six parameters now, three of them set by exactly one caller each. You have to change it again this afternoon, and you already know you will not simplify it. You will add a seventh parameter.

We call this technical debt, or a bad abstraction, and we mean that it is messy and needs a refactor. That framing is the problem: the mess is not the defect.

Shankara opens his commentary on the Brahma Sutras not with the first verse but with a preamble about a mistake so ordinary we have stopped noticing we make it. He calls it adhyasa, superimposition. Two things that cannot be one, the knower and the known, get taken for each other. The attributes of one are laid onto the other, and we spend our days treating the body as the self and its fortunes as our own, coupling what is real with what is not.

His definition is careful. Superimposition is “the apparent presentation, in the form of remembrance, to consciousness of something previously observed, in some other thing.” A form seen before, elsewhere, shows up here, laid over what is actually in front of you. The load-bearing word is remembrance. The thing you superimpose is remembered, not perceived. You are seeing something that is not here, recalled from somewhere it once was.

The stock image is a coil of rope in dim light, taken for a snake. You recoil, your pulse jumps; the fear is real and immediate. Then someone brings a lamp, and there is no snake, there was never a snake, and nothing to kill or back away from. Shankara says it plainly: “the mere hearing of the true nature of the rope dispels the fear caused by the imagined snake.” He gives the mistake a name, avidya, not-knowing, and gives its cure a name too, vidya, which he defines as knowledge of the thing underneath, reached by discriminating away what was laid on top. See the rope, and the snake was never there to fix.

That is a sharper diagnosis than the one we usually reach for. A bad abstraction, we say, is badly built: it grew messy, it collected cases, it needs cleaning up. Adhyasa says something else. The wrong abstraction is not badly built; it was never in the domain at all. It is a shape you laid over the problem, and the problem never had that shape.

The difference matters because it changes the cure. If the abstraction is merely messy, you tidy it: extract a little more, rename, add a test, straighten the branches. You refine the snake. Every refinement makes it a more convincing snake and leaves you exactly as far from the rope as you started. A superimposition is not something you repair by improving it; you take it out.

A wrong abstraction has three properties, and once you have the word for them you start seeing them everywhere.

The first property is that it is unreal and expensive at once. The abstraction was never in the domain, yet everything it produces is real: the bugs that are only the model disagreeing with the world, the workarounds built to route past it, and the fear. The function no one will touch is the snake on the path: imagined, and enough to keep the whole team walking around it. That is why “we should just refactor it” never happens on its own. You are not avoiding messy code. You are avoiding a snake.

The second property is harder, because it hides. From inside a wrong abstraction you cannot tell that it is wrong, because from inside there is no rope to see, only the next parameter to add. The code argues for its own reality just by existing. Sandi Metz put it exactly: “Existing code exerts a powerful influence. Its very presence argues that it is both correct and necessary.” It compiles, it has a name, it has tests, it feels like the model. You can sometimes smell it from the signature, the way you can smell the six-parameter function, but a smell is not proof, and in its own terms the abstraction always looks defensible. What settles it comes from outside: a new requirement it cannot hold, or a bug it cannot explain, a lamp carried in.

The third property is the one Shankara names and we tend to forget. A wrong abstraction is a memory. The superimposed form is carried in from somewhere it fit; you do not invent it, you import it. You bring the shape of the last system you built, the framework’s assumptions about how the world is arranged, the pattern from a conference talk, the architecture some big company published, the model that was right on a different domain. Microservices laid over one team and one database; event sourcing where a single table would have done. You saw it work, you brought it, and you laid it over a problem that did not have that shape.

Which means the danger grows with experience. The more you have seen, the more shapes you carry ready to lay down. Senior engineers superimpose more, not less.

It helps to say what a wrong abstraction is not. An object-relational mapper lays an object graph over tables and rows, and under real data the relational substrate shows through: touch a hundred rows in a loop and each one fires its own hidden query, the N+1 problem, and the page that was quick in development falls over. That is annoying, and it is fixable with an eager load or a join, because the mapper is a faithful map of something that is really there. The rope keeps reminding you it is a rope. A wrong abstraction is the other case. No eager load reaches it, because there is no substrate under it to show through. The thing it modeled was never in the domain.

A wrong abstraction is a snake seen on a rope: convincing, consequential, and not there. You do not fix it by drawing a better snake; you remove it and look at the rope.

The cure follows from the diagnosis, and it runs backward from instinct. You do not push forward into a better abstraction. You take the wrong one apart. Metz’s prescription is the whole move: when the abstraction is wrong, “the fastest way forward is back.” Inline the shared code back into every caller, keep only the lines each caller actually runs, let the duplication you were proud of removing return, and then read what is there. With the code sitting in front of each caller again, you can see which parts were truly the same and which were different things that had been wearing one name. The real seams show. From there a better abstraction can be drawn, or you may find the honest answer is to leave some duplication alone. This is negation as a design method: not this, not this, until what is actually shared is the only thing left. It is the same move as debugging by elimination, one layer up. There it finds the fault; here it dissolves the false unity.

Then the limits, because a principle that finds a snake everywhere is a snake of its own.

Not every abstraction is a superimposition, and saying otherwise is a different kind of lie. You still see the rope as a rope, which is itself a concept, and a correct one. A faithful abstraction is an accurate name for what the domain actually shares, and most non-trivial software would be unwritable without a stack of them. The claim is narrow and has to stay that way: it is about the wrong abstraction, the shape the domain never had, and not about abstraction itself. Metz wrote “prefer duplication over the wrong abstraction,” which is a smaller and more careful thing than “prefer duplication.” An engineer who blurs the two has only drawn a different snake.

What you get back when you inline is the substrate, the actual cases and the actual data, and more than one honest abstraction may be drawable from it. Sometimes the right answer really is to keep the duplication. There is no final, correct abstraction waiting underneath the wrong one. There is the ground, and better or worse ways to name it.

And the cure has a cost, and it is not always available to you. Inlining a load-bearing abstraction that half the system depends on, in a codebase without tests, is how you cause an incident. Shankara, having offered the rope, admits the limit in the next breath: the shallow error goes the instant you see the truth, but the deep one, wound through everything, is not undone by a single look. Reflection has to follow. The wrong abstraction you can inline in an afternoon dissolves in an afternoon; the one wired through a schema with ten million live rows you may see clearly and still be unable to migrate away from this quarter. Knowing it is a snake only opens the work; freeing yourself of it is the longer task.

The clearest software statement of all this is Sandi Metz’s, in a short piece called “The Wrong Abstraction,” published in 2016 and drawn from a talk two years before it. She tells the lifecycle without a word of Sanskrit: a clean abstraction, extracted from real duplication, that a later engineer bends to fit a case it was not made for, one parameter at a time, until it has become a “condition-laden procedure” that everyone fears and no one can change. The reason we keep bending it, she names precisely, is the sunk cost fallacy. The more it cost to build, the more we feel we must preserve it, and the harder we push forward into the mess. Her advice is the lamp. Stop pushing forward, go back, inline it, and “let it show you what’s right.” That last phrase is vidya in six words. You remove the wrong abstraction and let the substrate show you the right one.

The function no one trusts is still open in your editor. The seventh parameter is still the easy move, and it will work, the way restarting the pod worked, the way the cache theory felt right. It will also leave the snake exactly where it is for a while longer. The other move is to put the code back where it came from, into each of its callers, and read what is actually there. You may find one abstraction, or two, or a little duplication worth keeping. What you will not find is the snake. It was never in the domain. You brought it.

Further Reading

Shankara’s account of adhyasa opens his commentary on the Brahma Sutras, the passage known as the Adhyasa Bhashya. George Thibaut’s translation (Oxford, 1890, public domain, in the Sacred Books of the East) carries both the definition and the rope and snake; Swami Gambhirananda’s edition (Advaita Ashrama) is the one to reach for when you want the traditional reading in modern English. On the software side, Sandi Metz’s “The Wrong Abstraction” (sandimetz.com, 2016) is the essay to read, and the line “duplication is far cheaper than the wrong abstraction” comes from her RailsConf 2014 talk, “all the little things.” Joel Spolsky’s “The Law of Leaky Abstractions” (2002) is the companion piece on why the substrate always shows through, and the DRY principle it complicates comes from Andrew Hunt and David Thomas’s The Pragmatic Programmer (1999).