Connection pooling was considered to be purely external solution (like pgbouncer or pgpool) for very long time. However, external poolers have restrictions. For instance, in session/transaction mode the benefit of pooler is limited: each active session/transaction still needs a separate backend. In statement mode additional effort is needed to maintain all connections in the same state. Otherwise you may be faced with situation when prepared statement which you’re using is not defined or GUCs have wrong values.
The true solution of connection pooling problem would be built-in pooling facility which guarantees that all the user sessions would always work in the same way they work in separate backends. However, this needs significant infrastructure rework, such as storing the whole session states in shared memory.