R package development in Positron
First impressions using Positron for R (and Python!) package development
This post is about the R package development experience with Positron, the new IDE from Posit based on VS Code. This is not a tutorial on R package development in general — there are great resources for that elsewhere. Read on.
RStudio, VS Code, and Positron
Back in 2011 I wrote a blog post about a relatively new IDE for R called RStudio. At the time I was using ESS and NppToR, and RStudio was a welcome replacement for both. Fast forward a decade and RStudio is surely the most widely used feature-rich IDE for data analysis, software development, and document authoring using R (and RMarkdown, Quarto, etc).
For a while now RStudio has supported Python via reticulate, and supports syntax highlighting for other file types (e.g., shell scripts and Dockerfiles). However, I still end up switching back and forth between RStudio for all things R, and VS Code for everything else. Working with R, building R packages, and writing with RMarkdown or Quarto just never felt natural to me with VS Code, regardless of all of the extensions I tried.
In June 2024, Posit (the company formerly known as RStudio) released a beta version of its next generation IDE for data science with R and Python called Positron. Positron is effectively a fork of VS Code (“Code OSS”). While folks at Posit have said RStudio will continue to be supported, it sounds like Positron development will take a front seat on any future development.
This isn’t a general overview of Positron. For that, I’d highly recommend reading:
These resources provide great overviews of Positron for R and Python, and Andrew’s post in particular goes over keymappings and settings to make Positron feel more like RStudio (I definitely recommend enabling RStudio’s keymap, if that’s your thing).
I spend a lot of time developing R packages. I put my first package on CRAN back in 2014. I’ve published many since, including personal hobby projects and company projects that we made open source, and I’ve built dozens of internal R packages for companies I’ve worked for over the years. RStudio has an amazing set of tools for package development, and I wanted to get a sense of what package development feels like in Positron. Let’s go.
R package development in Positron
My package development workflow relies heavily on usethis for development and RStudio’s Build pane for documenting, building, testing, and checking.
First things first — if you spend a lot of time in RStudio I highly recommend turning on RStudio’s keymap, then customizing as described in Andrew Heiss’s post and on the Positron wiki. Cmd-shift-P to bring up the command palette, search for “keymap” then check the box.
Create a project
I use a project-oriented workflow, First things first, let’s create a new project. Use cmd-shift-P to create a new project, and follow the prompts to create a new R project. Go ahead and initialize version control or run git init
in a terminal.
Create a package and write a function
From here I’d do the same thing I’d do in RStudio: navigate to the console and create a new package. Let’s call it hellow.
usethis::create_package("hellow")
This package is going to have a single function called isay()
that’ll first message some praise to you as a stellar developer, then output a simple greeting in English.
isay <- function(size=1, ...) {
message(praise::praise())
h <- sample(c("Hello", "Hi", "Howdy", "What's up?"), size=size, ...)
return(h)
}
Here’s one feature in RStudio that I don’t see replicated in Positron. When I have a function in RStudio, I can place my cursor in the function body, and either use the command palette or the little magic wand button to insert a Roxygen skeleton for the given function.
To my knowledge there’s no such functionality in Positron so we’ll have to write the Roxygen documentation ourselves. No big deal for this simple function, but if we had many arguments, this functionality is a nice little time saver.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdec8b4b8-6bc4-48d4-93c4-79791d4efd81_966x776.png)
Document and build
Next, either use the command palette or the keyboard shortcut cmd-shift-D to document the package. This is important not just for rendering the .Rd documentation files. This will also write the exported isay()
function into the NAMESPACE file so you can actually use the function once you build and load the package.
Next, use the command palette to build the package, or just use the RStudio keyboard shortcut you’re used to: cmd-shift-B.
A slight difference I notice here is that RStudio uses R CMD INSTALL
, while Positron uses pak::local_install()
. The end result should be the same: the package is rebuilt and installed from source. I’m not sure if there would ever be any functional differences between the two, but both work just fine for my case here.
Check and test
Perhaps before we installed the package, we should have run a check! RStudio has a button for this in the build pane, but I just use the keyboard shortcut cmd-shift-E. The same keyboard shortcut works in Positron, or you could use the command palette. Note that if you have devtools installed, both RStudio and Positron use devtools::check()
instead of R CMD check
. I prefer this behavior.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F628384e8-dc3a-4d16-b000-d3ce5ff253c6_1067x851.png)
Uh oh. We have a few warnings. Easy fixes though. When I used usethis to create the package, it wrote some placeholders in the DESCRIPTION file. One was the LICENSE. Additionally, I’m using the praise()
function from the praise package, but I forgot to add it to the Imports field in the description. I’ll fix these the same way I do it in RStudio: usethis in the console.
usethis::use_mit_license("Stephen Turner")
usethis::use_package("praise")
After this, our devtools::check()
is OK.
Now, let’s write some tests. Again, usethis to the rescue: usethis::use_test(“hello”)
. Let’s write a few simple test cases.
test_that("isay", {
expect_message(res <- isay())
expect_identical(length(res), 1L)
expect_message(res <- isay(50, replace=TRUE))
expect_identical(length(res), 50L)
expect_error(isay(50))
})
Again, I can use the same keyboard shortcut I use in RStudio (cmd-shift-T) to run all the tests with testthat, or I could use the command palette.
I use several RStudio addins. One I use constantly when I’m developing a package is the addin to check package coverage with covr.
This results in a new window in the RStudio Viewer pane showing the percent coverage I have on each of my functions.
Unfortunately, as noted in the Positron wiki, Positron doesn’t have any support for RStudio addins. This isn’t a dealbreaker, just a minor inconvenience. I found this addin definition in the covr source code, and it’s pretty simple:
covr::report(covr::package_coverage())
Running this command in the Positron console produces the same report, but the HTML page opens in your default web browser. I shy away from putting too much into my ~/.Rprofile as this can lead to problems with reproducibility, but developer conveniences like this should be fine. This would let me simply run cr()
to produce this covr report.
cr <- function() {
if (interactive()) {
covr::report(covr::package_coverage())
}
}
Docker and external tools
We’re done with the hellow R package. Here I’m going to demonstrate another common pattern I’ve used for years, one that makes the VS Code-like experience of Positron really shine once you have to step outside of the R ecosystem. The pattern is something like this: I want to do some data processing that requires the R package I’m building but also some other external tools upstream and/or downstream, and I want to do it all in a container. In this example, I want to build a Docker container that uses the hellow R package to issue a greeting, and another external tool to translate that greeting into another language.
The pracpac package (CRAN, paper) — “Practical R Packaging with Docker” — streamlines the creation of Docker images with R packages and dependencies enabled. Running pracpac::use_docker()
analyzes your package DESCRIPTION, builds an renv lock file that captures the versions of the dependent packages you have installed on your machine, and writes a Dockerfile that can build your R package, installing all the dependencies from that renv lock file, guaranteeing that the versions of all the dependent packages you have on your machine are what gets installed in the Docker image. The pracpac package also templates out pre- and post-processing R scripts, and a shell script that will drive the analysis.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f9f715-4440-4c89-87f2-591601bc5ba4_2546x892.png)
After I run pracpac::use_docker()
, I now have a renv.lock file and a Dockerfile. While RStudio can open and even syntax highlight shell scripts, JSON files, and Dockerfiles, the VS Code experience makes Positron a more compelling alternative. This would be the point where I would have previously used RStudio for all the R packaging work, and VS Code for everything else (with all the extension packages I use for editing shell scripts, JSON, Dockerfiles, etc.). Positron wins here once I need to branch out of R. I only need to add the line to install a few additional system utilities using apt, and actually write this shell script. The pracpac package templated out everything else.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe60bc9-92b0-45d7-ace1-8ea54f5e6ef4_1405x1143.png)
I can now run the hellow container, which first uses the hellow R package, giving me some praise, then issuing a greeting in English. The greeting is then translated into French using the Google Translate CLI installed in the Docker container.
docker run --rm hellow
You are finest!
[1] "What's up?"
[1] "Quoi de neuf ?"
Translations of [1] "What's up?"
[ English -> Français ]
[1] "What's up?"
[1] "Quoi de neuf ?", [1] « Quoi de neuf ? »
docker run --rm hellow
You are peachy!
[1] "Howdy"
[1] "Bonjour"
Translations of [1] "Howdy"
[ English -> Français ]
[1] "Howdy"
[1] "Bonjour", [1] « Salut »
Conclusions
Positron feels like RStudio for package development
My first impressions of Positron are generally positive. It’s still a little rough around the edges, as to be expected from a product still in beta. I’m betting we’ll hear more about Positron at the Posit conference next month.
That being said, Positron is almost at parity with RStudio’s feature set for R package development. I use usethis for dependency management, DESCRIPTION file manipulation, .R and test file creation, data-raw creation, vignettes, pkgdown, GitHub actions, and other tasks. And, I do this all from the console pane. Here, there’s no difference in user experience between Positron and RStudio.
I rely on the IDE functionality in RStudio for documenting/building/checking/testing packages. Once you enable the RStudio keymap, your muscle memory from RStudio will carry you in Positron:
Cmd-Shift-D: Document with Roxygen
Cmd-Shift-B: Build and install
Cmd-Shift-T: Test package with testthat
Cmd-Shift-E: Check package with
devtools::check()
RStudio adds and the “Code Tools” items under RStudio’s magic wand menu are nice to have features but aren’t dealbreakers. However, these small sacrifices are outweighed by the VS Code-like experience that Positron provides once you have to step outside of the R ecosystem.
Dealbreakers
Positron’s wiki clearly states that Positron is a beta product, and that there are features that don’t yet work, but may in the future.
Positron might not be a good fit for you today if...
you need stable, polished software. Positron is still in beta, and some features are unstable or unfinished.
you need all the features of the RStudio IDE. Positron doesn’t have all RStudio’s features; some notable absences are inline output for Quarto and R Markdown, profiling, Sweave, RStudio Add-In support, etc.
you use remote development features (e.g. dev containers, remote SSH); these are not supported in Positron yet. However, providing tools for Remote SSH is on our roadmap.
There are a few dealbreakers for me that’ll keep me from switching full time to Positron, at least in the interim: (1) remote development, and (2) copilot. The Positron team is actively working on both of these.
The biggest dealbreaker for me that’ll keep me coming back to VS Code for non-R development is its remote development capability. This is a general Positron feature, not strictly related to R package development. This is the ability to connect VS Code running locally to remote servers through SSH. That is, I fire up VS Code on my Mac, Cmd-Shift P to open up the command palette, and connect to a cloud VM over SSH, allowing me to edit remote files locally, and execute code on the remote machine. As of this writing, Positron doesn’t currently support this, and the workarounds aren’t really workarounds. But, it sounds like Posit is working on this. Until then, Positron won’t replace VS Code for my non-R development, and as long as I’m still using VS Code, I probably won’t leave RStudio, and I don’t have the mental bandwidth to add a third IDE to my development stack.
Also, it sounds like Posit is actively working on Copilot and Copilot chat support in Positron. Following the discussion on some of those issues it sounds like there are third party extensions that can enable this, but I’d love to see native copilot support like we have in VS Code and RStudio. (On the other hand, I’ve lately been turning copilot off when I’m trying to find a creative solution to a niche problem.)
Licensing
Another thing to keep in mind is Positron’s licensing. Positron uses the Elastic License 2.0, which includes this important limitation:
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
My company, and many others in the Bioinformatics Platform as a Service space (Form Bio, Seqera, Latch Bio, DNA Nexus, etc.) offer users the ability to launch an RStudio Server, Jupyter Notebook, or VS Code instance running on managed compute instances, connected to platform data and results. The use case here is this: imagine you’ve run a variant calling workflow on Form Bio or an RNA-seq analysis with an nf-core workflow on Seqera. You have results as outputs from those workflows and now you’d like to do some tertiary analysis on that data, or write a report with Quarto pulling in results files for your analysis. You could download the data and do this analysis and reporting locally. Alternatively, you could launch RStudio Server running on managed compute with access to platform storage, and perform all your analysis or reporting remotely in that RStudio server instance. None of these companies are selling a managed RStudio Server (or VS Code, or Jupyter Lab) as a product offering, and in no way would they compete with Posit’s enterprise offerings. I’m not a lawyer, but it looks like the Positron license would prevent a use case like this.
Preview: Python package development in Positron
I’ll write a post on Python package development with Positron in the future, but for a preview — if you’ve developed Python packages using VS Code before, you’ll feel at home in Positron. Here’s a snapshot of developing a minimal version of the same package, this time in Python.