Programmers tend to start with a clean design that works well, which then gets buggered about with to handle edge cases. Then a few more edges cases come around, the system stops being clean and becomes a mass of inefficient code.
To avoid this, edge cases should be worked in not as hacks to the existing code base but as full fledged iterative parts of the overall solution. But unfortunately, developers tend to be lazy and take the least path of work due to various reasons and this is one of the largest contributors to the problem you described above. Whether you're doing something insanely simple or something far more complex and involved, the same series of iterative steps should be considered, validated, and executed.