R Programming • Software Craft and Communication

Naming & Formatting — Use meaningful variable names and consistent formatting

Well-chosen names and consistent style transform opaque scripts into readable, maintainable, and collaborative R code.

A statistical scatter plot with code braces and a variable symbol for R programming.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

The question of how to name variables and structure source code is nearly as old as programming itself. In the earliest days of computing, memory constraints forced programmers to use cryptic single-character identifiers—registers were labeled A, X, or i not because those names were meaningful, but because every byte mattered. As languages evolved and teams grew, however, the cost of reading code began to exceed the cost of writing it. Research in software engineering consistently shows that developers spend roughly 70% of their time comprehending existing code, making readability a first-class engineering concern rather than an aesthetic preference.

R, originally designed by Ross Ihaka and Robert Gentleman at the University of Auckland in 1993, inherited much of its syntax from S, a language created at Bell Labs in the 1970s. Early S and R scripts were often short, exploratory, and written by a single statistician. As the R ecosystem expanded into bioinformatics, finance, machine learning, and reproducible research, the need for disciplined naming conventions and formatting standards became urgent. The timeline below traces the key milestones that shaped modern R style.

1976 • S Language at Bell Labs

John Chambers creates S, establishing the dot-separated naming convention (e.g., data.frame) that persists in base R to this day.

1993 • R Is Born

Ihaka and Gentleman release R, inheriting S's flexible but inconsistent naming culture—dots, underscores, and camelCase all coexist without guidance.

2005 • Google's R Style Guide

Google publishes one of the first formal R style guides, recommending camelCase for variables and PascalCase for functions, sparking community debate about standardization.

2014 • Tidyverse Style Guide

Hadley Wickham codifies the tidyverse style guide, advocating snake_case for all identifiers and two-space indentation, which becomes the de facto community standard.

2020 • styler & lintr Maturity

Automated tools like styler and lintr reach production quality, enabling CI pipelines to enforce formatting rules automatically.

The central question this lesson addresses is deceptively simple: How should you name things in R, and how should you lay out your code on the page? The answer draws on decades of software engineering research, cognitive psychology (specifically how the brain parses symbolic text), and hard-won community consensus. Getting this right will make every project you touch—from a homework script to a production Shiny application—dramatically easier to write, debug, and extend.

SECTION 2

Core Principles of Naming & Formatting

Good naming and formatting rest on a handful of principles that recur across every style guide and every language. These are not arbitrary rules; they derive from how human cognition processes structured text. The conceptual grid below distills the core ideas into four pillars that, taken together, form a complete framework for clean R code.

1

Intention-Revealing Names

A name should answer three questions without requiring the reader to look elsewhere: What does it hold? Why does it exist? How is it used? Replace d with elapsed_days.
2

Consistent Casing Convention

Pick one casing style—preferably snake_case—and apply it everywhere. Mixing camelCase, dot.case, and snake_case within one script forces readers to context-switch constantly.
3

Structural Whitespace

Indentation, blank lines, and spaces around operators are not decoration—they encode the logical structure of your code the way paragraphs and headings structure prose. Two-space indentation is the R community standard.
4

Scope-Proportional Length

Short-lived loop counters may use short names like i; long-lived data frames deserve longer, descriptive names like patient_demographics. The name's length should be proportional to its scope and significance.
✦

KEY TAKEAWAY

Think of variable names as labels on filing-cabinet drawers. If every drawer is labeled "stuff," finding a specific document requires opening each one. If drawers are labeled "2024 Tax Returns" or "Health Insurance Claims," you can locate what you need in seconds. Code formatting is the equivalent of organizing those drawers into logical rows and columns instead of scattering them across a warehouse floor. Names communicate what; formatting communicates structure.

SECTION 3

Visual Explanation — Good vs. Bad Naming

The diagram below presents two versions of the same R code snippet side by side: the left panel uses vague, inconsistently formatted identifiers, while the right panel applies the principles from Section 2. Color-coded annotations highlight exactly which rules are violated or satisfied. Study the contrast—it captures the essence of why naming and formatting matter.

Side-by-Side: Poor vs. Clean R Code✗ POOR NAMING & FORMATTINGd <- read.csv("data.csv")x=d$col3y=d$col7m<-lm(y~x)s=summary(m)p=s$coefficients[2,4]if(p<.05){cat("sig")}→ "d"? "x"?→ no spaces→ magic indexProblems: cryptic names, inconsistentspacing, no indentation, magic numbers,mixed assignment operators (= vs <-).✓ CLEAN NAMING & FORMATTINGsurvey_data <- read.csv("data.csv")income <- survey_data$annual_incomesatisfaction <- survey_data$job_scorefit_income <- lm( satisfaction ~ income)fit_summary <- summary(fit_income)p_value <- fit_summary$coefficients[ "income", "Pr(>|t|)"]if (p_value < 0.05) { cat("Statistically significant")}Improvements: descriptive snake_case names,consistent <- operator, spaces around ops,2-space indent, named indexing, clear output.
The left panel demonstrates common anti-patterns: single-letter names, missing whitespace, inconsistent assignment operators, and magic numeric indices. The right panel applies snake_case naming, structural whitespace, and named indexing so the reader can understand the analytical intent without any comments.

