# load raw data
df_raw <- readr::read_csv(here::here("data", "raw", "mydata.csv"))
# save processed data
saveRDS(df_clean, here::here("data", "processed", "data_clean.rds"))
# load a saved model
m1 <- readRDS(here::here("output", "models", "m1.rds"))1 (R)Project Hygiene
RProjects, renv, and Folder Structure
1.1 Why project hygiene matters
Reproducibility begins before you write a single line of analysis. A clean, self-contained project structure ensures that collaborators, and your future self, can re-run everything without hunting for files or reinstalling packages.
The concept of literate programming (Knuth, 1984) established the principle that code and documentation should be written together, with human-readable explanation woven through the analysis rather than added as an afterthought. This philosophy underlies tools like R Markdown and Quarto, and extends naturally to project organisation: just as literate programming asks that code be written for human readers, good project hygiene asks that a project be structured for human navigators, i.e., people who need to understand, re-run, or build on your work without guidance from you directly.
Nagler (1995) outlines a set of principles for good computing practice in quantitative research that remain as relevant today as when they were written. Central among these are the ideas that code should be modular, well-documented, and organised so that the analysis can be reproduced from scratch by someone unfamiliar with the project. This means separating raw data from processed data, keeping analysis scripts independent and sequentially ordered, and never modifying raw data files directly. Bowers & Voors (2016) extend these principles to the context of modern social science, arguing that transparent and reproducible research practice is not merely a technical matter but a scientific norm, one that requires deliberate organisational habits from the outset of a project.
Good project organisation is also a matter of research data management (RDM). Funding bodies and journals increasingly require that data and analysis code be archived and made available alongside publications, in line with the FAIR principles (Wilkinson et al., 2016): (meta)data should be Findable, Accessible, Interoperable, and Reusable. A well-organised RProject is a practical first step towards FAIR-compliant research: clear folder structures, descriptive file names, and documented dependencies make it far easier to archive and share a project in a usable state. Conversely, a disorganised project that only makes sense to its author is difficult to share meaningfully, regardless of whether the files are technically accessible.
Poor project organisation is one of the most common sources of reproducibility failures in practice. Files named analysis_final_v2_ACTUAL.R, data loaded with absolute paths like C:/Users/myname/Desktop/study1/data.csv, and package dependencies that are never documented all make it difficult or impossible for others (or your future self after six months away from the project) to reproduce your work.
The habits covered in this chapter, namely consistent folder structure, relative file paths via here::here(), and package management with renv, are low-cost investments that pay dividends throughout the life of a project and beyond.
1.2 RProjects
Always work inside an RProject (.Rproj). This sets the working directory to the project root, makes here::here() reliable, and keeps your R environment isolated from other projects. Without an RProject, file paths are fragile and environment state can bleed between sessions.
An RProject is simply a folder with a .Rproj file at its root. Opening this file in RStudio or Positron sets the working directory automatically — no setwd() required.
1.2.1 Creating a new RProject
In RStudio: File → New Project → New Directory → New Project. Tick “Use renv with this project” to initialise package management from the start.
In Positron: File → New Folder → initialise as R project.
Get into the habit of always opening your work via the .Rproj file rather than navigating to individual scripts. This ensures the working directory is always set correctly and that here::here() resolves paths from the project root.
1.3 Folder structure
A consistent folder structure makes projects navigable by collaborators and reviewers, and is a prerequisite for FAIR-compliant data sharing (Wilkinson et al., 2016). The exact structure will vary by project, but the following is a reasonable starting point for a research project with data, analysis scripts, and a manuscript:
my-project/
├── my-project.Rproj
├── _quarto.yml # if using Quarto
├── renv.lock # package versions — commit to version control
├── README.md # project overview and setup instructions
├── data/
│ ├── raw/ # original data, never modified
│ └── processed/ # cleaned and anonymised outputs
├── scripts/
│ ├── 01-anonymise.qmd
│ ├── 02-process.qmd
│ ├── 03-eda.qmd
│ └── 04-analysis.qmd
├── manuscript/
│ ├── manuscript.qmd
└── output/
├── figures/
├── tables/
└── models/
The R/ folder contains numbered scripts following a modular analysis workflow (Nagler, 1995): each script has a single responsibility, takes clearly defined inputs, and produces clearly defined outputs. Scripts are numbered to indicate the order in which they should be run. The data/raw/ folder contains the original, unmodified data — this is the source of truth and should never be altered directly.
If your project involves personal, sensitive, or identifiable data (e.g. recordings, transcripts, demographic information), keep the following in mind:
- Never commit raw data to version control. Add
data/raw/to your.gitignorebefore your first commit. This prevents raw data from ever being tracked accidentally. - Never share the project folder (e.g. as a zip, via GitHub, or cloud sync) without first checking what
data/raw/contains. - Pseudonymisation and anonymisation come first. Only once raw data has been processed and (pseudo)anonymised should derived outputs be treated as shareable.
- Archive, then remove. Ideally, raw data lives in
data/raw/only during active processing. Once archived to a secure location, remove it from the project folder.
The data/processed/ folder should contain only anonymised or non-identifiable outputs.
1.4 Project-relative file paths
Absolute file paths like C:/Users/myname/Desktop/study1/data.csv break the moment the project is moved or shared. Use here::here() for all file paths so the project is portable across machines and operating systems:
here::here() builds the file path from the project root — the folder containing the .Rproj file, regardless of where the script is located within the project. This means a script in R/04-analysis.R and a .qmd file in manuscript/ can both use here::here("data", "processed", "data_clean.rds") and resolve to the same file.
Never use setwd() in a script that will be shared or re-run. It hardcodes a path that only works on your machine. here::here() is always the better alternative inside an RProject.
1.5 renv: reproducible package management
renv records the exact version of every package your project uses in a lockfile (renv.lock). Anyone who clones your project and runs renv::restore() gets an identical package environment — no more “works on my machine” failures due to package version differences.
renv::init() # initialise once at project start
renv::snapshot() # update lockfile after installing new packages
renv::restore() # recreate environment from lockfileThe renv.lock file should be committed to version control. The renv/library/ folder should not — it is machine-specific and can be large.
renv solves package version differences between collaborators but has limits over long time horizons. R itself, system libraries, and compiled packages can change in ways that renv cannot fully protect against. Document your R version, operating system, and any system dependencies in your README.md for long-term reproducibility.
1.5.1 An alternative: pacman
If you prefer not to use renv, pacman::p_load() provides a lightweight alternative that installs missing packages automatically on load:
install.packages("pacman") # run once
pacman::p_load(tidyverse, here, broom, gt, kableExtra)pacman::p_load() checks whether each package is installed, installs any that are missing, and loads all of them. This does not lock package versions, so it is less reproducible than renv over time, but it is simpler and sufficient for following along in a workshop setting.
Avoid leaving install.packages() calls uncommented in scripts. They will re-run every time the script is executed or the document is rendered, which is slow and can cause unexpected package version changes.
1.6 Quarto in an RProject
If your project produces a Quarto book or report, place _quarto.yml at the project root. Quarto and RStudio/Positron detect it automatically and use it to configure rendering for the entire project.
For a standalone manuscript or report (not a book), place the .qmd file at the project root alongside the .Rproj file. The YAML header of the .qmd file controls rendering in this case.
A Quarto project and an RProject are complementary but independent. The .Rproj file manages the R environment and working directory; the _quarto.yml file manages document rendering. Both can coexist in the same folder, and together they provide a fully self-contained, reproducible research project.