10. Vivarta

The crash report gives you one line. TypeError: Cannot read properties of undefined (reading 'code') at t (bundle.min.js:1:41022). That is the running process talking, and it is barely talking. t is a name the minifier invented. bundle.min.js is a file the build generated. Column 41022 of its single machine-written line is where the program died, and on its own it says nothing about why.

You paste the trace into the error tracker and it resolves. t becomes Cart.applyCoupon, and bundle.min.js:1:41022 becomes cart.ts:88, a frame you wrote. Nothing about the running program changed. The tracker applied the source map the build had produced and read the minified frame back to its origin. The failure sat at line 88 the whole time. The minified trace and the source line were never two things; one is the other, transformed.

Advaita has two words for transformation, and the distance between them is the distance between a trace you can read and one you cannot. Vivarta is apparent transformation: the thing takes a new form without changing essence, and the form can be resolved back. Parinama is real transformation: the thing becomes something else, and there is no way back. The tradition splits on which one the world is. Samkhya holds that the world is parinama of its cause, a real modification. Advaita holds that it is vivarta, appearance laid over one unchanged ground, and Shankara argues the case directly in the second book of the Brahma Sutra commentary. You do not have to settle the metaphysics to use the pair, because your build pipeline runs both kinds of transformation every day and labels neither.

The Panchadasi, Vidyaranya’s fourteenth-century Advaita manual, reaches for the sea. A wave, it says, is “neither distinct from, nor one with the sea; nor is it either.” It is the sea in a passing shape, name and form that rise and fall over water that does not. That is vivarta: the wave is the ocean transformed, recoverable to it, never a thing apart.

Put the wave next to the pipeline. The source is the water, and everything the toolchain does to it on the way to a running process is a wave, a passing shape the source takes. The parser turns text into a syntax tree whose nodes still point back at the lines they came from. The compiler lowers the tree to bytecode or machine code, and a debugger holding the symbols steps it back to the line. The just-in-time compiler inlines a function into its caller, and when it deoptimizes it hands back the frame you wrote. Every one of these is vivarta. The essence survives the change because the toolchain kept a way back: a source map, a symbol table, a line table.

Other transformations keep no way back. A minifier shipped without its source map does more than shorten names. It discards the record of what they were, and applyCoupon becomes t with nothing left to resolve it. strip removes a binary’s symbol table, and a crash arrives as a column of hex addresses. A destructive migration rewrites the rows and drops the old column, and the state it held is gone. This is parinama. The change is real and the origin is not recoverable. Vivarta you can read back. Parinama you cannot.

The line is subtler than lossless versus lossy. Minification with a source map still throws away whitespace, comments, the names themselves, and stays vivarta anyway. What the map preserves is the identity, the path from a running frame back to the line that produced it. That recoverability is the source map, and without it the same change is parinama.

This reframes what debugging is. The usual picture holds two separate worlds, the code you write and the process that runs, joined by a build step you mostly ignore, and debugging is guessing at the running world from outside it. The truer picture is one thing in two forms, with the build as the transformation between them.

The claim needs one honest limit. The process carries state the source never held: the values in flight, the request being served, the heap at this instant. What a build lets you recover is structure and behavior, the path from a frame back to a line. The live state exists only while it runs, and no map brings it back. The wave is the ocean’s water, and the shape it holds this instant is its own. So whether you can debug turns less on your cleverness than on how much of the path stayed reversible.

The running process is the source in motion. You can trace a failure back to its cause exactly as far as the transformations between them stayed reversible.

The ledger has to be honest. A source map built from different source than the bundle it maps will resolve the frame to a real line that is the wrong line, and do it with full confidence. That failure is worse than a trace that stays unresolved, and it belongs to the previous principle rather than this one, a measure you trusted past the edge it was good for. Vivarta assumes a faithful map. A faithful one brings the frame back true; a stale one hands you a confident, wrong answer instead.

Recoverability runs on a spectrum. Optimized native code with partial debug information gives you the function and usually the line, and then a local shows as <optimized out> because the compiler kept no location for it at the crash point. You get enough to place yourself in the code, short of the full picture. Most of a real build sits here, mostly recoverable, and the honest question at each step is how much.

None of this makes parinama a mistake. You drop the source map to ship fewer bytes, strip the binary to shrink it, run the destructive migration to be rid of a column you are sure you will never read again, and each of those is often the right call. What matters is knowing which steps in your chain are lossy, because those decide whether a page at three in the morning is readable. Keep the ledger where it earns its place: upload the source map, archive the debug symbols, keep the migration’s backfill. Recoverability is a thing you keep on purpose.

React ships this choice in the open. Its production build swaps each error message for a number, so a crash reads Minified React error #418 and a link, because shipping the full sentence to every user would cost bytes that nobody reads until something breaks. The message is not gone. React keeps the decoder, and the link resolves the code back to the text and fills in the value that goes with it. That minified error is vivarta, transformed to save weight and recoverable because the map was kept.

Go keeps the ledger a different way, down in the runtime. Build a binary with -ldflags="-s -w" and it is genuinely stripped, the symbol table and DWARF both gone, and its panics still print the function, the file, and the line. The toolchain embeds a separate line table that strip cannot reach, so the way back is not something you can drop.

An earlier principle set local, staging, and production beside the Mandukya Upanishad’s three states of waking, dream, and deep sleep, and left a fourth state waiting here. The Mandukya calls it turiya and describes it mostly by what it is not, “with which there can be no dealing.” It is not a fourth environment. You cannot deploy to it or open a shell on it, because it is the source itself, the thing all three environments are only ever running. Local, staging, and production are three waves. The source is the water. You debug a running process by resolving it back toward that water, and how far you get is how much of the way was vivarta.

An unreadable trace names a gap in your build before it names a bug. t at column 41022 is the source, still there, in a form the build left you no way to read. The line was 88 the whole time. Whether you can get to it is a decision your build already made.

Further Reading

The Panchadasi of Vidyaranya is a fourteenth-century Advaita manual; Nandalal Dhole’s 1899 English translation (public domain) carries the wave-and-sea passage used here, in the chapter on contentment, and the gold-earring-and-bangle image for one substance under many forms. The vivarta and parinama distinction is the Advaita account of causation set against the Samkhya one, and Shankara argues it in the Brahma Sutra Bhashya, second adhyaya. Gaudapada’s Karika on the Mandukya Upanishad pushes the stronger claim, ajativada, that nothing is ever really originated at all. On the engineering side, the Source Map v3 specification defines the ledger a browser and an error tracker walk to resolve a minified frame; React’s minified error decoder lives at react.dev/errors; the Go linker’s -s and -w flags are documented in the cmd/link reference; and DWARF is the debugging-information format a native debugger reads to put you back on the source line. For the three environments and the fourth state that this essay picks up, see the essay on the three realities.