Charles' Profile picture
Hi, I'm Charles Szilagyi, full-stack engineer, founder, mentor. I'm using software to make things better without making them worse in London, UK.

Debug anything: Editing variables and restarting functions

13 April 2020
3 min read

It's possible to replay code blocks in the debugger to see how they behave in different scenarios. Let's look at how to change variables and restart functions in the call stack - a huge time saver!

Previously we've looked at how to start debugging a simple server and the ways you can move around the code in the debugger. Today we'll check out two less prominent features. The first one is the ability to edit variables. The second being the ability to replay a function - to restart a stack frame, to be precise - without restarting the debugger or the application.

While these features might not be super intuitive, they are quite powerful and can save a ton of time. You can try a function buried deep in the call stack with different inputs, without restarting a potentially long and tedious journey.

Let's see how we can use them together to try different scenarios in our usual tiny server app.

Setup

We'll play around with the usual code snippet, go ahead and copy-paste it into an empty file if it's not on your machine yet:

const http = require('http');
const url = require('url');

const hostname = '127.0.0.1';
const port = 3456;
const serverUrl = `http://${hostname}:${port}`

const getNameFromReq = (req) => {
  const {name} = url.parse(req.url, true).query;

  return name
}

const getGreeting = (name) => {
  const greeting = `Hello, ${name}!`

  return greeting
}

const server = http.createServer((req, res) => {
  const name = getNameFromReq(req)
  const greeting = getGreeting(name)

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end(`${greeting}\n`);
});

server.listen(port, hostname, () => {
  console.log(`Server running at ${serverUrl}`);
});

1585563843 deuganything 02 01 code

Hello, undefined!

Our toy server assumes there is always a query parameter called name present in the request. If it's missing, the response becomes "Hello, undefined!":

> curl http://127.0.0.1:3456
Hello, undefined!

Let's use the debugger and see how our getGreeting function behaves with different name parameters. The fix is trivial, obviously, but let's play along, we're here to see what the debugger can do. 🤓

Let's put a breakpoint on line 15 and hit the server without a name query argument (e.g. curl http://127.0.0.1:3456).

You should see the debugger kicking in and stopping on line 15.

1586334485 deuganything 03 01 stop

If you're not confident how to do this, please refer to Part 1, the basics

Make sure the Variables and Call stack sections are both open in the debug sidebar. We'll focus on the local variable name for now, which is undefined:

1586334491 deuganything 03 02 undefined

Let's step over the next line (F10) and observe the return value of getGreeting:

If you need a quick overview of step over, step into and step out, read Part 2, Navigating with steps

Ok, no surprises here: if the name is undefined, the greeting will say Hello, undefined!.

Rewind

Let's re-run this function, this time with a different name. We don't need to fire off another request - we can restart the last stack frame:

And we're back at the beginning of the getGreeting function 👌

Let's now try what would happen if the name was null? We can edit the local variable to fund out:

We can try to set a string value - mind the quotes around the value:

You can override anything local or defined in the closure, including functions:

Keep in mind: once you override functions you'll have to restart the process to get back to the original implementation.

And this is it: you are now able to restart functions in the debugger and edit local and closure values, including strings and functions. Why not play around and see what happens if you restart functions higher up in the stack?

Happy debugging! 🥼