In codebases I've worked on, the biggest culprits that make maintenance and modification difficult are
- functions that do several different things
- tight coupling
- implicit behavior
- too much indirection
- too many thin custom wrappers
Often several of these come together when an inexperienced developer factors out everything they can (over-eager DRY) but still tries to keep a few highly specific things "easy". One thing I see often is modules or classes that wrap a standard library, adding code essentially removing features in the interest of "ease-of-use".
I think you can write better code if you write code in multiple passes: only write what you need right now, and use the code as-is between each pass. With that approach, you'll gain some real intuition about what is easy to read and modify, which is much more helpful than someone's list of tips.
That said, I've found that writing simple explicit functions that take arguments and return a result can get you pretty far. Same for class constructors and methods, API endpoints, etc. Try to avoid writing "convenience" functions or classes when just using the library directly with some arguments would suffice. In other words, boilerplate probably isn't as bad as you think, and the indirection/implicit behavior/tight coupling is often worse than the boilerplate.
Comments
In codebases I've worked on, the biggest culprits that make maintenance and modification difficult are
- functions that do several different things
- tight coupling
- implicit behavior
- too much indirection
- too many thin custom wrappers
Often several of these come together when an inexperienced developer factors out everything they can (over-eager DRY) but still tries to keep a few highly specific things "easy". One thing I see often is modules or classes that wrap a standard library, adding code essentially removing features in the interest of "ease-of-use".
I think you can write better code if you write code in multiple passes: only write what you need right now, and use the code as-is between each pass. With that approach, you'll gain some real intuition about what is easy to read and modify, which is much more helpful than someone's list of tips.
That said, I've found that writing simple explicit functions that take arguments and return a result can get you pretty far. Same for class constructors and methods, API endpoints, etc. Try to avoid writing "convenience" functions or classes when just using the library directly with some arguments would suffice. In other words, boilerplate probably isn't as bad as you think, and the indirection/implicit behavior/tight coupling is often worse than the boilerplate.