diff --git a/pkg_building.Rmd b/pkg_building.Rmd index fd8bf23ad..a0d9a4e2e 100644 --- a/pkg_building.Rmd +++ b/pkg_building.Rmd @@ -18,18 +18,18 @@ We recommend that package developers read Hadley Wickham and Jenny Bryan's thoro ### Naming your package {#naming-your-package} -- We strongly recommend short, descriptive names in lower case. If your package deals with one or more commercial services, please make sure the name does not violate branding guidelines. You can check if your package name is available, informative and not offensive by using the [`pak::pkg_name_check()` function](https://pak.r-lib.org/reference/pkg_name_check.html); also use a search engine as you'd thus see if it's offensive in a language other than English. In particular, do *not* choose a package name that's already used on CRAN or Bioconductor. +- We strongly recommend short, descriptive names in lowercase. If your package deals with one or more commercial services, please make sure the name does not violate branding guidelines. You can check if your package name is available, informative and not offensive by using the [`pak::pkg_name_check()` function](https://pak.r-lib.org/reference/pkg_name_check.html); also use a search engine as you'd thus see if it's offensive in a language other than English. In particular, do *not* choose a package name that's already used on CRAN or Bioconductor. - There is a trade-off between the advantages of a unique package name and a less original package name. - - A more unique package name might be easier to track (for you and us to assess package use for instance, less false positives when typing its name in GitHub code search) and search (for users to ask "how to use package blah" in a search engine). - - On the other hand a *too* unique package name might make the package less discoverable (that is to say, to find it by searching "how to do this-thing in R"). It might be an argument for naming your package something very close to its topic such as [geojson](https://github.com/ropensci/geojson)). + - A more unique package name might be easier to track (for you and us to assess package use for instance, fewer false positives when typing its name in GitHub code search) and search (for users to ask "how to use package blah" in a search engine). + - On the other hand, a *too* unique package name might make the package less discoverable (that is to say, to find it by searching "how to do this-thing in R"). It might be an argument for naming your package something very close to its topic such as [geojson](https://github.com/ropensci/geojson)). - Find other interesting aspects of naming your package [in this blog post by Nick Tierney](https://www.njtierney.com/post/2018/06/20/naming-things/), and in case you change your mind, find out [how to rename your package in this other blog post of Nick's](https://www.njtierney.com/post/2017/10/27/change-pkg-name/). ### Creating metadata for your package {#creating-metadata-for-your-package} -We recommend you to use the [`codemetar` package](https://github.com/ropensci/codemetar) for creating and updating a JSON [CodeMeta](https://codemeta.github.io/) metadata file for your package via `codemetar::write_codemeta()`. It will automatically include all useful information, including [GitHub topics](#grooming). CodeMeta uses [Schema.org terms](https://schema.org/) so as it gains popularity the JSON metadata of your package might be used by third-party services, maybe even search engines. +We recommend you use the [`codemetar` package](https://github.com/ropensci/codemetar) for creating and updating a JSON [CodeMeta](https://codemeta.github.io/) metadata file for your package via `codemetar::write_codemeta()`. It will automatically include all useful information, including [GitHub topics](#grooming). CodeMeta uses [Schema.org terms](https://schema.org/) so as it gains popularity the JSON metadata of your package might be used by third-party services, maybe even search engines. ## Platforms {#platforms} @@ -41,9 +41,9 @@ We recommend you to use the [`codemetar` package](https://github.com/ropensci/co - Functions and arguments naming should be chosen to work together to form a common, logical programming API that is easy to read, and auto-complete. - - Consider an `object_verb()` naming scheme for functions in your package that take a common data type or interact with a common API. `object` refers to the data/API and `verb` the primary action. This scheme helps avoid namespace conflicts with packages that may have similar verbs, and makes code readable and easy to auto-complete. For instance, in **stringi**, functions starting with `stri_` manipulate strings (`stri_join()`, `stri_sort()`, and in **googlesheets** functions starting with `gs_` are calls to the Google Sheets API (`gs_auth()`, `gs_user()`, `gs_download()`). + - Consider an `object_verb()` naming scheme for functions in your package that take a common data type or interact with a common API. `object` refers to the data/API and `verb` the primary action. This scheme helps avoid namespace conflicts with packages that may have similar verbs and makes code readable and easy to auto-complete. For instance, in **stringi**, functions starting with `stri_` manipulate strings (`stri_join()`, `stri_sort()`, and in **googlesheets** functions starting with `gs_` are calls to the Google Sheets API (`gs_auth()`, `gs_user()`, `gs_download()`). -- For functions that manipulate an object/data and return an object/data of the same type, make the object/data the first argument of the function so as to enhance compatibility with the pipe operators (base R's `|>`, magrittr's `%>%`). +- For functions that manipulate an object/data and return an object/data of the same type, make the object/data the first argument of the function to enhance compatibility with the pipe operators (base R's `|>`, magrittr's `%>%`). - We strongly recommend `snake_case` over all other styles unless you are porting over a package that is already in wide use. @@ -51,21 +51,21 @@ We recommend you to use the [`codemetar` package](https://github.com/ropensci/co - Argument naming and order should be consistent across functions that use similar inputs. -- Package functions importing data should not import data to the global environment, but instead must return objects. Assignments to the global environment are to be avoided in general. +- Package functions importing data should not import data to the global environment but instead must return objects. Assignments to the global environment are to be avoided in general. ### Console messages {#console-messages} -- Use either the [cli package](https://cli.r-lib.org/), or base R's tools (`message()` and `warning()`) to communicate with the user in your functions. +- Use either the [cli package](https://cli.r-lib.org/) or base R's tools (`message()` and `warning()`) to communicate with the user in your functions. - Highlights of the cli package include: automatic wrapping, respect of the [NO_COLOR convention](https://cli.r-lib.org/articles/cli-config-user.html?q=no#no_color), many [semantic elements](https://cli.r-lib.org/articles/semantic-cli.html), and extensive documentation. Read more in a [blog post](https://blog.r-hub.io/2023/11/30/cliff-notes-about-cli/). - Please do not use `print()` or `cat()` unless it's for a `print.*()` or `str.*()` methods, as these methods of printing messages are harder for users to suppress. -- Provide a way for users to opt out of verbosity, preferably at the package level: make message creation dependent on an environment variable or option (like ["usethis.quiet"](https://usethis.r-lib.org/reference/ui.html?q=usethis.quiet#silencing-output) in the usethis package), rather than on a function parameter. The control of messages could be on several levels ("none, "inform", "debug") rather than logical (no messages at all / all messages). Control of verbosity is useful for end users but also in tests. More interesting comments can be found in an [issue of the tidyverse design guide](https://github.com/tidyverse/design/issues/42). +- Provide a way for users to opt out of verbosity, preferably at the package level: make message creation dependent on an environment variable or option (like ["usethis.quiet"](https://usethis.r-lib.org/reference/ui.html?q=usethis.quiet#silencing-output) in the usethis package), rather than on a function parameter. The control of messages could be on several levels ("none", "inform", "debug") rather than logical (no messages at all / all messages). Control of verbosity is useful for end users but also in tests. More interesting comments can be found in an [issue of the tidyverse design guide](https://github.com/tidyverse/design/issues/42). ### Interactive/Graphical Interfaces {#interactive-graphical-interfaces} -If providing graphical user interface (GUI) (such as a Shiny app), to facilitate workflow, include a mechanism to automatically reproduce steps taken in the GUI. This could include auto-generation of code to reproduce the same outcomes, output of intermediate values produced in the interactive tool, or simply clear and well-documented mapping between GUI actions and scripted functions. (See also ["Testing"](#testing) below.) +If providing a graphical user interface (GUI) (such as a Shiny app), to facilitate workflow, include a mechanism to automatically reproduce steps taken in the GUI. This could include auto-generation of code to reproduce the same outcomes, the output of intermediate values produced in the interactive tool, or simply clear and well-documented mapping between GUI actions and scripted functions. (See also ["Testing"](#testing) below.) The [`tabulizer` package](https://github.com/ropensci/tabulizer) e.g. has an interactive workflow to extract tables, but can also only extract coordinates so one can re-run things as a script. Besides, two examples of shiny apps that do code generation are [https://gdancik.shinyapps.io/shinyGEO/](https://gdancik.shinyapps.io/shinyGEO/), and [https://github.com/wallaceEcoMod/wallace/](https://github.com/wallaceEcoMod/wallace/). @@ -77,7 +77,7 @@ We recommend your package use a consistent method of your choice for [checking i If your package accesses a web API or another web resource, -- Make sure requests send an [user agent](https://httr2.r-lib.org/articles/wrapping-apis.html#user-agent), that is, a way to identify what (your package) or who sent the request. The users should be able to override the package's default user agent. Ideally the user agent should be different on continuous integration services, and in development (based on, for instance, the GitHub usernames of the developers). +- Make sure requests send an [user agent](https://httr2.r-lib.org/articles/wrapping-apis.html#user-agent), that is, a way to identify what (your package) or who sent the request. The users should be able to override the package's default user agent. Ideally, the user agent should be different on continuous integration services, and in development (based on, for instance, the GitHub usernames of the developers). - You might choose different (better) defaults than the API, in which case you should document them. - Your package should help with pagination, by allowing the users to not worry about it at all since your package does all necessary requests. - Your package should help with rate limiting according to the API rules. @@ -151,11 +151,11 @@ bibentry( - The package name. - Badges for continuous integration and test coverage, the badge for rOpenSci peer-review once it has started (see below), a repostatus.org badge, and any other badges (e.g. [R-universe](https://ropensci.org/blog/2021/10/14/runiverse-badges/)). - - Short description of goals of package (what does it do? why should a potential user care?), with descriptive links to all vignettes unless the package is small and there's only one vignette repeating the README. Please also ensure the vignettes are rendered and readable, see [the "documentation website" section](#website)). + - Short description of the goals of the package (what does it do? Why should a potential user care?), with descriptive links to all vignettes unless the package is small and there's only one vignette repeating the README. Please also ensure the vignettes are rendered and readable, see [the "documentation website" section](#website)). - Installation instructions using e.g. the [remotes package](https://remotes.r-lib.org/), [pak package](https://pak.r-lib.org/), or [R-universe](https://ropensci.org/blog/2021/06/22/setup-runiverse/). - Any additional setup required (authentication tokens, etc). - Brief demonstration usage. - - If applicable, how the package compares to other similar packages and/or how it relates to other packages. + - If applicable, how does the package compare to other similar packages and/or how it relates to other packages. - Citation information i.e. Direct users to the preferred citation in the README by adding boilerplate text "here's how to cite my package". See e.g. [ecmwfr README](https://github.com/bluegreen-labs/ecmwfr#how-to-cite-this-package-in-your-article). If you use another repo status badge such as a [lifecycle](https://www.tidyverse.org/lifecycle/) badge, please also add a [repostatus.org](https://www.repostatus.org/) badge. [Example of a repo README with two repo status badges](https://github.com/ropensci/ijtiff#ijtiff-). @@ -168,12 +168,12 @@ If you use another repo status badge such as a [lifecycle](https://www.tidyverse where issue\_id is the number of the issue in the software-review repository. For instance, the badge for [`rtimicropem`](https://github.com/ropensci/rtimicropem) review uses the number 126 since it's the [review issue number](https://github.com/ropensci/software-review/issues/126). The badge will first indicated "under review" and then "peer-reviewed" once your package has been onboarded (issue labelled "approved" and closed), and will link to the review issue. -- If your README has many badges consider ordering them in an html table to make it easier for newcomers to gather information at a glance. See examples in [`drake` repo](https://github.com/ropensci/drake) and in [`qualtRics` repo](https://github.com/ropensci/qualtRics/). Possible sections are +- If your README has many badges consider ordering them in an HTML table to make it easier for newcomers to gather information at a glance. See examples in [`drake` repo](https://github.com/ropensci/drake) and in [`qualtRics` repo](https://github.com/ropensci/qualtRics/). Possible sections are - Development (CI statuses cf [CI chapter](#ci), Slack channel for discussion, repostatus) - Release/Published ([CRAN version and release date badges from METACRAN](https://www.r-pkg.org/services#badges), [CRAN checks API badge](https://github.com/r-hub/cchecksbadges), Zenodo badge) - Stats/Usage (downloads e.g. [download badges from r-hub/cranlogs](https://github.com/r-hub/cranlogs.app#badges)) - The table should be more wide than it is long in order to mask the rest of the README. + The table should be more wide than it is long to mask the rest of the README. - If your package connects to a data source or online service, or wraps other software, consider that your package README may be the first point of entry for users. It should provide enough information for users to understand the nature of the data, service, or software, and provide links to other relevant data and documentation. For instance, a README should not merely read, "Provides access to GooberDB," but also include, @@ -201,9 +201,9 @@ where issue\_id is the number of the issue in the software-review repository. Fo - The package should contain top-level documentation for `?foobar`, (or ``?`foobar-package` `` if there is a naming conflict). Optionally, you can use both `?foobar` and ``?`foobar-package` `` for the package level manual file, using `@aliases` roxygen tag. [`usethis::use_package_doc()`](https://usethis.r-lib.org/reference/use_package_doc.html) adds the template for the top-level documentation. -- The package should contain at least one **HTML** vignette providing a substantial coverage of package functions, illustrating realistic use cases and how functions are intended to interact. If the package is small, the vignette and the README may have very similar content. +- The package should contain at least one **HTML** vignette providing substantial coverage of package functions, illustrating realistic use cases and how functions are intended to interact. If the package is small, the vignette and the README may have very similar content. -- As is the case for a README, top-level documentation or vignettes may be the first point of entry for users. If your package connects to a data source or online service, or wraps other software, it should provide enough information for users to understand the nature of the data, service, or software, and provide links to other relevant data and documentation. For instance, a vignette intro or documentation should not merely read, "Provides access to GooberDB," but also include, "..., an online repository of Goober sightings in South America. More information about GooberDB, and documentation of database structure and metadata can be found at *link*". Any vignette should outline prerequisite knowledge to be able to understand the vignette upfront. +- As is the case for a README, top-level documentation or vignettes may be the first point of entry for users. If your package connects to a data source or online service or wraps other software, it should provide enough information for users to understand the nature of the data, service, or software, and provide links to other relevant data and documentation. For instance, a vignette intro or documentation should not merely read, "Provides access to GooberDB," but also include, "..., an online repository of Goober sightings in South America. More information about GooberDB, and documentation of database structure and metadata can be found at *link*". Any vignette should outline prerequisite knowledge to be able to understand the vignette upfront. The general vignette should present a series of examples progressing in complexity from basic to advanced usage. @@ -213,7 +213,7 @@ The general vignette should present a series of examples progressing in complexi - The vignette(s) should include citations to software and papers where appropriate. -- If your package provides access to a data source, we require that DESCRIPTION contains both (1) A brief identification and/or description of the organisation responsible for issuing data; and (2) The URL linking to public-facing page providing, describing, or enabling data access (which may often differ from URL leading directly to data source). +- If your package provides access to a data source, we require that DESCRIPTION contains both (1) A brief identification and/or description of the organisation responsible for issuing data; and (2) The URL linking to a public-facing page providing, describing, or enabling data access (which may often differ from URL leading directly to data source). - Only use package startup messages when necessary (function masking for instance). Avoid package startup messages like "This is foobar 2.4-0" or citation guidance because they can be annoying to the user. Rely on documentation for such guidance. @@ -237,7 +237,7 @@ f <- function(a = TRUE) { } ``` -- Documentation should support user navigation by including useful [cross-links](https://roxygen2.r-lib.org/reference/tags-index-crossref.html) between related functions and documenting related functions together in groups or in common help pages. In particular, the `@family` tags, that automatically creates "See also" links and [can help group](https://pkgdown.r-lib.org/reference/build_reference.html) functions together on pkgdown sites, is recommended for this purpose. See [the "manual" section of The R Packages book](https://r-pkgs.org/man.html) and [the "function grouping" section of the present chapter](#function-grouping) for more details. +- Documentation should support user navigation by including useful [cross-links](https://roxygen2.r-lib.org/reference/tags-index-crossref.html) between related functions and documenting related functions together in groups or in common help pages. In particular, the `@family` tag, which automatically creates "See also" links and [can help group](https://pkgdown.r-lib.org/reference/build_reference.html) functions together on pkgdown sites, is recommended for this purpose. See [the "manual" section of The R Packages book](https://r-pkgs.org/man.html) and [the "function grouping" section of the present chapter](#function-grouping) for more details. - You can re-use documentation pieces (e.g. details about authentication, related packages) across the vignettes/README/man pages. Refer to [roxygen2 vignette on documentation reuse](https://roxygen2.r-lib.org/articles/reuse.html). @@ -262,11 +262,11 @@ There are a few elements we'd like to underline here. ### Automatic deployment of the documentation website {#docsropensci} -You only need to worry about automatic deployment of your website until approval and transfer of your package repo to the ropensci organization; indeed, after that a pkgdown website will be built for your package after each push to the GitHub repo. You can find the status of these builds at `https://dev.ropensci.org/job/package_name`, e.g. [for `magick`](https://dev.ropensci.org/job/magick); and the website at `https://docs.ropensci.org/package_name`, e.g. [for `magick`](https://docs.ropensci.org/magick). The website build will use your pkgdown config file if you have one, except for the styling that will use the [`rotemplate` package](https://github.com/ropensci-org/rotemplate/). The resulting website will have a local search bar. Please report bugs, questions and feature requests about the central builds at [https://github.com/ropensci/docs/](https://github.com/ropensci/docs/) and about the template at [https://github.com/ropensci/rotemplate/](https://github.com/ropensci/rotemplate/). +You only need to worry about automatic deployment of your website until approval and transfer of your package repo to the rOpenSci organization; indeed, after that, a pkgdown website will be built for your package after each push to the GitHub repo. You can find the status of these builds at `https://dev.ropensci.org/job/package_name`, e.g. [for `magick`](https://dev.ropensci.org/job/magick); and the website at `https://docs.ropensci.org/package_name`, e.g. [for `magick`](https://docs.ropensci.org/magick). The website build will use your pkgdown config file if you have one, except for the styling that will use the [`rotemplate` package](https://github.com/ropensci-org/rotemplate/). The resulting website will have a local search bar. Please report bugs, questions and feature requests about the central builds at [https://github.com/ropensci/docs/](https://github.com/ropensci/docs/) and about the template at [https://github.com/ropensci/rotemplate/](https://github.com/ropensci/rotemplate/). *If your package vignettes need credentials (API keys, tokens, etc.) to knit, you might want to [precompute them](https://ropensci.org/technotes/2019/12/08/precompute-vignettes/) since credentials cannot be used on the docs server.* -Before submission and before transfer, you could use the [approach documented by `pkgdown`](https://pkgdown.r-lib.org/reference/deploy_site_github.html) or the [`tic` package](https://docs.ropensci.org/tic/) for automatic deployment of the package's website. This would save you the hassle of running (and remembering to run) `pkgdown::build_site()` yourself every time the site needs to be updated. First refer to our [chapter on continuous integration](#ci) if you're not familiar with continuous integration. In any case, do not forget to update all occurrences of the website URL after transfer to the ropensci organization. +Before submission and before transfer, you could use the [approach documented by `pkgdown`](https://pkgdown.r-lib.org/reference/deploy_site_github.html) or the [`tic` package](https://docs.ropensci.org/tic/) for automatic deployment of the package's website. This would save you the hassle of running (and remembering to run) `pkgdown::build_site()` yourself every time the site needs to be updated. First, refer to our [chapter on continuous integration](#ci) if you're not familiar with continuous integration. In any case, do not forget to update all occurrences of the website URL after transfer to the rOpenSci organization. ### Grouping functions in the reference {#function-grouping} @@ -293,8 +293,8 @@ Our template is compatible with this configuration. ### Package logo {#package-logo} -To use your package logo in the pkgdown homepage, refer to [`usethis::use_logo()`](https://usethis.r-lib.org/reference/use_logo.html). -If your package doesn't have any logo, the [rOpenSci docs builder](#docsropensci) will use rOpenSci logo instead. +To use your package logo on the pkgdown homepage, refer to [`usethis::use_logo()`](https://usethis.r-lib.org/reference/use_logo.html). +If your package doesn't have any logo, the [rOpenSci docs builder](#docsropensci) will use the rOpenSci logo instead. ## Authorship {#authorship} @@ -313,9 +313,9 @@ Many packages include code from other software. Whether entire files or single f > The ownership of copyright and intellectual property rights of all components of the package must be clear and unambiguous (including from the authors specification in the DESCRIPTION file). Where code is copied (or derived) from the work of others (including from R itself), care must be taken that any copyright/license statements are preserved and authorship is not misrepresented. > -> Preferably, an ‘Authors@R' field would be used with ‘ctb' roles for the authors of such code. Alternatively, the ‘Author' field should list these authors as contributors. +> Preferably, an 'Authors@R' field would be used with 'ctb' roles for the authors of such code. Alternatively, the ‘Author' field should list these authors as contributors. > -> Where copyrights are held by an entity other than the package authors, this should preferably be indicated via ‘cph' roles in the ‘Authors@R' field, or using a ‘Copyright' field (if necessary referring to an inst/COPYRIGHTS file). +> Where copyrights are held by an entity other than the package authors, this should preferably be indicated via 'cph' roles in the 'Authors@R' field, or using a 'Copyright' field (if necessary referring to an inst/COPYRIGHTS file). > > Trademarks must be respected. @@ -328,11 +328,11 @@ For more explanations around licensing, refer to the [R packages book](https://r - All packages should pass `R CMD check`/`devtools::check()` on all major platforms. -- All packages should have a test suite that covers major functionality of the package. The tests should also cover the behavior of the package in case of errors. +- All packages should have a test suite that covers the major functionality of the package. The tests should also cover the behavior of the package in case of errors. - It is good practice to write unit tests for all functions, and all package code in general, ensuring key functionality is covered. Test coverage below 75% will likely require additional tests or explanation before being sent for review. -- We recommend using [testthat](https://testthat.r-lib.org/) for writing tests. Strive to write tests as you write each new function. This serves the obvious need to have proper testing for the package, but allows you to think about various ways in which a function can fail, and to *defensively* code against those. [More information](https://r-pkgs.org/tests.html). +- We recommend using [testthat](https://testthat.r-lib.org/) for writing tests. Strive to write tests as you write each new function. This serves the obvious need to have proper testing for the package but allows you to think about various ways in which a function can fail, and to *defensively* code against those. [More information](https://r-pkgs.org/tests.html). - Tests should be easy to understand. We suggest reading the blog post [*"Why Good Developers Write Bad Unit Tests"*](https://mtlynch.io/good-developers-bad-tests/) by Michael Lynch. @@ -353,7 +353,7 @@ For more explanations around licensing, refer to the [R packages book](https://r - Once you've set up [continuous integration (CI)](#ci), use your package's code coverage report (cf [this section of our book](#coverage)) to identify untested lines, and to add further tests. -- Even if you use [continuous integration](#ci), we recommend that you run tests locally prior to submitting your package (you might need to set `Sys.setenv(NOT_CRAN="true")`). +- Even if you use [continuous integration](#ci), we recommend that you run tests locally before submitting your package (you might need to set `Sys.setenv(NOT_CRAN="true")`). ## Examples {#examples} @@ -361,63 +361,40 @@ For more explanations around licensing, refer to the [R packages book](https://r - You can run examples with `devtools::run_examples()`. Note that when you run R CMD CHECK or equivalent (e.g., `devtools::check()`) your examples that are not wrapped in `\dontrun{}` or `\donttest{}` are run. Refer to the [summary table](https://roxygen2.r-lib.org/articles/rd.html#functions) in roxygen2 docs. -- To safe-guard examples (e.g. requiring authentication) to be run on CRAN you need to use `\dontrun{}`. However, for a first submission CRAN won't let you have all examples escaped so. In this case you might add some small toy examples, or wrap example code in `try()`. Also refer to the `@exampleIf` tag present, at the time of writing, in roxygen2 development version. +- To safeguard examples (e.g. requiring authentication) to be run on CRAN you need to use `\dontrun{}`. However, for a first submission, CRAN won't let you have all examples escaped. In this case, you might add some small toy examples, or wrap the example code in `try()`. Also refer to the `@exampleIf` tag present, at the time of writing, in the roxygen2 development version. - In addition to running examples locally on your own computer, we strongly advise that you run examples on one of the [continuous integration systems](#ci). Again, examples that are not wrapped in `\dontrun{}` or `\donttest{}` will be run, but for those that are you can configure your continuous integration builds to run them via R CMD check arguments `--run-dontrun` and/or `--run-donttest`. ## Package dependencies {#pkgdependencies} -- Consider the trade-offs involved in relying on a package as a dependency. On one hand, - using dependencies reduces coding effort, and can build on useful functionality developed by - others, especially if the dependency performs complex tasks, is high-performance, - and/or is well vetted and tested. On the other hand, having many dependencies - places a burden on the maintainer to keep up with changes in those packages, at risk - to your package's long-term sustainability. It also - increases installation time and size, primarily a consideration on your and others' development cycle, and in automated build systems. "Heavy" packages - those with many dependencies themselves, and those with large amounts of compiled code - increase this cost. Here are some approaches to reducing - dependencies: +- Consider the trade-offs involved in relying on a package as a dependency. On one hand, using dependencies reduces coding effort, and can build on useful functionality developed by others, especially if the dependency performs complex tasks, is high-performance, and/or is well vetted and tested. On the other hand, having many dependencies places a burden on the maintainer to keep up with changes in those packages, at risk to your package's long-term sustainability. It also increases installation time and size, primarily a consideration in your and others' development cycle, and automated build systems. "Heavy" packages - those with many dependencies themselves, and those with large amounts of compiled code - increase this cost. Here are some approaches to reducing dependencies: - - Small, simple functions from a dependency package may be better copied into - your own package if the dependency if you are using only a few functions - in an otherwise large or heavy dependency. (See [*Authorship* section - above](#authorship-included-code) for how to acknowledge original authors - of copied code.) On the other hand, complex functions with many edge - cases (e.g. parsers) require considerable testing and vetting. - - - An common example of this is in returning tidyverse-style "tibbles" from package - functions that provide data. - One can avoid the modestly heavy **tibble** package dependency by returning - a tibble created by modifying a data frame like so: - - ``` + - Small, simple functions from a dependency package may be better copied into your own package if you are using only a few functions in an otherwise large or heavy dependency. (See [*Authorship* section above](#authorship-included-code) for how to original authors of copied code.) On the other hand, complex functions with many edge cases (e.g. parsers) require considerable testing and vetting. + - A common example of this is in returning tidyverse-style "tibbles" from package functions that provide data. One can avoid the modestly heavy **tibble** package dependency by returning a tibble created by modifying a data frame like so: + + ```r class(df) <- c("tbl_df", "tbl", "data.frame") ``` - (Note that this approach is [not universally endorsed](https://twitter.com/krlmlr/status/1067856118385381377).) + (Note that this approach is [not universally endorsed](https://x.com/krlmlr/status/1067856118385381377).) - - Ensure that you are using the package where the function is defined, - rather than one where it is re-exported. For instance many functions in **devtools** can be found in smaller specialty packages such as **sessioninfo**. The `%>%` function - should be imported from **magrittr**, where it is defined, rather than the heavier - **dplyr**, which re-exports it. + - Ensure that you are using the package where the function is defined, rather than one where it is re-exported. For instance many functions in **devtools** can be found in smaller specialty packages such as **sessioninfo**. The `%>%` function should be imported from **magrittr**, where it is defined, rather than the heavier **dplyr**, which re-exports it. - - Some dependencies are preferred because they provide easier to interpret - function names and syntax than base R solutions. If this is the primary - reason for using a function in a heavy dependency, consider wrapping - the base R approach in a nicely-named internal function in your package. See e.g. the [rlang R script providing functions with a syntax similar to purrr functions](https://github.com/r-lib/rlang/blob/9b50b7a86698332820155c268ad15bc1ed71cc03/R/standalone-purrr.R). + - Some dependencies are preferred because they provide easier-to-interpret function names and syntax than base R solutions. If this is the primary reason for using a function in a heavy dependency, consider wrapping the base R approach in a nicely named internal function in your package. See the [rlang R script providing functions with a syntax similar to purrr functions](https://github.com/r-lib/rlang/blob/9b50b7a86698332820155c268ad15bc1ed71cc03/R/standalone-purrr.R). - If dependencies have overlapping functionality, see if you can rely on only one. - - More dependency-management tips can be found in the chapter ["Dependencies: Mindset and Background" of the R packages book](https://r-pkgs.org/dependencies-mindset-background.html) and in a [post by - Scott Chamberlain](https://recology.info/2018/10/limiting-dependencies/). + - More dependency-management tips can be found in the chapter ["Dependencies: Mindset and Background" of the R packages book](https://r-pkgs.org/dependencies-mindset-background.html) and in a [post by Scott Chamberlain](https://recology.info/2018/10/limiting-dependencies/). - Use `Imports` instead of `Depends` for packages providing functions from other packages. Make sure to list packages used for testing (`testthat`), and documentation (`knitr`, roxygen2) in your `Suggests` section of package dependencies (if you use `usethis` for adding testing infrastructure via [`usethis::use_testthat()`](https://usethis.r-lib.org/reference/use_testthat.html) or a vignette via [usethis::use\_vignette()](https://usethis.r-lib.org/reference/use_vignette.html), the necessary packages will be added to `DESCRIPTION`). If you use any package in the examples or tests of your package, make sure to list it in `Suggests`, if not already listed in `Imports`. -- If your (not Bioconductor) package depends on Bioconductor packages, make sure the installation instructions in the README and vignette are clear enough even for an user who is not familiar with the Bioconductor release cycle. +- If your (not Bioconductor) package depends on Bioconductor packages, make sure the installation instructions in the README and vignette are clear enough even for a user who is not familiar with the Bioconductor release cycle. - Should the user use [`BiocManager`](https://www.bioconductor.org/install/index.html#why-biocmanagerinstall) (recommended)? Document this. - Is the automatic installation of Bioconductor packages by `install.packages()` enough? In that case, mention that the user needs to run `setRepositories()` if they haven't set the necessary Bioconductor repositories yet. - - If your package depends on Bioconductor after a certain version, mention it in DESCRIPTION and in the installation instructions. + - If your package depends on Bioconductor after a certain version, mention it in DESCRIPTION and the installation instructions. - Specifying minimum dependencies (e.g. `glue (>= 1.3.0)` instead of just `glue`) should be a conscious choice. If you know for a fact that your package will break below a certain dependency version, specify it explicitly. But if you don't, then no need to specify a minimum dependency. In that case when a user reports a bug which is explicitly related to an older version of a dependency then address it then. @@ -438,7 +415,7 @@ For more explanations around licensing, refer to the [R packages book](https://r ## Recommended scaffolding {#recommended-scaffolding} -- For HTTP requests we recommend using [httr2](https://httr2.r-lib.org), [httr](https://httr.r-lib.org), [curl](https://jeroen.r-universe.dev/curl#), or [crul](http://docs.ropensci.org/crul/) over [RCurl](https://cran.rstudio.com/web/packages/RCurl/). If you like low level clients for HTTP, curl is best, whereas httr2, httr and crul are better for higher level access. +- For HTTP requests we recommend using [httr2](https://httr2.r-lib.org), [httr](https://httr.r-lib.org), [curl](https://jeroen.r-universe.dev/curl#), or [crul](http://docs.ropensci.org/crul/) over [RCurl](https://cran.rstudio.com/web/packages/RCurl/). If you like low-level clients for HTTP, curl is best, whereas httr2, httr and crul are better for higher-level access. - For parsing JSON, use [jsonlite](https://github.com/jeroen/jsonlite) instead of [rjson](https://cran.rstudio.com/web/packages/rjson/) or [RJSONIO](https://cran.rstudio.com/web/packages/RJSONIO/).