I have a strong opinion on this, which is that all custom coding should be done within custom modules, with only one possible exception (see below):
I discern three cases:
- Completely new functionality - this obviously calls for a custom module that encapsulates the new functionality. It makes it reusable and can even turn into an 'official' contributed Drupal module, if the functionality meets popular demand.
- Tweaking existing functionality - for every site, I immediately setup a blank, custom module (named after the site). All custom code used for tweaking existing functionality (be it from core or contributed modules) happens within this module. That way, all my customizations are cleanly separated, which makes it much easier to update the core or other modules without constantly having to reapply my customizations to the updated code (of course one has to check if the customizations still work after the update and adjust or remove them as needed).
- Fixing bugs/adding missing features - this is the possible exception mentioned above. If my changes are just a bug fix or an addition of an obvious, but missing feature, I might just do that within the original code, submitting my changes as a patch to the original module, hoping that they will get incorporated in a future release, thus making my changes obsolete.
In my experience, the 'overhead' of separating the customizations from the original code isn't really an 'overhead', as the 'one-off' tweaks & fixes usually stick around much longer than anticipated, and have a tendency to grow over the lifetime of a site. Having them separated from the start saves a lot off troubles down the road in maintenance, as applying updates & security fixes, as well as extending the tweaks will be much easier.