Earlier quoted context omitted.
I wish I knew the answer to this, but I don’t. The closest I’ve come to succeeding at this is to have a reputation as a “breaker”. This really started in undergrad algorithms; I wasn’t great at finding solutions, but I had a knack for finding edge cases to break solutions others had come up with. Sometimes it just needed a tweak to work, sometimes it was “throw it all away and start over”. For the few times I’ve done…
IMHO, Essentially you need to let them experience the issue you foresee first hand, by letting them fail. That way they can learn the same lessons you learned previously.
What happens is that the code will be released, some user will hit the bug in the field, and the issue will be escalated to the engineer, who will proceed to spend entire days or weeks to troubleshoot the problem, and then just put a dirty patch that fixes the specific issue, leaving all the other corner conditions still exposed and ready to explode. At that point, major redesign of the solution, which would have been possible at the code review phase, will just be deemed too risky by both the engineer and the management.
On top of this, Management, usually ignorant to concepts such as “over engineered solution”, will praise the heroic effort of the engineer who spent days troubleshooting the issue, clueless of the fact that it was a completely self inflicted wound because the engineer decided that the best way to calculate 2+2 is to really do sqrt(2^4) and then realized that the user really wanted to do 3+3, so they’re going to put a special case to evaluate 3+3 as sqrt(3^3+3^2) rather than just doing the damn x+y.
Nobody will even realize that the issue could have been completely prevented in the first place by adopting a solution that was much easier to reason with on the whiteboard, rather than a house of cards of custom logic.
I’ve seen this script dozens of time.