I think I've managed to find a good balance. I discuss the approach at the new webpack book freely available online.
Basically I maintain a high level webpack.config.js that combines smaller parts of configuration using webpack-merge, a little utility of mine. There are a couple of interesting advantages to this:
- You codify your webpack knowledge to functions. No need to remember it all once you have figured out good ways to do what you want.
- Given it's all functions, this means you can publish your configuration parts to npm.
- Because you can publish them to npm, this means you can share them across multiple projects. Less maintenance fatigue. You could also push loader dependencies and all that to a package to improve further.
The con is that given it's all functions, there's an extra layer of abstraction between you and the pure configuration. Poor abstractions can often make it harder to understand what's going on. And extra code is always code that can have bugs in it.
This all goes back to the idea that the only way to solve boilerplate is simply to have less of it around. There will always be some boilerplate code around, but you can manage it to some extent.
I am currently suffering from boilerplate across my React component projects so the approach will become very handy once I get around to refactoring them. That will ease my maintenance efforts a lot.