The Dilemma: One Repository or Two?

When developing a library that targets both the Python and R ecosystems, deciding on your repository architecture is one of the most critical early choices you will make. You might find yourself torn between keeping everything centralized in a monorepo or splitting the implementations into dedicated repositories to appease ecosystem-specific conventions.

Add in subtle naming mismatches—such as Python preferring snake_case (good_name) and R traditionally favoring period or camelCase separation (good.name)—and the decision becomes even trickier. Here is an in-depth breakdown of how industry leaders handle this problem, the technical trade-offs of both approaches, and how to choose the right strategy for your project.

Option 1: The Monorepo Approach

In a monorepo, both language packages live under the same GitHub repository, typically organized into distinct subdirectories:

good-name/\n├── python/\n│   ├── pyproject.toml\n│   └── src/good_name/\n├── r/\n│   ├── DESCRIPTION\n│   ├── NAMESPACE\n│   └── R/\n├── docs/\n└── README.md

When a Monorepo Makes Sense

  • Shared Native Core: If your library is backed by a compiled engine (such as C++, C, or Rust) via tools like pybind11 and Rcpp/extendr, a monorepo is almost mandatory. Projects like Apache Arrow, XGBoost, and DuckDB use this strategy so that changes to the core engine propagate to both language wrappers simultaneously.
  • Synchronized Logic and Algorithms: If changes in API design or algorithms must stay strictly parallel across both languages, a single repository allows you to update both implementations in a single pull request.
  • Centralized Community Hub: Having a single issue tracker and documentation hub reduces fragmentation. Users do not need to hunt across multiple repositories to file bug reports or discuss algorithmic details.

Challenges with the Monorepo

  • Installation Footprint: Installing directly from GitHub requires specifying subdirectories. While modern tooling supports this, it introduces friction:
    • R: remotes::install_github("user/good-name", subdir = "r")
    • Python: pip install git+https://github.com/user/good-name.git#subdirectory=python
  • Release Tagging: If version numbers desynchronize (e.g., you release a quick patch for Python v1.0.1 while R stays on v1.0.0), Git tags can become messy (e.g., python-v1.0.1 vs r-v1.0.0).

Option 2: Separate Repositories (Polyrepo)

In this approach, you maintain two entirely independent repositories: good_name for Python and good.name for R.

When Separate Repositories Make Sense

  • Independent Implementations: If the packages are completely independent reimplementations written natively in pure Python and pure R without a shared compiled backend.
  • Frictionless Ecosystem Tooling: Both CRAN and PyPI have distinct automation and CI/CD pipelines. Standard R packages expect root-level files (DESCRIPTION, NAMESPACE), and tools like devtools::install_github("user/good.name") work immediately out of the box without the subdir argument.
  • Different Maintenance Cycles: If one language sees significantly higher development velocity, separate repositories prevent commit noise from polluting the other ecosystem's history.

Resolving the Naming Difference (good_name vs. good.name)

If you choose a monorepo, standard practice is to use a neutral repository name on GitHub using kebab-case:

github.com/organization/good-name

Inside the subdirectories, adhere strictly to each community's packaging standards:

  • In python/pyproject.toml: declare the package as good_name.
  • In r/DESCRIPTION: declare the package as good.name (or goodname).

Note on R naming: While dots were historically common in R package names (e.g., data.table), the modern tidyverse and CRAN best practices increasingly recommend lower case without dots (e.g., goodname) to prevent confusion with S3 methods (where dots denote generic-method pairings).

The Verdict: Which Should You Choose?

  • Choose a Monorepo if: You have a shared C++/Rust core, want a single source of truth for documentation and cross-language issues, or intend to maintain strict feature parity across both languages.
  • Choose Separate Repositories if: The implementations are native and independent, you have separate maintainers for R and Python, or you prioritize standard zero-config installation directly from GitHub.