0. Don't try to be clever, prefer value/move semantics These days optimizers can often "see through" (via constexpr/inline/lto) and optimize simple code that uses value semantics to the equivalent (or even better) "clever" code that tries to avoid extra copies, allocations, etc. See also the note on small value optimization. 1. Modernization - use override - consider constexpr for inline functions (e.g., enum class bitmask operators) - consider noexcept ? migrate to #pragma once 2. Almost never auto Using auto instead of the actual type often makes code harder to understand. You may (but don't have to) use auto when (a) the type is spelled out on the right hand side (e.g., casts, make_*<>() functions, etc), and (b) in cases of idiomatic use where the type is clear to anyone familiar with C++ and it would be painful to spell it out explicitly. Examples of the latter are lambda initializations, iterator initializations (e.g, from begin()), and some cases of pair initialization (e.g, from container's insert()). 3. Almost never brace-initialization We only use brace-initialization syntax when initializing an aggregate or a container. We can also use it for an aggregate-like initialization of an aggregate-like class. An aggregate-like class is class with public data members only and an aggregate-like initialization is an initialization that provides initializers for every such data member. For example: struct foo { int x, y; foo (int x, int y); foo (int both); }; foo x {0, 1}; // Ok. foo x {0}; // Bad. For default member variable initialization use assignment syntax, for example: struct foo { int i = 123; string s = string (123, 's'); }; 4. Many performance-critical standard types are "small object optimized" For example, across all the implementations that we care, std::string can hold at least 15 characters without allocation. Similarly, std::function can hold a lambda with at least 2 pointers also without allocation. As a result, it is seldom makes sense to resort to elaborate optimizations such as string pooling. In fact, if we have a string pool that contains mostly short, SOO strings, then we will most likely hurt performance due to lack of locality.