A Forest Ranger, A Software Developer and Nicholas Taleb Walk Into A Bar...
Things that get stronger under stressors are Antifragile, a term coined by Nicholas Taleb. Unlike the fragile which collapses under stress or the resilient which is impervious to stress, the antifragile thrives in stressful environments.
A visceral example of the antifragile is the control burned forest. Under the stress of a controlled burn, the forest thrives for the following reasons (from National Geographic):
- By ridding a forest of dead leaves, tree limbs, and other debris, a prescribed burn can help prevent a destructive wildfire
- Controlled burns can also reduce insect populations and destroy invasive plants
- In addition, fire can be rejuvenating. It returns nutrients to the soil in the ashes of vegetation that could otherwise take years to decompose. And after a fire, the additional sunlight and open space in a forest can help young trees and other plants start to grow.
There are parallels between introducing a controlled burn to a forest and a controlled churn to a codebase. By frequently introducing controlled chaos, a codebase becomes antifragile to churn. Do a Google Search for 'Code Churn', and you will find that it typically indicates poor code quality. I however have found controlled churn events to improve code quality. I'll use the rest of this post to elaborate, and provide some examples from my experience.
Dead Leaves And The Dirty Ground
In my last post, Prototyping richsoni.com With Material-UI, I asserted my motivations for switching the design system of richsoni.com from Ant Design to Material Design. In the same post, I shared several prototypes for this site using Material-UI, a React component library that implements Material Design.
While the motivations outlined in Prototyping richsoni.com With Material-UI are enough to justify migrating from Ant Design to Material-UI, it is worth emphasizing there is an additional value presented by the act of migration itself. The migration exposes a natural boundary between presentational and controlling (data fetching / transforming) functions. Refactoring code on this boundary mimics a Firebreak (a mechanism for restricting a controlled burn to a specific area) by exposing and quarantining each area of code.

Code firebreaks are difficult to identify in the absence of churn. This makes designing a system in a vacuum nearly impossible. The results are either an over-investment in making the fringes of a system resilient to something that will never happen or under-investment in protecting and improving the core parts of the system. It's hard to say why this happens, but a safe guess is that the developers made incorrect assumptions about the future of the codebase.
Instead of predicting the future, development teams should observe and react to change.
In a controlled churn situation, the prescription is clear: divest in the code within the firebreak and double down on the code quarantined out of the burn area. This makes the codebase antifragile.
Before converting the build system for richsoni.com from Jekyll to Gatsby in mid-2018, the site was a single module.
During that conversion, a seam was exposed between the build system and the data layer.
By quarantining the data layer into a separate module (@richsoni/cms
), it reduced the likelihood of future changes to the build system affecting the data layer.
Had the build system never changed, the data layer and build system would most likely still be coupled into the same module.
This would be unfortunate because there are benefits to quarantining the data layer from the rest of the system.
Besides simplicity and elegance, a separate cms
module can be imported by other packages (the @richsoni/aws
module for example).
The Theme At The Seam
The seam exposed by the Ant Design to Material-UI migration is between the transformation/control layer and the presentation layer.
By quarantining the transform/control layer into its own module it becomes impervious to changes in the presentation layer (i.e. pseudo MVC at the module level).
This was accomplished on richsoni.com by migrating the transform/control code to a Gatsby Theme module (@richsoni/gatsby-theme-core
).
By requiring this theme, a Gatsby site only has to implement the presentation layer (i.e. @richsoni/gatsby-material-ui
).
A Tour of The Core
When @richsoni/gatsby-theme-core
is built as a standalone site, it generates the code found at raw.richsoni.com.
This barebones rendering of the site makes no attempt at styling the content.
As its name suggests, @richsoni/gatsby-theme-core
can also function as a Gatsby Theme, which is imported by higher-order Gatsby sites (i.e. gatsby-antd.richsoni.com, and richsoni.com).
As a Gatsby Theme, it provides a clean and elegant mechanism to reuse the control/transform code while giving full visual control to the importing package.
Theme Friendly Components
File Shadowing is a key feature of Gatsby Themes which lets the higher-order Gatsby site easily override any of the theme's components.
The importing packages only need to create a new version in a specific location: src/{theme-name}/{pathToFile}
to override the file.
The package can leverage code (transformation, state control, data fetching etc.) from the theme or its local codebase when Shadowing a file, making a Gatsby Themes a very powerful tool.
The following is a code example of a typical page within @richsoni/gatsby-theme-core
:
// packages/gatsby-theme-core/src/pages/examples.tsx
import React, {useState} from 'react';
import ExamplePage from '../components/ExamplePage';
export const transformData = (data) => data;
export query = graphql`
query ExamplePageQuery {
...ExampleFragment
}
`
export default ({data, location}) => (
const [value, setValue] = useState('Default');
<ExamplePage
location={location}
{...transformData(props.data)}
value={value}
setValue={setValue}
/>
);
Every page and template is the same, it hands off the presentation to another component. The standalone version of the site (raw.richsoni.com) uses the default presentation components provided with the theme. For example:
// packages/gatsby-theme-core/src/components/ExamplePage.tsx
import React from 'react';
import {ExampleSchema} from '../lib/';
export default ({title, value, setValue}: ExampleSchema) => (
<article>
<h1>{title}</h1>
<input value={value} onChange={setValue} />
</article>
);
Finally, when the module is used as a theme, the importing site can Shadow the presentation file:
// packages/gatsby-antd/src/gatsby-theme-core/components/ExamplePage
import React from 'react';
import {ExampleSchema} from '@richsoni/gatsby-theme-core/src/lib/';
import {Layout, H1, Input} from 'antd'
export default ({title, value, setValue}: ExampleSchema) => (
<Layout>
<H1>{title}</H1>
<Input value={value} onChange={setValue}
</Layout>
);
Testing raw.richsoni.com
End to end tests are notoriously flaky, and their use is controversial. Even Google has chastised their value. In my experience, they do add some value in terms of detecting regression that unit tests cannot. They make upgrading libraries a lot less stressful, and can identify unknown side effects of code changes.
However, they are cumbersome to work with when rapidly iterating over a product. The churn of UI changes constantly breaks the tests, and it becomes a nuisance to the developer flow.
Since raw.richsoni.com is more stable than its styled counterpart (richsoni.com), I thought it might be a good idea to try writing end to end tests against that instead of the actual site. The functionality on this site should change significantly less often, and is significantly more important than the UI specific throw away code.
For this, I tried Cypress for the first time. I was impressed with how polished and easy it was to use.
Conclusions
This refactor did not take much time (about a week), but it will continually improve the codebase long into the future. The presentation layer is free to churn without altering the more stable 'core' code. This makes the codebase robust to presentation layer churn.
The @richsoni/gatsby-theme-core
package is also antifragile to presentation logic churn.
Every breaking change is an opportunity to improve code quality, and test coverage of the package.
With its Cypress test suite, upgrades to underlying libraries (React, Gatsby etc.) should be a breeze.
I can also fearlessly refactor the architecture as functionality mutates, while having confidence that I am not breaking anything.
With these new benefits as well as a spiffy new UI to come, its hard to argue that 'churn is bad'. None of these changes would be possible without it. Code has to progress, and churn is progression. It just needs to be controlled.