johno

Composition over configuration

Composition is powerful because it's a "simple" abstraction that's meant to combine functionality between two otherwise separate parts. It tends to be less leaky than configuration-based approaches and avoids framework coupling in convention-based approaches.

In essence, composition can be boiled down to:

Object composition
const newThing = {
  ...someValues,
  color: 'tomato'
}
Or functional composition:
f(g(x))

For the purposes of this post we'll be mostly focusing on functional composition and how it relates to components.

Components over configuration

In the frontend world we're already operating with a compositional abstraction, the very components we're working with day in and day out. We operate with a syntactic wrapper (JSX) and pass data to a tree of components to render UI. Technically speaking JSX is a Domain Specific Language (DSL), but it's one that is generally accepted in the wider community and really isn't much more than a shortcut for function calls.

JSX and components provide an expressive way to express composition that can cut out configuration in a lot of cases. This is a good thing because composition is only introduced where it's invoked and doesn't cause global side effects.

Natural layering

Component composition allows you to layer specific needs.

Components can be wrapped
<ResponsiveIframe>
  <Youtube id="12345" />
</ResponsiveIframe>
Components can be siblings
<Image src="12345" alt="numbers" />
<Citation photographer="Fred Flintstone" />
Components can be abstracted
<ImageWithCitation src="12345" alt="numbers" photography="Fred Flintstone" />

Combining common elements

In the above example you might find that as a project evolves that all Youtube components are accompanied by a wrapping ResponsiveIframe component. After that pattern becomes solidifed you can create a new component, ResponsiveYoutube and begin using that in its place.

What makes this especially powerful is the fact that you don't have to immediately port the existing usage right away. That can happen over time. Additionally, when there are edge cases that need some type of one-off functionality you find yourself working at the component layer rather than overloading a plugin, syntax, or configuration option.

Breaking them back apart

Similarly to Legos you can break apart components when further customization is needed. Libraries can expose more "batteries included" components as the primary API surface area which can be broken back apart to create something more bespoke.

Configuration is (typically) global

In most cases, configuration is applied globally to a project or framework. For example, let's consider remark plugins in Gatsby.

When you apply a plugin to the global remark transformer it will affect every document in the project. This can be problematic when you just need to add in a responsive Youtube embed in a single Markdown file. What's also interesting, is a project will typically already have a Youtube component that's used somewhere else (perhaps in a JSX-based page).

Suboptimal workflow

The plugin workflow also departs from the parts of the project you're spending most of your time in. If you're building for the web you're typically writing content or building components for new pages, layouts, etc. You're operating at an abstraction layer that's typically above the notion of configuration and plugins (which is a good thing!).

Composition is evolving

React Hooks introduce a new method of composition in components that provide the power of mixins without the drawbacks. Granted, Hooks, have a lot of prior art, but their introduction makes it more accessible to framework users and it's built in.

Conclusion

Configuration-based systems are necessary at low-levels to ensure underlying tooling is flexible. This posts isn't meant as a jab at any of the tools we use on a day-to-day basis. In fact, I love that they're so flexible and pluggable so we can focus time and energy on making those tools bulletproof for the greater community. However, for the vast majority of developers, we can work at the next layer of abstraction which can, and should, favor composition.

Related