But I also wrote down some basic principles that are important in the project. When I read them again, I realized they're some core principles I find very important when coding. I know there are many principles out there to adhere to, but these are the ones I stress the most to colleagues and team-members.
Communication, communication, communication
Something I learned from Kent Beck's excellent Implementation Patterns (also available at Bol.com): when you're writing code, you're communicating. You're communicating with other coders, now and in the future, but also with yourself.
This is something to always keep in mind. I cannot stress this enough. Always be thinking if your code clearly communicates what it is trying to achieve. Most other principles more or less follow from this one.
Single Responsibility Principle
While I have definitely written classes that violate this rule, it is important to work towards having a class being responsible for only one thing. If a new responsibility is introduced, split it out into a separate class. Possibly, you will need a third class to coordinate between the two class.
This sounds like extra work and it is (especially if there are unit tests involved!), which is what often holds people back. But the extra work you do now will be saved later when your application doesn't exist out of a few big classes where all overview is lost. You will more easily be able to look at a class and understand what's going on. And it will be easier to change or refactor. The class provides better and clearer communication about what its purpose is.
I've worked on projects that use Dependency Injection and on projects that don't. The projects that do have been more elegant and have made it easier to plug in different or new implementations of dependencies in classes.
Dependency Injection is, in my experience, something that is hard to explain to novices, but once you get it, you see it as a very elegant approach for complex applications.
Keep your classes small. The Single Responsibility Principle should make this easier to achieve. What is small? Some blogger once pointed towards the fact that this wasn't always clear to new developers. He/she (can't remember who it was) proposed to use the arbitrarily chosen value of 300 lines as a maximum.
I find this generally works well. If a class is 300 lines or more, it's no longer readable. Just under 300 is of course also hard to read, but 200 is still more or less comprehensible. Still not ideal of course. But it provides a fairly good guideline to use for your classes.
On a side note: one class per file. Files are cheap and allow you to see the structure of an application by looking at the Solution Exporer (if using Visual Studio) or the directory. Also, when using a version control system, you can ask the history of a file to see the history of a class. You win nothing by putting multiple classes in the same file, even if they're small classes.
Method names, variables, properties, classes, etc are important
Communication again! Take a moment to think about the name of that variable and make it clear what it is.
And don't use abbreviations, it starts with using 'repo' instead of 'repository' and ends with using 'ddlPrefCustClass' instead of 'dropDownListPreferredCustomerClass'. You might have guessed this of course, but when a file is full of these abbreviations, it makes it orders of magnitude harder to read.
Code is written once, but read multiple times. Don't be lazy.
If you're using a version control system (please don't tell me you're not!), work in small commits. This allows you to see your thinking/working process later, but it also avoids having to iterate over many classes after having implemented something wrong and wanting to roll back part of what you've changed while keeping other pieces.
When you see something in the code that's not quite right or can be improved, fix it. Maybe commit your changes first (small commits!) but definitely fix it. Code written now can be as beautiful as you imagined it, but a few days/weeks/months/years down the line, you might see it differently. It might not be as clear as it was back then. Now is your chance to fix it.
Code must constantly be maintained. It's never 100% perfect. It can collect dust and lose its shine. When you encounter this, polish up the code, even if only a little bit. It keeps the project alive and maintainable.
This is closely related to the Boy Scouts Rule, where you leave the place you're visiting cleaner than it was when you arrived.
To me, this isn't the same as having tests. Test-driven development means you write your tests first. This is very important. It makes you think about what you're trying to achieve and how the public API of your method/class should look like to communicate (!) best. It also makes you order your thoughts about what you need and how you will implement it, instead of diving in without thinking.
After writing the test, write the minimum of code to make it pass. Then do the red-green-refactor cycle. First make the test fail (I have had cases where the test passes even though there is no implementation, especially when refactoring). Then fix it. And, often forgotten, have an extra look at the code and see if you can refactor it. Often, you can.
If you write your code first, and your tests last, it happens that you're testing what you wrote, not what the method or class should be doing. Also, you can end up with more code than necessary, have certain parts not under test, less clear code/API, etc.
I realize there are cases where TDD is not possible or hard. Refactoring and proof-of-concepts come to mind. But be honest, you're not refactoring or writing POCs 100% of the time, are you?
I believe that keeping these things in mind will lead to a more readable and maintainable codebase. Both for yourself as for others coming after you.
A former colleague once said: "Writing code is like a visit to the bathroom. Always think about the people visiting after you." Spot on.