Appendix A — Standard Operating Procedures

For any issue or feature addition there are a few general strategies to follow while working with any of the packages in The Workbench. These strategies will make it easier to ensure the quality of work with the bonus of being able to more easily collaborate with other maintainers and contributors to this project.

A.1 Get a Reproducible Example

The most powerful tool in the developer’s toolbox for demonstrating an an issue or a new feature is the reproducible example. It is no surprise that Jenny Bryan (of “Everything I Know Is From Jenny Bryan” fame) is the maintainer of the {reprex} package and has an incredibly useful webinar on how to create reproducible examples with {reprex}.

System-Dependent Issues

There are times when you cannot create a reproducible example that will produce the results you want to see. This is often the case when there is a system-dependent issue. In these cases, you should share the reprex with someone who has the system in question and have them run it.

A.2 Git/GitHub Etiquitte

In The Workbench practice, commits are never pushed to the main branch of the packages. All issues and features are fixed/made by pull requests because they give a better accounting of what was changed and why. Using this, it is good to strive for these practises1:

  1. If an issue for the feature/bug does not exist, create it with documentation about where the error exists (or where the feature should be). Use the “copy permalink” when you click on a line number in the source view of GitHubto create a preview of the code as it existed before the feature/fix.
  2. create a new branch from main that describes the purpose and includes the issue number. For example: avoid-gert-config-449 removed a bit of code that was using the {gert} package to check for a git user, which was unnecessary.
  3. push the branch to GitHub2 and create a new draft pull request
  4. Add a test that will reproduce the bug (if possible) before any code
  5. COMMIT OFTEN. There are MANY times when I have been working on a feature deeply and have forgotten to commit, only to lose track of what changes I made and why3. Remember that git is your lab notebook.
  6. Iterate writing code and committing until the tests for that particular function passes.
  7. Write a new item in NEWS and bump the version in DESCRIPTION.
  8. Push to GitHub and mark as ready for review; tag @carpentries/maintainers-workbench

A.2.1 Pull Request Reviews

While The Workbench was being rapidly developed by Zhian Kamvar, all pull requests were merged by Zhian, even if he created them. After the launch of The Workbench across all official lessons, The Workbench Maintainer Group will now review all pull requests. In order to ensure pull request reviews are equitably given, please read Alex Hill’s blog post entitled “The Art of Giving and Receiving Code Reviews”.

A.3 Addressing Issues

One big challenge with issues is that you cannot know where the issue is coming from. If you do nothing else, please watch this Keynote talk from Jenny Bryan: Object of type ‘closure’ is not subsettable. This will give you the right mindset for broadly approaching issues in R package development, which can be summarised as:

  1. Turn it off and on again (Restart R in a clean session)
  2. Make a reprex
  3. Dig into the error (via traceback() and/or a debugger)
  4. Future-proof your fixes and make things within reach

With these tools, if you cannot reproduce the error, you can guide the user to give you the information you need to reproduce the error and then you can follow the git guidelines to iterate on the fix.

Think of the big picture

As you are fixing an issue, consider the larger picture of the issue. Sometimes you will run into an issue that will technically fix it, but still leave the possibility for future issues to open up. The most important thing is to document what future work needs to be done to ensure a robust solution while it is still fresh in your mind. This can be in an issue or in the comments of the code itself.

Sometimes an issue is urgent enough that a quick fix is what is needed, and in those cases, it’s best to open up a followup issue that addresses what would be needed for a more robust fix. Sometimes the solution is splitting a large function into smaller functions, but other times it may be a refactor of the internal data structure.

A.4 Creating New Features

If you want to create a new feature, the most important thing to do is to clearly define the audience, scope, dependencies, inputs and the outputs of the feature that you want to create. It’s best to strive for new features that bolster existing features (e.g. sandpaper::serve() provides an continously updating version of sandpaper::build_lesson()) or those that are modular and optional (e.g. the fail_on_error config option for lessons using R Markdown force an error in code blocks to stop the lesson from rendering unless those code blocks have the error = TRUE option).

One of the most important things to consider when adding new features is the maintenance and deployment workflow. Maintainers and contributors should not need to worry about function arguments, GitHub Actions, or new Markdown syntax in order to implement a new feature that will be deployed automatically to all of the lessons. A new feature should not break their workflow or the deployment workflow.

Simple Example

As an example, when we were discussion folder organistaion, The original config template had a “schedule” section instead of “episodes”. When I replaced “schedule” with “episodes”, I added functionality to allow for old-style lessons to move forward so that any existing test lessons did not break. To this day, you can still create a lesson using schedule instead of episodes as the keyword.


  1. I say strive for these practises because I do not always follow them myself, but they are there for very good reasons. For example: testing before you write code helps you design your code to do the thing you want it to do AND it helps avoid confirmation bias in the tests.↩︎

  2. In RStudio, when you use the interface to create a new branch, it will generally also push the branch to GitHub for you. If it does not do this, you will need to push the branch with git push -u origin <BRANCH_NAME>. I do this often enough that I’ve created a git alias (which can go in your ~/.gitconfig file):
    pushb = !"git push --set-upstream $(git remote | head -n 1) $(git symbolic-ref HEAD --short)"↩︎

  3. You do not have to push after each commit. Sometimes it is strategic to collect a little stack of local commits in case you were experimenting and need to revert back to one that gave you a working state.↩︎