Debugging Phoenix and Elixir Applications in Visual Studio Code.

Brooklin Myers
Jun 21 · 7 min read

Spend less time fixing code and more time delivering valuable new features.

Photo by Munro Studio on Unsplash

Knowing how to debug your Phoenix or Elixir project properly is the difference between finding a bug in a few minutes or spending the next few painstaking hours reading thousands of lines of code looking for the one line that’s causing your app to crash, your boss to lose their hair (there’s a reason many developers are bald), and your users to abandon ship.

In this article, you’re going to learn how to leverage IO, IEX, and the built-in Visual Studio Code debugger to improve your debugging ability.

Debugging using Elixir’s IO module

The simplest way to debug is printing. You can print values using the IO.puts and IO.inspect functions.

IO.puts is used for simple, readable values which implement the String.Chars protocol. This includes integers, strings, and atoms.

iex(1)> IO.puts("example string")
example string
:ok
iex(2)> IO.puts(:example_atom)
example_atom
:ok
iex(3)> IO.puts(2)
2
:ok

IO.puts will crash if you give it something like a tuple.

iex(1)> IO.puts({1, 2, 3}) 
** (Protocol.UndefinedError) protocol String.Chars not implemented for {1, 2, 3} of type Tuple
(elixir 1.11.2) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir 1.11.2) lib/string/chars.ex:22: String.Chars.to_string/1
(elixir 1.11.2) lib/io.ex:686: IO.puts/2

IO.puts can also give weird values for lists because it’s not converting elixir’s internal representation of the value back into something human-readable.

iex(1)> IO.puts [1, 2, 3]                
☺☻♥
:ok

IO.inspect knows how to print all data structures, including lists, structs, tuples, and maps.

iex(1)> IO.inspect([1,2,3]) 
[1, 2, 3]
[1, 2, 3]
iex(2)> IO.inspect({1,2,3})
{1, 2, 3}
{1, 2, 3}
iex(3)> IO.inspect(%{"example" => "map"})
%{"example" => "map"}
%{"example" => "map"}

You’ll notice that inspect appears to print everything twice. Actually, inspect only prints once, and iex prints the return value. That’s right, IO.inspect returns the value passed to it. Here’s an example using a function that returns a tuple. This function still returns the tuple {1, 2, 3}, and also prints {1,2,3}.

So when I run the function elsewhere, it has the correct return value.

If you want, you can clone the example repository and run mix test. You should see something like this.

You can also give your inspect values a label to make them easier to read.

You can also use IO.inspect with the binding() function to view the arguments the function was called with.

Debugging using IEX

to run your Elixir project in the Interactive Elixir shell, run:

iex -S mix

To run your tests in an Elixir or Phoenix project in the Elixir Interactive shell run:

iex -S mix test

To run your Phoenix app in the Elixir Interactive shell you can run:

iex -S mix phoenix.server

IEx.pry() allows you to set a breakpoint in your code and inspect values. It’s great when you want to quickly inspect many values at a particular spot in your code without digging through tons of print statements.

Now when elixir evaluates the code where the IEx.pry() function is, you’ll be prompted with a request to pry. If you accept, then you can inspect values in the Interactive Elixir shell.

Ignore that I run iex.bat instead of iex. That’s a quirk of using Elixir on a Windows computer.

You can accomplish the same thing when running a phoenix application in the Interactive Elixir shell.

Here’s an example from a phoenix blog app where the user navigates to http://localhost:4000/author/1 to see a list of posts by an author.

Using the Visual Studio Code debugger

While using IEx for quickly viewing values at a particular spot in the code base is great. However, using an actual debugger with a visual interface is much more convenient when you want to view multiple locations, step through the code, and monitor data as your application runs.

Setting up the debugger for phoenix or elixir is done automatically for you by Visual Studio Code.

When you open a phoenix or elixir project in Visual Studio Code, you should have a Run and Debugger option in your menu. If you don’t see it, you can run a Show Run and Debug command with ctrl-shift-D .

If you don’t already have phoenix or elixir project, or if you want to try running all of the example code for yourself then you can clone the example repository.

Make sure you have an elixir .ex file open and click create a launch.json file. This should create a launch.json file for you.

You can run your tests with the debugger using the mix test configuration.

However, you might want to run your project using the iex -S mix command so that you can use the Interactive Elixir shell with the debugger.

Unfortunately at the time of writing I haven’t been able to figure out how to get this working. The default task runs iex -S mix correctly but I get an error :elixir_ls_expression_timeout anytime I hit a breakpoint. If you have this working please let me know how you did it!

However the mix test configuration works great!

If you are using phoenix add “task”: “phx.server” to the config.

Now you can set breakpoints in your code by clicking the red dot to the left of any line. I’ve set up some example code and a test below to show the start of a breakpoint.

Here’s the test that will execute the code above.

Then select the mix test configuration and run it using the green play button.

You can step through your code by pressing the blue step-over button. Notice that the step0 variable is listed now because it’s been defined, but variables defined below the yellow line are not listed because they don’t exist yet.

You can step into a function using the blue step-into button. This allows you to see the inside of a function rather than jumping to the next line.

First, step over until you are on the line with the function you want to step into.

Then press the blue step-into button. Notice that the variables and arguments change because we’re inside of a different function now.

To conveniently navigate your code without stepping over and into every function, set another breakpoint and hit the blue continue button.

You can also set variables to watch so you can track the progress of a value.

You can set a breakpoint to trigger only if a condition is true. Right click where you would normally add a breakpoint and select the conditional breakpoint option.

This set the condition for the breakpoint to trigger. For example, this breakpoint will only trigger if a equals 1.

Conclusion

Knowing how to debug your code means you spend more time delivering valuable new features and less time fixing the bugs you create along the way. I hope this article helps save you some headache the next time you have a tricky bug to fix. If you have a comment or question please reply to this article and I would be happy to get back to you.

Also, if you figure out how to get the visual studio code debugger running for a plain Elixir project please let me know! I couldn’t figure that out before I hit my personal deadline to publish so it would help me, and other folks a lot! I will update this article if I find out how to fix the issue.

Geek Culture

Proud to geek out. Follow to join our 1M monthly readers.