Let’s say you are building a git analytics product.
Your product supports GitHub and GitLab for now.
It might support more products in the future.
90% of the codebase that supports GitHub and GitLab is identical.
10% is specific to GitHub and GitLab.
There are two ways to build software abstractions here.
The easy path to fall for is to have unified objects that take care of both GitHub and GitLab data.
These objects would, however, behave 10% differently depending on whether it is GitHub or GitLab.
Your codebase will be ridden with conditionals that trigger only for one or the other.
Alternatively, you can build two different code paths, one for GitHub and one for GitLab.
Those code paths will be similar to each other.
Any identical code in them can be extracted into common functions.
These common functions should know nothing about GitHub or GitLab.
However, a new engineer working on GitLab-related features does not need to know that a path for GitHub exists.
It further reduces the chance of accidentally breaking GitHub-related code as well.
Keep the code paths separate.
Let them use common code when required.
Make your abstractions deep not wide.