I've thought about this so many times that it feels as if I must have written about it before, but it seems at least not recently.
We talk a lot at GameChanger about the fine line between hacking shit together and good software engineering practice. My north star on the topic has always been the aspiration of a code base.
By aspiration, I mean what the code structure is trying to be. This is often just in the heads of its engineers, and I regularly call it the "stated architecture", to distinguish it from the actual, current architecture.
I use this concept to guide my hackery by always assuring that I'm building things that while imperfect do not contradict the aspirations. A function can have a horrid implementation if it's being called in the right way. A whole module can be spaghetti if the data it outputs adheres to a future-minded design. A pile of data can be ugly to look at if it doesn't by it's accumulation make it difficult to clean up or migrate to a planned optimal form.
What I've seen to be an important process as our team grows is to make the aspirations of our code bases clear and explicit. Some code is wrong because it is a mistake (bug, laziness, etc), while other code is wrong because it is an incomplete incarnation of its visionary form.
Incidentally, this property of code bases is why I think when certain kinds of turnover happen, all hell breaks loose: if the aspirations are completely lost in history, and the minds of ex-coworkers, there is usually no recourse bit to create new aspirations, and in their light everything is suddenly clearly broken, and only a rewrite will rectify it.