"My code is correct, but it's not working."
If you have ever said this, you have told the single biggest lie in software engineering.
β‘ The Illusion
We have all been there. You look at a block of TypeScript code, check it with your eyes, and swear to the coding gods that the logic is flawless without any problems. Yet, the terminal is telling the error line, or the process is hanging indefinitely, mocking you.
Your brain goes through the five stages of debugging grief:
- Denial: "This is impossible. The code is literally perfect."
- Anger: "Express/Node/Git is broken. It's a bug in the runtime."
- Bargaining: "Maybe if I restart the server, it will fix by itself."
- Depression: "I don't know what I'm doing. I should just choose a different career."
- Acceptance: Looking at the error trace.
Here is the cold, hard systems reality: The computer is a machine of pure logic. It does not have an ego, it does not have fatigue, and it does not make random assumptions. It executes exactly what you wrote, not what you intended to write.
π Case Study: The Double Response Kalesh(fight)
Let's look at a classic real-world bug that drove me crazy for 20 minutes today.
I was building a deployment reverse proxy engine. When deploying a project, the client CLI kept hanging on the fetch call. The server terminal was spitting out a massive traceback:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:642:11)
...
I checked my route handler. I checked my if blocks. I was returning the response. I was sure.
But the error trace pointed to a single line:
res.send(200).json({ status: 'success', subDomain })
The epiphany: I had chained .json() onto .send(200). In Express, .send() instantly finishes the response cycle and flushes the headers to the client. The subsequent .json() call tries to send data again over a closed channel.
I was looking for complex logical bugs, but the error traceback was pointing directly at the boundary. It wasn't lying. I was.
π The Rules of the Debugging Arena
If you want to survive the systems grind, you must live by these three rules:
1. Trust the Trace over your Eyes
Your eyes read what your brain wants to see (the intended logic). The stack trace reads what the CPU actually executed. Never skip reading the traceback. The file path and line number in the logs are the exact coordinate of your mistake.
2. Audit the Boundaries (I/O)
Most bugs happen at interface boundariesβwhere the client talks to the server, where the code reads the filesystem, or where arguments are passed to a git command. If your client is expecting an array and you wrap it in an object, the system will crash. Check the data shape at the borders.
3. Savour the Error
A clean terminal that hides silent failures is a time-bomb. A red error stack is a roadmap. It is telling you exactly where the system is weak. Every error you debug is a micro-lesson that hardens your systems knowledge.
π Outro
The next time a terminal prints a massive traceback, don't get frustrated. Don't close the terminal.
Take a deep breath, wash your hands in cold water, and read the trace line by line.
Because the compiler doesn't hate you. And the Error never lies.
π¬ What is the single dumbest debugging mistake that cost you hours? Let me know in the comments!
If you liked this post, follow for more deep-dives into backend systems and DevOps pipelines.
β Golu
Top comments (0)