Notice that the clean version is longer in terms of character count, yet it is faster to read. This is the central paradox of good style: investing a few extra keystrokes at write time saves orders of magnitude more time at read time. The right panel is essentially self-documenting—you can determine the analysis (a linear regression of job satisfaction on income, followed by a significance test) without a single comment.

SECTION 4

How R Naming Conventions Work in Practice

R Identifier Rules

At the language level, R imposes minimal syntactic constraints on identifiers. A valid R name must start with a letter or a dot (if the dot is not followed by a digit), and may contain letters, digits, dots, and underscores. This permissiveness is precisely why stylistic discipline matters—the language won't save you from naming a data frame xx2. Understanding the interaction between R's syntactic rules and community conventions is essential for writing idiomatic code.

The Three Major Casing Conventions in R

ConventionExampleWhere You See ItRecommendation
snake_casepatient_ageTidyverse, most modern packagesPreferred
dot.casepatient.ageBase R (legacy from S)Avoid — dots conflict with S3 method dispatch
camelCasepatientAgeBioconductor, some Shiny appsAcceptable within ecosystems that mandate it

The three naming conventions encountered in R and their typical usage contexts.

Formatting Rules — The Tidyverse Standard

  • ✓ Indentation: Use two spaces per nesting level. Never use tabs—R's default editor uses spaces, and mixing tabs and spaces causes alignment bugs.
  • ✓ Line length: Keep lines under 80 characters. For long function calls, break after each argument and align on the opening parenthesis.
  • ✓ Spacing: Place spaces around binary operators (<-, +, ==), after commas, and before opening curly braces. No space before parentheses in function calls.
  • ✓ Assignment: Use <- for assignment, not =. Reserve = for named arguments inside function calls.
  • ✓ Curly braces: Opening brace on the same line as the control keyword; closing brace on its own line, aligned with the keyword.
⚠

Why Dots Are Dangerous in R

In R, S3 method dispatch uses dots as separators: print.data.frame means "the print method for class data.frame." If you name a variable model.fit, R could confuse it with an S3 method called model for class fit. Using model_fit eliminates this ambiguity entirely.

SECTION 5

Taxonomy of Names — What to Call What

Different kinds of R objects call for different naming strategies. A function should usually contain a verb because it does something; a data frame should contain a noun because it is something. The diagram below maps the major R object types to their recommended naming patterns, with concrete examples for each category.

R Object Naming TaxonomyR IdentifiersDATA (Nouns)patient_recordsmonthly_revenuegene_expression_matrixdata frames, vectors, matricesFUNCTIONS (Verbs)calculate_bmiparse_log_filevalidate_inputverb_noun patternBOOLEANS (Predicates)is_validhas_missing_valuesshould_normalizeis_, has_, can_, should_CONSTANTSMAX_ITERATIONSDEFAULT_ALPHAPARAMETERSn_clusterslearning_rateLOOP COUNTERSi, j, kshort scope → short name OKRule of Thumb: Name length ∝ Scope sizeTiny scope → short name | Module-wide scope → long, descriptive name
The naming taxonomy organizes R identifiers into six categories: data objects (nouns), functions (verbs), booleans (predicates), constants (SCREAMING_SNAKE_CASE), parameters, and loop counters. The bottom banner reinforces the scope-proportional length principle.

Two patterns in this taxonomy deserve special emphasis. First, the verb_noun pattern for functions (calculate_bmi, parse_log_file) immediately tells the reader what action the function performs and what it operates on. Second, boolean prefixes like is_, has_, and should_ make conditional logic read almost like English: if (has_missing_values) { impute_data(df) } is far clearer than if (flag) { f(d) }.

SECTION 6

Worked Example — Refactoring a Messy Script

Below, we take a realistic but poorly written R script and systematically apply naming and formatting rules. The original script loads a CSV of student grades, computes class statistics, and identifies students eligible for honors. Each refactoring step targets a specific principle.

