ESS is great. ESS’s integration with the R package devtools makes it a greater tool. By default, the integration isn’t activated. One needs to explicitly activate ess-r-package.
(use-package ess
:config
(require 'ess-r-mode)
(require 'ess-r-package))
Now, I am ready to create my newest R package: M-x ess-r-devtools-create-package

Well, I’ve created an R package! There are many operations one can do, just run M-x ess-r-devtools-execute-command.

During the package development cycle, I need to do the following very frequently (actually, I wanted to say extremely frequently to show that I am super prudent): run the unit tests, check the package, refresh the documentation, and above all, load the package.
Actually, all of the above have their own emacs command: ess-r-devtools-test-package, ess-r-devtools-check-package 1, ess-r-devtools-document-package, ess-r-devtools-load-package. They have key bindings too, but I can never remember them. At the same time, these commands are too long, even with helm it is difficult to find them quickly. My strategy is to create some aliases:
(defalias 'lp 'ess-r-devtools-load-package)
(defalias 'lt 'ess-r-devtools-test-package)
(defalias 'lc 'ess-r-devtools-check-package)
(defalias 'ld 'ess-r-devtools-document-package)
So that I can run them pretty easily, e.g. M-x lp.
The default ess-r-package-auto-enable-namespaced-evaluation as t is kind of annoying. By default, after I M-x lp a package foo containing a function bar; I can’t directly run bar(). Instead, I need to explicitly run foo::bar(). This isn’t the default behavior of many R IDEs, e.g. RStudio.
I set the mentioned variable to nil instead. BTW, I did that multiple times already, maybe it deserves an explanation. A Boolean value in Emacs lisp has only t and nil.
(setq ess-r-package-auto-enable-namespaced-evaluation nil)
By doing so, all code is evaluated in the global namespace. Using the above example, I can directly run bar() after M-x lp.
ESS actually has a neat suite of commands to handle “Rd” (R Documentation) files. But I believe no one would want to edit “Rd” files manually (It probably is handled by roxygen2 automatically now). A command that is quite helpful to dealing with this is Rd-preview-help (default to C-c C-p).

By default, ESS enables flycheck (on the fly syntax checking), but you’ll need to install the R package lintr.

ESS makes some amendments to the default linter (e.g. the 80 character rule is not checked), which I found very reasonable.
The newest kids on the block are the whole LSP craze and eglot. The next version of emacs (version 29) will have eglot built-in. When the R package languageserver is installed and eglot is enabled, I got some full-fledged IDE experience.

To be honest, I am a bit overwhelmed by the amount of information, especially the hover-on help system. I don’t want to switch it on by default. But I agree that xref-find-defintion (jump to the definition of the symbol) is quite useful. As it is new, I haven’t invested a lot of time customizing it. “But that can well be my prejudice.”™
there are even ess-r-rhub-check-package ↩