Broccoli Debugging ππ
A couple of years ago I wrote a blog post entitled Debugging a Broccoli Tree. That has been the most common resource I give to folks that are having a hard time understanding why a given build pipeline is not behaving as they intended it to. However, over the years I have slowly become increasingly unsatisfied with editing the build pipeline and sprinkling around a bunch of stew.debug
statements every time I need to debug something.
Issues with stew.debug
π«
I believe that using stew.debug
is much like using a debugger
statement: an extremely useful tool that you would generally use when you are actively working on a specific section of code. It is not something you would leave strewn about your build pipelines, because:
- It becomes quite noisy to have a bunch of
DEBUG-*
folders) laying around in your project. - It has a fairly significant impact on your build speed (due to the fact that the
stew.debug
nodes are creating a bunch of additional I/O operations to duplicate the contents of their input nodes).
In addition to these issues with leaving stew.debug
's around there is a massive impedance mismatch. Most of the time folks are debugging something in a build, they are not actually working on the build pipeline itself. They're generally just trying to figure out why a given addon isn't working correctly, or why some content that they assumed would end up in the output is not present. In those cases, adding stew.debug
's is the farthest thing from their mind.
Hmm, so how can we address all of these concerns? They seem at odds with each other. How can we have the ability to easily debug build related things and not have to modify build pipelines that are generally out of our control?
Identifying a Longer Term Solution π π€
The first step in solving this conundrum is defining the constraints that we need to address for both of the main use cases (pipeline author and pipeline consumer). I came up with these primary constraints:
- Pipeline Authors:
- Must allow the build pipeline author to leave the debugging capabilities in the build.
- Must not incur an additional performance penalty when it is unused.
- Consumers:
- Must be easy to enable.
- Must be able to limit the output to just the nodes we are interested in.
Having this list of constraints was actually very helpful in identifying a path forward. As you might notice, this list of constraints is very similar to how we handle logging in most of our Node packages: via the debug package.
For those of you that are unaware of debug
, it is a utility that lets users opt-in to logging at run time while leaving the log statements in the package. The general API for debug
is:
In normal runtime conditions these log messages do not emit console output, but if the user provides a special environment variable it will begin logging these messages to the console. For example the following would enable logging for any usage of debug
that specified common-prefix-here
as their namespace:
DEBUG=common-prefix-here:* node ./script.js
Enter: broccoli-debug
π·
Now comes the easy part! We have identified exactly what we want, and we can finally start building something.
broccoli-debug has exactly the API that we enjoyed about the debug
package: we can leave the debug nodes in the build pipeline without worrying about impacting the build performance and our users can opt-in to more detailed logging output whenever they need to.
The best way to use broccoli-debug
is by creating the debug tree helper with your prefix, then invoke the callback with the tree you are debugging along with a label:
Lets consider an example addon that does something special in its treeForAddon
hook. It might be useful to inspect the result of that custom processing before calling super
. You could implement that functionality with something similar to:
Normally, when this addon is used no special debugging output will be generated, but when the user invokes our build with the proper environment variable set they can inspect the exact contents of that tree:
BROCCOLI_DEBUG=some-awesome:* ember serve
The above statement will create a DEBUG/
folder in the project root, which will now have the following folders:
DEBUG
βββ some-awesome-including-addon-special-sauce:input
βββ some-awesome-including-addon-special-sauce:output
These folders will have the exact contents of the nodes that were passed in to this.debugTree
.
Conclusion π©
I will continue to use broccoli-stew
's stew.debug
helper it is still an incredibly powerful tool for debugging a build pipeline while working on it. However, I am beginning to slowly incorporate broccoli-debug
into more of the build pipelines that I help maintain (see ember-engines/ember-engines#407), and I think you should too!