Refactoring a Grade Analysis Script

  1. 1

    Step 1 — Identify the Original Code

    The original script reads: d=read.csv("g.csv"); m=mean(d$c3); s=sd(d$c3); h=d[d$c3>90,]; cat(nrow(h)). Every identifier is a single letter, there are no spaces, the assignment uses = instead of <-, and the column c3 is meaningless without documentation.
    Violations: cryptic names, no whitespace, wrong assignment operator, magic column reference.
  2. 2

    Step 2 — Apply Intention-Revealing Names

    Replace every single-letter identifier with a descriptive snake_case name. d becomes grade_data; m becomes mean_final_score; s becomes sd_final_score; h becomes honors_students; and the column c3 is renamed (or referenced as) final_score.
    Every name now answers "what is this?" without additional context.
  3. 3

    Step 3 — Fix Assignment and Spacing

    Replace = with <- for all variable assignments. Add spaces around every binary operator and after every comma. Separate the semicolon-chained statements onto individual lines for clarity.
    Consistent <- operator; one statement per line.
  4. 4

    Step 4 — Extract the Magic Number

    The threshold 90 is a magic number—its meaning is invisible. Extract it into a named constant: HONORS_THRESHOLD <- 90. Now the filtering line reads honors_students <- grade_data[grade_data$final_score > HONORS_THRESHOLD, ], which is self-explanatory.
    Magic numbers replaced with a named constant in SCREAMING_SNAKE_CASE.
  5. 5

    Step 5 — Compose the Final Refactored Script

    The final script reads: HONORS_THRESHOLD <- 90 grade_data <- read.csv("grades.csv") mean_final_score <- mean(grade_data$final_score) sd_final_score <- sd(grade_data$final_score) honors_students <- grade_data[grade_data$final_score > HONORS_THRESHOLD, ] cat("Honors count:", nrow(honors_students), "\n") Every identifier is meaningful, formatting is consistent, and no external documentation is needed to understand the script's purpose.
    Clean, self-documenting R script ready for collaboration and version control.

SECTION 7

Strengths, Limitations, and Common Pitfalls

Meaningful naming and consistent formatting provide enormous benefits, but they are not without tradeoffs. Understanding these limitations prevents dogmatic over-application and helps you make pragmatic decisions in real codebases.

AspectStrengthsLimitations / Pitfalls
ReadabilityDescriptive names let new team members onboard quickly; code reviews become substantive rather than deciphering exercises.Excessively long names (e.g., the_mean_of_all_final_exam_scores_for_spring_2024) hinder readability in complex expressions.
ConsistencyA uniform style reduces cognitive load, since the reader's eye learns one visual pattern and can parse structure automatically.When integrating base R (dot.case) with tidyverse (snake_case) in the same project, perfect consistency is impossible—pragmatic boundaries are needed.
ToolingTools like styler and lintr automate formatting, removing human error from whitespace and indentation.Automated formatters cannot assess the semantic quality of names—lintr will accept my_var without complaint.
Team CollaborationShared style guides minimize merge conflicts and reduce friction in pull request reviews.Enforcing style on legacy codebases can create massive diffs that obscure substantive changes; incremental adoption is safer.
PerformanceNaming and formatting have zero runtime cost—R's interpreter ignores whitespace and name length.No limitations here. This is purely a developer-time investment with no runtime penalty.

Strengths and limitations of disciplined naming and formatting practices.

✦

KEY TAKEAWAY

Good naming is an act of API design—even for scripts only you will read. Six months from now, you are a different programmer, and your past code is indistinguishable from a stranger's. Formatting is the visual grammar that lets both present-you and future-you parse that code's intent. Think of clean style not as overhead but as communication infrastructure that pays compound interest over the lifetime of a codebase.

SECTION 8

Connection to Advanced Practices — Linting, CI, and Package Development

The naming and formatting principles covered in this lesson are the foundation upon which more advanced software engineering practices are built. As you progress from standalone scripts to R packages, Shiny applications, and collaborative research repositories, the stakes increase and the tooling becomes more sophisticated. The table below maps the concepts from this lesson to their advanced counterparts.

This LessonAdvanced PracticeTool / Resource
Manual snake_case namingAutomated lint checks in CI pipelines using GitHub Actionslintr::lint_package()
Manual indentation and spacingAuto-formatting on save via IDE integration or pre-commit hooksstyler::style_pkg()
Descriptive function namesFormal API design with roxygen2 documentation and namespace exportsroxygen2, devtools
Consistent style within a scriptProject-wide style guides enforced by .lintr config files and code review policies.lintr YAML config
Named constantsConfiguration management via environment variables or config packagesconfig, dotenv

From script-level discipline to production-grade tooling.

