Let’s discuss your production code. Yes, all that junk you’ve been writing and currently sitting in production. We've all contributed to some of that and there is no shame to be had about it. Perhaps it was the best you could do at the time with what you knew. Maybe the technology simply wasn't there. Its all lessons learned even though some of us may like to pretend that this does not apply to them. They've crafted procedures and protocols in place designed to prevent this exact problem. Weeks are spent on sizable design documents, UML diagrams, and all the bells and whistles that go with that. Everything is accounted for before coding takes place. With the premise that, by the time the associated piece of code is done, it is as solid as it can get. Built to last we should say. Except that it's not what the client wants by then.
Besides the fact that these documents have no profound value, any team out there on a mission to build tanks or bulled proof code needs to reevaluate their basics. Software is like any other living thing. It's there to serve a purpose within a given set of constraints. It should be allowed room to grow and change. This may sound mundane but it has profound implications on your speed of delivery and simplicity. Please note that I'm not promoting some type of careless practice with endless refactoring. I'm softening your fear of change and advocating pragmatic solutions. No amount of design and documentation will foresee business needs. Be open to modifying your code as constraints and time allow. This should be expected and your system should be designed as such by segregating fundamental business rules from external dependencies.
With a "built to throw away" mindset, dependencies are minimized and responsibilities managed. More importantly, developers welcome change with open arms as this can be achieved in small increments without having to carve out an extended time dedicated to refactoring. No single module, class or function is considered irreplaceable. In this mindset, progress can be better evaluated as a function of how much code you threw away than how much you've added. Any portion thrown away means you've understood the problem a bit better. This is progress.