In my experience, the situation you're describing more often results in "sloppy" code -- it seems convoluted, non-obvious, poorly thought out until you realize when the pivots happened. Overengineered code usually looks neat at first glance, maybe even impressive, especially to non-technical stakeholders. Then when you actually dive into it, you notice a bunch of wrong abstractions that introduce layers of indirection without offering any sort of valuable flexibility. "This metaclass that implements a context manager could've just been a slightly longer than average function" type stuff.
A codebase that pivoted multiple times makes more sense the more time you spend in it, an overengineered one makes less.
A codebase that pivoted multiple times makes more sense the more time you spend in it, an overengineered one makes less.