A quick recap on how your perfectly clean code base became corrupted, in one gif:
We've all been there. You think you designed the greatest system ever... and then after months of changes and responding to requests of "80% solution now instead of 100% later", your code is less sexy than a Gremlin.
So what do you do to keep your code from degenerating into code goulash?
Things to consider before you start coding
Don't code (too) specifically for the problem
If you make your code too concrete, it will be painful to shift/alter/upgrade.
How many times have you been assured "This is exactly what we need it to do. No, it will never need to do XYZ. Never." And so you code to that specification. Two months later "We need it to do XYZ." Of course, XYZ usually conflicts with how you coded it... since it never needed to be that way.
I'm not saying to code for XYZ when you don't need XYZ... code in a way that if you need to implement XYZ you won't be starting over. Things that can help: choosing the correct design pattern(s); using abstract classes; using interfaces. Changing your database from SQL to Mongo should be something you can handle with relative ease. Can you?
Don't code features you don't need
This is a slippery slope. You are creating some great new classes and your mind goes everywhere about all the cool things it can do but you don't need quite yet. 1) This will waste time and 2) it will complicate your code. Example:
In the pre-Facebook days, another developer and I coded a user-generated content system. You know, sharing what you had for lunch... that sort of important social tool. Basic functions would be 1) User can create post and 2) User can edit post. Then it devolved into things we thought would be nice but didn't exactly need to make it work.
Let people up-vote posts! (they never did)
Let people flag content! (they never did)
Automatically hide content that was flagged too much! (never happened)
Create an Admin screen to go and review flagged posts! (never went to it)
I'm not saying these aren't needed for your lunch-sharing application... just that we overthought and over-coded the actual need for our system. We ended up spending time creating things we thought we needed and literally were never used once in 5 years despite thousands of posts added into the system. Similarly:
Don't fix problems that don't exist
This is like the previous bullet, but it manifests differently.
In brainstorming new systems with clients, sometimes we are driven off on a tangent of "but what if the user does XYZ, or ABC, or 123?" Usually I find it is best to address issues that are real issues in your system, not issues that may not ever manifest. This is a judgement call though. Obviously you don't want people breaking your application. That's different from safeguarding against scenarios that are as likely to occur as the Gremlin making a comeback.
Things to do once your project is in production
Continue to improve/clean existing code
Every coder knows that what he wrote last week is complete crap, but the code he is writing today is glorious. Well, when you see that crap code, fix it up. This can be just little things to make the code cleaner and easier to read, but really this leads to:
This will take a little more time, but a little refactoring can lead to significant code cleaning over time. As your code is cleaned up, it will make it easier to implement the correct design pattern or extend your existing classes/methods.
If you aren't learning, you are stagnating! Want something to learn? How about Refactoring?
How do you keep your code clean and flexible?