In professional R package development, CRAN's R CMD check does not enforce naming conventions, but virtually all high-quality packages on CRAN and Bioconductor follow either the tidyverse or Bioconductor style guide. Mastering the basics now will prepare you for the expectations of open-source contribution and industry R development, where code that doesn't meet style standards is routinely rejected in review regardless of its functional correctness.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain why the tidyverse style guide recommends snake_case over dot.case for R identifiers, even though dot.case has a long history in the language. What specific technical issue can arise from using dots in names?
Show answer
The dot character serves double duty in R: it is both a legal character in identifiers and the separator used by the S3 object-oriented dispatch system. When R encounters a call like print(x), it looks for a method named print.<class_of_x>. If you name a variable data.table, it becomes ambiguous whether you mean a variable called "data.table" or the data method for class table. Using underscores (data_table) eliminates this ambiguity entirely, since underscores have no special dispatch semantics in R.

Question 2: BASIC

Rewrite the following line of R code to comply with tidyverse naming and formatting conventions: avgTemp=mean(weatherData$T,na.rm=TRUE)
Show answer
The corrected version is: avg_temp <- mean(weather_data$temperature, na.rm = TRUE). Three changes were applied. First, avgTemp (camelCase) becomes avg_temp (snake_case). Second, the assignment operator changes from = to <- with surrounding spaces. Third, weatherData$T is replaced with a descriptive snake_case data frame name and a meaningful column name, and a space is added after the comma separating arguments.

Question 3: INTERMEDIATE

Consider this function definition. Identify all naming and formatting violations, then produce a corrected version: calcBMI <- function(w,h){ w/(h^2) }
Show answer
Violations: (1) The function name calcBMI uses camelCase instead of snake_case. (2) Parameters w and h are cryptic single letters—they should be weight_kg and height_m (including units in the name). (3) No space after the comma between parameters. (4) No space before the opening brace. (5) The body should be on its own indented line. Corrected: calculate_bmi <- function(weight_kg, height_m) { weight_kg / (height_m ^ 2) }. Note how including units in parameter names eliminates a common source of bugs—the caller immediately knows to pass kilograms and meters, not pounds and inches.

Question 4: APPLIED

You inherit a Shiny application with the following server-side code fragment. Refactor it for readability and explain your decisions: output$p1 <- renderPlot({ d <- filteredData(); plot(d$x, d$y, col=d$g, pch=16, main="") })
Show answer
A well-refactored version might read: output$scatter_plot <- renderPlot({ filtered_df <- filtered_data() plot( x = filtered_df$longitude, y = filtered_df$latitude, col = filtered_df$region_color, pch = 16, main = "" ) }) Key decisions: (1) p1 was renamed to scatter_plot so the UI binding is self-documenting. (2) The reactive expression filteredData was converted to snake_case. (3) The plot() call was broken across multiple lines with named arguments for readability. (4) Column references were given meaningful domain names. In a Shiny app, these output IDs are referenced in the UI—descriptive IDs prevent mismatched bindings.

Question 5: CRITICAL THINKING

A colleague argues that enforcing a strict style guide on an R data analysis project is unnecessary overhead, since "data science scripts are disposable—they run once, produce a figure, and never need maintenance." Construct a detailed counter-argument drawing on at least three principles from this lesson. Under what narrow circumstances, if any, might your colleague's position be defensible?
Show answer
Three counter-arguments: (1) Reproducibility: In scientific computing, scripts are not disposable—they are the computational record of an analysis. Reviewers, collaborators, and future-you must be able to read and rerun them months or years later. Cryptic names make auditing impossible. (2) Debugging efficiency: Even "one-off" scripts frequently fail and require debugging. When a pipeline breaks at 2 AM before a deadline, the ability to read your own code quickly is worth every second invested in formatting. (3) Habit formation: Style is a muscle. If you practice sloppy naming on "disposable" scripts, sloppy naming will be your default when you write production code. The marginal cost of good naming at write time is near zero once the habit is internalized. The colleague's position might be defensible only in truly ephemeral interactive console sessions—typing x <- 5; y <- x + 3 at the REPL to test arithmetic does not warrant base_operand <- 5. But any code saved to a file is, by definition, intended to persist, and therefore merits clean style.

SUMMARY

Lesson Summary

Summary

This lesson established that meaningful variable names and consistent formatting are foundational skills in R programming, not optional embellishments. We traced the evolution from S's permissive dot-case conventions through the emergence of the tidyverse style guide and modern automated tools like styler and lintr. The four core principles—intention-revealing names, consistent casing, structural whitespace, and scope-proportional length—provide a complete framework for writing clean, self-documenting R code.

We examined the naming taxonomy for R objects (nouns for data, verbs for functions, predicates for booleans, SCREAMING_SNAKE_CASE for constants), walked through a complete refactoring example, and connected these practices to advanced topics like CI-based lint enforcement and R package development. The overarching lesson is that clean code is not slower to write—it is faster to read, debug, and extend, and those activities dominate the lifecycle of every nontrivial project.

Varsity Tutors • R Programming • Naming & Formatting — Use meaningful variable names and consistent formatting