diff --git a/rules.org b/rules.org
index 21c33f4cea35decd075427a393c8056a2c641cca..d4db7a0ba62879a3ad5847ae7f6792d7263bf8f9 100644
--- a/rules.org
+++ b/rules.org
@@ -763,7 +763,7 @@ for (auto cell : caloCellContainer) ...
 auto foo = Foo {x};
 #+END_EXAMPLE
 
-   There are three sorts of places where it generally makes sense to
+   There are a few sorts of places where it generally makes sense to
    use =auto=.
 
      - When the type is already evident in the expression and the declaration
@@ -785,10 +785,38 @@ auto ufoo = std::make_unique<Foo>();
 std::map<int, std::string> m  = ..;
 auto ret = m.insert (std::make_pair (1, "x"));
 if (ret.second) ....
+#+END_EXAMPLE
+
+     - In the case where a class method returns a type defined within the
+       class, using the =auto= syntax to write the return type at the
+       end of the signature can make things more readable when the method
+       is defined out-of-line:
+       #+BEGIN_EXAMPLE
+template <class T> class C {
+public:
+  using ret_t = int;
+  ret_t foo();
+};
+
+// Verbose: the return type is interpreted at the global scope, so it
+// needs to be qualified with the class name.
+template <class T>
+typename C<T>::ret_t C<T>::foo() ...
+
+// With this syntax, the return type is interpreted within the class scope.
+template <class T>
+auto C<T>::foo() -> ret_t ...
 #+END_EXAMPLE
 
      - =auto= may also be useful in writing generic template code.
-   
+
+   In some cases, C++20 allows declaring a template function without
+   the =template= keyword when the argument is declared as =auto=:
+   #+BEGIN_EXAMPLE
+auto fn (auto x) { return x + 1; }   
+#+END_EXAMPLE
+   It is recommended to avoid this syntax for public interfaces.
+
    In general, the decision as to whether or not to use =auto= should
    be made on the basis of what makes the code easier to _read_.
    It is bad practice to use it simply to save a few characters
@@ -1998,7 +2026,58 @@ private:
      that must be acquired in both =getSize()= and =push()= in the above
      example.
 
+** Formatted output
+
+ - *Prefer std::format to printf or iostream formatting.* [<<use-format>>]
+
+   For new code, use the C++20 formatting library to format values to
+   a string rather than using printf-style formatting or using
+   iostream manipulators.
+
+   Example:
+   #+BEGIN_EXAMPLE
+#include <format>
+...
+const char* typ = "ele";
+float energy = 14.2345;
+int mask = 323;
 
+std::cout << std::format ("A {1:.2f} GeV {0} mask {2:#06x}.\n",
+                          typ, energy, mask);
+// prints: A 14.23 GeV ele mask 0x0143.
+#+END_EXAMPLE
+
+   Compare using =printf=-style formatting:
+   #+BEGIN_EXAMPLE
+#include "CxxUtils/StrFormat.h"
+...
+std::cout << CxxUtils::strformat ("A %.2f GeV %s mask %#06x.\n",
+                                  energy, typ, mask);
+#+END_EXAMPLE
+   or =iostream=:
+   #+BEGIN_EXAMPLE
+#include <iomanip>
+...
+const int default_precision = std::cout.precision();
+const std::ios_base::fmtflags default_flags = std::cout.flags();
+const char default_fill = std::cout.fill();
+std::cout << "A " << std::fixed << std::setprecision(2) << energy
+          << std::defaultfloat << std::setprecision(default_precision)
+          << " GeV " << typ << " mask "
+          << std::hex << "0x" << std::setfill('0') << std::setw(4) << mask
+          << std::setfill(default_fill)
+          << ".\n";
+std::cout.flags(default_flags);
+#+END_EXAMPLE
+
+   Like the streaming operator, =std::format= has a way of customizing how
+   a given type is formatted.  However, it is somewhat more involved than
+   for =operator<<=; in addition, =std::format= will not use existing
+   custom streaming operators.  Therefore, for generating printable
+   representations of class instances, it is probably better in most cases
+   to use the =iostream= mechanism.
+
+   
 ** Assertions and error conditions
 
  - *Pre-conditions and post-conditions should be checked for validity.* [<<pre-post-conditions>>]
@@ -2208,11 +2287,44 @@ Exception: Mine!
 ** Parts of C++ to avoid
 
 Here a set of different items are collected. They highlight parts of
-the language which should be avoided, because there are better ways to
-achieve the desired results. In particular, programmers should avoid
+the language which should be avoided, either because there are better ways to
+achieve the desired results or because the language features are still
+immature. In particular, programmers should avoid
 using the old standard C functions, where C++ has introduced new and
 safer possibilities.
 
+ - *Do not use C++ modules.* [<<no-modules>>]
+
+   Modules were introduced in C++20 as a better alternative to =#include=.
+   If a module is referenced via =import=, it avoids repeatedly parsing
+   the code as well as avoiding issues that arise due to interference
+   between headers.  However, building modules requires significant
+   support from the build system, and the support in compilers and
+   associated tools is still very immature.  Even using the standard
+   library as a module is not fully functional with C++20.
+
+   For now, avoid any use of modules.  With C++23, it may be possible
+   to use standard libraries as modules, but building ATLAS code
+   as modules will require significant additional development.
+
+
+ - *Do not use C++ coroutines.* [<<no-coroutines>>]
+
+   Coroutines allow for a non-linear style of control flow, where one
+   can return from the middle of a function and then resume execution
+   from that point at a later time.  However, the coroutine interfaces
+   available in C++20 are quite low-level: they are intended to be used
+   as building blocks for other library components rather than for
+   direct use by user code.  Further, uncontrolled use of the type
+   of control flow made possible by coroutines has the potential to be
+   terribly confusing.
+
+   For now, avoid use of coroutines.  If you have a use case that would
+   greatly benefit from using coroutines, please consult with software
+   coordination.  This recommendation will be revisited for new versions
+   of C++ which may include easier mechanisms for using coroutines.
+
+
  - *Do not use malloc, calloc, realloc, and free. Use new and delete instead.* [<<no-malloc>>]
 
     You should avoid all memory-handling functions from the standard
@@ -2228,7 +2340,8 @@ safer possibilities.
 
     =scanf= and =printf= are not type-safe and they are not
     extensible. Use =operator>>= and =operator<<= associated with C++ streams
-    instead. iostream and stdio functions should never be mixed.
+    instead, along with =std::format= to handle formatting (see [[use-format]]).
+    iostream and stdio functions should never be mixed.
 
     Example:
     #+BEGIN_EXAMPLE
@@ -2249,9 +2362,8 @@ printf("This does not work: %s \n", aCPPString);
     It is of course acceptable to use stdio functions if you're calling
     an external library that requires them.
 
-    Admittedly, formatting using C++-style streams is more cumbersome
-    than a C-style format list.  If you want to use =printf= style formatting,
-    see ``CxxUtils/StrFormat.h''.
+    If you need to use =printf= style formatting, see ``CxxUtils/StrFormat.h'';
+    however =std::format= is preferred for new code.
 
 
  - *Do not use the ellipsis notation for function arguments.*  [<<no-ellipsis>>]