boomer
June 17, 2026 · View on GitHub
The {boomer} package provides debugging tools that let you inspect the intermediate results of a call. The output looks as if we explode a call into its parts hence the name.
boom()prints the intermediate results of a call or a code chunk.rig()creates a copy of a function which will display the intermediate results of all the calls of it body.rig_in_place()rigs a namespaced function in place, so its always verbose even when called by other existing functions. It is especially handy for package development.
Installation
Install CRAN version with:
install.packages("boomer")
Or development version with:
remotes::install_github("moodymudskipper/boomer")
boom()
library(boomer)
boom(1 + !1 * 2)

boom(subset(head(mtcars, 2), qsec > 17))

You can use boom() with {magrittr} pipes or base R pipes: just pipe
to boom() at the end of a pipe chain.
library(magrittr)
mtcars %>%
head(2) %>%
subset(qsec > 17) %>%
boom()

If a call fails, {boomer} will print intermediate outputs up to the occurrence of the error, it can help with debugging:
"tomato" %>%
substr(1, 3) %>%
toupper() %>%
sqrt() %>%
boom()

boom() features optional arguments :
-
clock: set toTRUEto see how long each step (in isolation!) took to run. -
print: set to a function such asstrto change what is printed (see?boomto see how to print differently depending on class). Useful alternatives would beconstructive::construct, to print code that accurately reproduces the objects and avoid the caveats ofprint(), ofinvisible(to print nothing).
One use case is when the output is too long.
boom(lapply(head(cars), sqrt), clock = TRUE, print = str)

boom() also works works on loops and multi-line expression.
boom(for(i in 1:3) paste0(i, "!"))

rig()
rig() a function in order to boom() its body, its arguments are
printed by default when they’re evaluated.
hello <- function(x) {
if(!is.character(x) | length(x) != 1) {
stop("`x` should be a string")
}
paste0("Hello ", x, "!")
}
rig(hello)("world")

rig_in_place()
rig() creates a copy of a function, but when developing a package we
might want to rig a function in place so it has a verbose output when
called by other functions. For this we can use rig_in_place().
For instance you might have these functions in a package :
cylinder_vol <- function(r, h) {
h * disk_area(r)
}
disk_area <- function(r) {
pi * r^2
}
cylinder_vol depends on disk_area, call devtools::load_all() then
rig_in_place() on both and enjoy the detailed output:
devtools::load_all()
rig_in_place(cylinder_vol, disk_area)
cylinder_vol(3,10)

boom_on() and boom_off()
While debugging a function, call boom_on() and all subsequent calls
will be boomed, call boom_off() to return to standard debugging.

boomer and AI
Just like us, AI first tries to debug from looking at the code alone and might fail. Give it some logs of what every intermediate output was at any time and it will be much more performant, here’s a suggested workflow when working on a package.
# Set these options, in the console, in your R Profile, or in .onLoad itself
options(
# log to a file rather than the console,
boomer.log = "boomer_execution_log.log",
# log the accurate objects rather than just print() them,
boomer.print = constructive::construct,
# log the times as well to spot performance bottlenecks
boomer.clock = TRUE,
# show more context, usually too verbose for humans but perfect for AI
boomer.ignore = character(),
# vector of functions to rig from your package
boomer.rig_on_load = c("fun1", "fun2")
)
.onLoad <- function(libname, pkgname) {
# rig relevant functions any time the package is reloaded
boomer::rig_on_load()
}
The execution created contains a header explaining what it is, and timestamps for context, so AI will have no problem understanding them and interpreting them.
boom_shinyApp()
A very experimental feature that allows you to rig the reactives of a
shiny app. See vignette("shiny", "boomer") for more information.
For the following app, saved in a proper project/package:
histogramUI <- function(id) {
tagList(
selectInput(NS(id, "var"), "Variable", choices = names(mtcars)),
numericInput(NS(id, "bins"), "bins", value = 10, min = 1),
plotOutput(NS(id, "hist"))
)
}
histogramServer <- function(id) {
moduleServer(id, function(input, output, session) {
data <- reactive(mtcars[[input$var]])
output$hist <- renderPlot({
hist(data(), breaks = input$bins, main = input$var)
}, res = 96)
})
}
ui <- fluidPage(
histogramUI("hist1")
)
server <- function(input, output, session) {
histogramServer("hist1")
}
The output of boom_shinyApp(ui, server) will look like:

There will be issues, please report!
Addin
To avoid typing boom() all the time you can use the provided addin
named “Explode a call with boom()”: just attribute a key combination
to it (I use ctrl+shift+alt+B on windows), select the call you’d like to
explode and fire away!
Options
Several options are proposed to tweak the printed output of {boomer}’s
functions and addin, see ?boomer to learn about them.
In particular on some operating systems {boomer}’s functions’ output
might not always look good in markdown reports or reprexes. It’s due to
how the system handles UTF-8 characters. In this case one can use
options(boomer.safe_print = TRUE) for a more satisfactory output.
Notes
{boomer} prints the output of intermediate steps as they are executed,
and thus doesn’t say anything about what isn’t executed, it is in
contrast with functions like lobstr::ast() which return the parse
tree.
Thanks to @data_question for suggesting the name {boomer} on twitter.