This solves the latency but not the overhead--you'll end up with a full pool of connections for each running instance of your application.

How many running instances do you need though? e.g. Scala web frameworks should be able to do thousands of RPS on a single core without the application developer really trying to optimize anything, and I always hear that even Ruby, Python, etc. are also fast enough to be IO bound so you should just need 2 copies for redundancy, right? Then give each like 8-16 connections.

If I'm implementing my own connection pool, I'll certainly make the number of connections configurable.

That's fine but doesn't address the issue that PgBouncer does. Your application connection pool can multiplex all the connections needed in one application. PgBouncer can multiplex the connections across all applications (whether different apps or many instances of the same app).