Beyond having good testing, don't try to write it to be easy to change, write it to be good code.
You should try to express problems elegantly, and that will help make some changes easy. That's nice when that happens, and it does happen quite a bit. But many changes will still not be straightforward.
Generally, write the code to do what it's meant to do, and when it needs to change, rip it out and rewrite it.
It's shockingly easy to write something that's concrete and later realize "I need two of these" and then quickly factor out the common functionality.
Especially, don't kludge things. The problem with "easy to change" is it means "easy to kludge just one more feature on here." That won't remain easy to change. Refactor aggressively and your code still won't be easy to change, but it won't become a kludgey mess that's hard to change, either.
Comments
Beyond having good testing, don't try to write it to be easy to change, write it to be good code.
You should try to express problems elegantly, and that will help make some changes easy. That's nice when that happens, and it does happen quite a bit. But many changes will still not be straightforward.
Generally, write the code to do what it's meant to do, and when it needs to change, rip it out and rewrite it.
It's shockingly easy to write something that's concrete and later realize "I need two of these" and then quickly factor out the common functionality.
Especially, don't kludge things. The problem with "easy to change" is it means "easy to kludge just one more feature on here." That won't remain easy to change. Refactor aggressively and your code still won't be easy to change, but it won't become a kludgey mess that's hard to change, either.