The tangled webs we weave
Another reflection on modern web development
Last month I worked on a prototype of three simple pieces of technology: Eleventy
+ Tailwind
+ Netlify CMS
. I love a good mashup. Those are fairly distinct technologies with well defined roles, so I didn’t anticipate too many hiccups. I was more on the lookout for limitations or deal breakers. The first week was filled with excellent velocity and momentum, but I hit a wall during the second week. The preview portion of Netlify CMS
started falling apart. So I started investigating…
Here’s a breakdown of the technologies I used to build the prototype:
- Use
Eleventy
CLI to compileMarkdown
andNunjucks
- Use
Tailwind
to make those look nice - Use
TailwindUI
for some nice prefab styled components - Use
PurgeCSS
to makeTailwind
smaller - Use
PostCSS
to run theTailwind
,Purge
, andAutoprefixer
- Use
AlpineJS
to makeTailwind
interactive
This was functioning as intended with a few npm scripts
working in tandem. But the next step got a little slippery.
- Use
NetlifyCMS
to make updating markdown easier for content authors - Use
netlify-cms-proxy-server
CLI so that I can test the CMS locally - Use
nunjucks-precompile
CLI so thatNetlify CMS
’s Preview can use myNunjucks
templates - Use
rollup
to bundle content filters soNetlify CMS
can fully render theMarkdown
content (stolen from Hylia) - Use
React
to create<Preview/>
render components inNetlifyCMS
- Use
Babel
standalone to transpile theJSX
components.
This is where things started to break down. Alpine
wasn’t working within the React
preview component of Netlify CMS
. So all my menus were exploded and none of the interactive bits worked. I tried a few avenues for a quick fix like rewriting my <Preview/>
component class
with componentDidMount
as a function
with useEffect
. This produced more errors as JSX
got very mad because it doesn’t like the custom directives that Alpine
uses, but those were all red herrings, however. It smelled like the problem was between Alpine
and the React
portion of Netlify CMS
because the <Preview/>
frame had no knowledge of Alpine
.
I decided the next best thing I could do is isolate the problem. I took Markdown,
Nunjucks
, rollup
, and Netlify CMS
out of the equation entirely by writing a reduced test case in CodePen
to prove that I could get React
and Alpine
working together. Seeing it worked in CodePen
validated the hypothesis that the problem was on the Netlify CMS
side of things (or however it was rendering previews). I ported my simple CodePen
over to Netlify CMS
with a bit of modification to inject Alpine
and then I finally saw my problem.
I didn’t see it before because I had too much template code in the iframe
, but reducing the amount of code helped me finally pinpoint the issue. Alpine
was being injected, but it was on the parent window
context, not the document
context of the iframe
. Now I had to figure out how to get Alpine
inside the preview iframe
. Curiously, the iframe
didn’t have a src
or srcdoc
attribute, so it must be some quirky DocumentFragment
thing I’ve never really used before. Pinpointing this might be tough. So I started digging into the Netlify CMS
source code (this pretty far down the rabbit hole for a prototype, btw). In EditorPreviewPane.js#L188-L233 you can see where the iframe
is generated with <FrameContextConsumer>
from react-frame-component
. I know nothing about that, but you can see a way to pass preview styles into the <FrameContextConsumer>
, I wondered if you could pass scripts that way as well.
Before I embarked on a patch to Netlify CMS
, I filed a feature request with a mock solution. Thankfully, Erez Rokah (maintainer) figured out a way to get access to the document
from react-frame-component
, with a much smaller fix than I was proposing. A patch landed in a few days. That’s an amazing turnaround and an open source success. 🎉 Thanks, Netlify!
LEGO, plumbing, and cattle herding
So my little mashup, which was supposed to be just 3 technologies ended up exposing me to ~20 different technologies and had me digging into nth-level dependency source code after midnight. If there’s an allegory for what I don’t like about modern day web development, this is it. You want to use three tools, but you have to know how to use twenty tools instead. If modules and components are like LEGO, then this is dumping out the entire bin on the floor just to find one tiny piece you need.
This experience was flavored with a recent post by Jessica Joy Kerr “Back when software was craft” (and a thread by Justin Searls) talking about the industrialization of our industry. Over the years we’ve made the software industry even more of a knowledge-based industry. We’ve moved away from a bespoke “craft”-like industry with custom hewn boards and we now have a process that resembles a system of standardized parts.
Software feels more like assembly than craft.
— Jessica Joy Kerr, Back when software was craft
I definitely have felt this shift in my own life but have been unable to express it so simply. It feels like the job of programming has shifted from “Can you make this?” to “Do you have the knowledge to staple these two technologies together?” Kerr is more accepting of this reality than I am. The plumbing and glue code are not my favorite parts of the job. And often, you don’t truly know the limitations of any given dependency until you’re five thousand lines of code into a project. Massive sunk costs and the promise of rapid application development can come screeching to a halt when you run out of short cuts.
It reminds me of a parable I once heard:
One day a farmer, tired of plowing his field by hand, decided to build a barn and buy a bunch of cows to help tend the field. The field plowing did get easier, but eventually cows gave birth to more cows, and that farmer spent the rest of their life cutting hay to feed the cattle and shoveling their shit.
Tradeoffs, man.