Things get really interesting when a team in your organization decides to take out this module ‘C’ and open source it. Now suddenly updates to this module need to balance both the company incentives and those of the open source community. Things can get really expensive quickly.
Yeah that is hairy. And I would say that for many organizations, if a group decides to attempt the extraction and reuse of a module, they should be prepared to adopt the mindset and working style of a library maintainer. Because once you get other people using your module (the point of reuse) you are automatically going to get multiple points of view on what needs to be improved and changed. You're going to end up being a library maintainer anyway, so you might as well go into it with eyes wide open.
Bad enough, but reality gets even worse. The author of C might be relatively incompetent, have a tendency to add and remove semantics without documenting the changes, be unwilling to fix their bugs, or just plain be a raging asshole.
These days I think long and hard about dependencies, and ten times as long and hard about dependencies controlled by people inside my organization that I don't have the power to strangle.
>This can work, but it increases complexity of C, and the logic gets messy as well, as the code gets riddled with if blocks like "if called from A then do this block of logic".
Instead of doing this, the author should consider decomposing C even more. The former is logical cohesion, the latter is functional cohesion.
I've seen several examples in my own company's libraries where the authors have built frameworks. It almost always ends up in dependency hell.
At this point, I tend to try and shed dependencies whenever possible. I hate pulling in massive frameworks or worse, libraries that depend on massive frameworks. It almost always results in a brittle solution that causes maintenance nightmares in the future.
This doesn't mean I never use a framework, I just try to restrict it to the application level.
Good stuff. The decision when to break out into something reusable is a complicated one. I’ve found that the “reusability” for a component could be approximated with the the ratio (implementation complexity / interface size). High ratio, i.e complex code combined with well defined and small interface, means it could be extracted and shared in an effective way.
I find frequently changing interfaces to be a bigger problem than big interfaces. If the big interface doesn't need changing often, it's not an impediment to reuse. But it is often hard to know what the future brings.
I've found that "micro-reuse" is usually more successful. Break big components into smaller components. If a large-scale abstraction fails to fit, then one can probably still use some of the sub-components.
Design them in a way that the sub-components can be used stand-alone when needed. It may take time (experience) to tune such: your first attempt at a given component abstraction will probably be clunky.