We all have worked with spaghetti code, trying to pinpoint the origin of a bug, following one method after another, from file to file, deciphering convoluted blocks of code that don't make any sense. This happens in a lot of cases despite the author's best intentions because once we get our code working, we move on.
If you're not taking the time and effort needed to write simple code, you're most likely leaving a mess for the next programmer (and even for yourself when you revisit your code). A few weeks or months of being negligent, and "suddenly" our code turns into spaghetti code.
Kent Beck suggests four rules for emerging simple design and avoiding our codebases from taking a wrong turn. In order of priority, our code must:
Run all the tests
Having a safety net is important to introduce new functionality or repair existing bugs without messing something else. Tests also serve to guide design, and together with refactoring, often lead us to more efficient algorithms.
Contain no duplicate code
This is a no brainer, repetition leads to repetition. Whenever you update one place, another has to be updated as well. This results in effort duplication, and failing to do so introduces bugs. You should make an effort to identify and get rid of it. Sometimes it's really easy to see, specially when it's a variable or a method, but other times it might be an entire module that could be reused in multiple places of your system, and an abstraction is needed to reuse it appropriately.
Express all the ideas the author wants to express
We want our code to be clear, and read pretty much like plain english. Some programming languages make this easy (e.g. Ruby), but this rule is language-agnostic and we can achieve almost the same level of clarity by taking the time to name our variables and functions in a way that clearly expresses our intention.
Minimize classes and methods
This goes in hand with removing duplication. Take the time to refactor your code after you solve the problem. Look for ways to do the same with less code, make your methods smaller, make your classes smaller, give them only one reason to change and extract functionality that can be reused.