I came across this rather interesting "Law" recently. Zawinski's Law states that “Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can.” Coined by Jamie Zawinski (who called it the “Law of Software Envelopment”) to express his belief that all truly useful programs experience pressure to evolve into toolkits and application platforms (the mailer thing, he says, is just a side effect of that). It is commonly cited, though with widely varying degrees of accuracy.
If most of the apps are anyway destined to be toolkits/platforms/frameworks, then we better bear this in mind while designing them in the first place, even if the application was never intended to be so. Interface centric, Object Oriented, Test Driven development would go a long way in ensuring this. These three paradigms are more about discipline than some cutting edge tech stuff.
Interface Centric: This is one of the most preached, and least practiced paradigm. Design your interfaces BEFORE you design your application. Look at the Façade pattern, for help. This is not difficult, but just a matter of habit.
Object Oriented: Ok. You have read a lot about it since the days you spent programming in your diapers. So, I won't repeat all that. Just keep all the good things you learnt about OO in mind- aim to improve reusability, decrease encumberance and judiciously manage relationships between various programming entities. There is only one way of improving in this area- practice. Make lots of mistakes, get lots of crap from experienced souls and don't commit those mistakes again. And yes, one last thing- pleeeeeease document your OO thoughts. Take your pick on how to do it- UML, design docs, code comments (or a combination of these), but DO IT.
Test Driven: So, you have designed your interfaces. You have designed your application around those interfaces. Now what? Write code. Right? Wrong. This is when you should define your test cases and automate them using unit test scripts. Application programming should just be a mode to pass these tests. Some argue that the Test Driven approach gives Development Myopia and people fail to see well into the future. My take on it is, if you use it along with the paradigms I cited above your myopia is prevented. You may or may not use tools like NUnit or JUnit. You may just write your test scripts in shell script or Perl. As long as you have a well maintained test bed that ensures good coverage of your code (there are tools for that too, like Ncover for .NET), you are safe and sound. In practice, the challenge is not in writing the unit tests but maintaining them through changes and across versions. That takes lot of discipline and…nothing else.
But, all this homily is easily forgotten. The only way to ensure that your development automobile doesn't get hijacked (by deadline pressures, for example) is to put it on well designed rails, guarded by controllers and traffic signals. Those rails are called "Processes". Those controllers are called "Reviewers". Those traffic signals are called "Management Checkpoints".
In conclusion, if you have processes that promote Interface Centric, Object Oriented, Test based software development that is guarded by Reviewers and controlled using carefully designed Management Checkpoints, your project may still go to dogs, but the chances are vastly reduced. Vastly.
PS: Please also read two of my earlier posts (linked below) in conjunction with this one to get a good view of what I preach as sound and sane software development practice:
Seven Rules to Guarantee Project Failure
12 Steps to better code