Skip to content

Instantly share code, notes, and snippets.

@bttmly
Created January 10, 2017 19:41
Show Gist options
  • Save bttmly/98c5b0197f16db73066e42bc0a5f51bd to your computer and use it in GitHub Desktop.
Save bttmly/98c5b0197f16db73066e42bc0a5f51bd to your computer and use it in GitHub Desktop.

The Magical Expanding Interface

Here is a series of logical, defensible actions that end up with a sub-optimal result:

  • We need to share some code between two applications (App A and App B), so we move it out of App A into somewhere else (i.e. its own repo, or into components or essentials or some similar collection.)
  • The interface exposed by this shared code isn't quite right for App B. Perhaps it's not ergonomic for the application for some reason, so to keep things DRY, they create a wrapper to provide whatever configuration, defaults, or argument shifting makes sense for App B. Now, all call sites in App B go through the wrapper.
  • Time goes by, and something else changes. Perhaps App A changes a bit, and so the engineers do step 2 above (but -- to be clear, they implement it in a different, App A specific way). Alternately, App B becomes more complex and the lower level wrapper is no longer sufficiently ergonomic. However several call sites rely on it, so a higher-level wrapper is created for future use.
  • Rinse and repeat

The ultimate result is the underlying library becomes very difficult to use. While someone may have done the up-front work of creating docs for the library, those docs no longer apply to most call sites in new code. Different applications have created wrappers, and perhaps those wrappers operate at different levels of abstraction, or are themselves wrapped around other wrappers. All of this is in the intended service of DRYness, but at the cost of a large amount of indirection. Thus, rather than lowering the cognitive load of context switching (because App A and App B are using a shared library) it is instead increased, because App A and App B have created different, incompatible, application-specific interfaces into that library.

Solutions:

  • Create a new method in the underlying library, and perhaps deprecate the old one if necessary.
  • Inline code from App A into App B, let their interfaces evolve organically, and pull the code into a library when stable. (Requires a lot of discipline)
  • I don't know! Suggestions please
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment