b8r—lessons learnt

If you read my blog much, you’ll probably find occasional discussions of b8r to be pretty positive compared to the blowtorch I tend to turn on most things I discuss. Weird, huh? So, after nearly two months of trying to build a new business from scratch using b8r, what have I decided I don’t like about b8r?

Yeah, better autocomplete would be nice

I definitely spend more time looking at my own documentation than I would like to admit. Then again, I also can’t remember APIs I’ve been using for 20 years, or whether it’s for of or for in. I don’t think this is egregious, but it’s definitely nice that VS Code knows what the parameters are for most DOM API, and it’s tantalizingly wonderful that it can infer theme for some of b8r’s APIs (presumably a TypeScript rewrite would make that pretty much guaranteed, but I wonder if I could get the same result more cheaply by tweaking the existing docs.

Nesting Components is Tricky

By and large, I haven’t found myself using component composition very often, and so there are rough edges to it that I wish there weren’t. Because even the simplest thing in React or Angular tends to require quite a bit of composition, that stuff works fairly well and consistently.

b8r’s collection of components is hard to reuse

While, within a project, we found the components built with b8r to be enormously flexible and reusable, the components provided as part of b8r were written at different times with different ideas of what a “nice” component would be like, and with different levels of battle-testedness. So, I’ll often find myself wishing I could just use a b8r component “off-the-shelf” but end up writing a new one from scratch.

this is all crap, and should be rewritten from scratch

While I was suffering with Angular at Google, I desperately wanted to replace it with something better (more like b8r, or fuck it just b8r). Eventually I moved to a project where this would actually be possible, and tried to get b8r accepted as a third-party library within google3.

This quickly started looking like an exercise in epic frustration (I was trying to get something out the door and in front of other developers within a very tight time-frame, and the approval process would take longer than I had. So, I decided to find out how hard it would be to rewrite just the bits of b8r I really wanted from scratch. Also, for extra credit, it should allow for components to be 100% javascript (no HTML in strings) so that Google’s code obfuscation pipeline could mangle it.

I got it done in a couple of evenings (except for the last bit, which I only did to proof-of-concept-given-a-bit-of-hand-waving level). In a nutshell, you write HTML and b8r-style bindings as you do now, but during the initial build (yes, everything at Google has to go through an immensely convoluted build cycle) the HTML gets converted to JSON which in turn generates javascript code-bindings (e.g. <div data-bind=”text=foo.bar”> would become

const elt = createElement(‘div’)

const elt = createElement('div')
elt.textContent = REDACTED.foo.bar
REDACTED.foo.onchange(() => elt.textContent = REDACTED.foo.bar)

I think we can all agree that this is pretty. fucking. slick.

Anyway, the fact that I could get all that working so quickly and leave all the cruft of b8r behind is so very tempting, but of course b8r is pretty battle-tested (a couple of years underpinning a social media client in active use, even if it’s only a few thousand users, is not nothing).

As an aside, what did the rewrite do?

  • regularized the syntax for data- and event- binding (no “wait, a colon goes there?”)
  • provided no component system; assumed that would be web-components (e.g. lit-element or similar)
  • the registry was built entirely on Proxy
  • a lot of kludgy-mappings (e.g. _b8r_listInstance) replaced with WeakMap

It was beautiful, and I’m very sorry it’s all in the bowels of google where it will likely never be seen or used again. But I’m getting acutely aware of my mortality and I have better things to do than fight five years of political battles against a bunch of already entrenched frameworks AND very talented people with great ideas on how to replace those frameworks.

web-components, are they really the future?

I left google with the strong opinion that web-components are the future. And I still think this is true, but that said, they’re more useful for small, low-level pieces (replacing bad native elements, like <select> or creating new, badly needed components, like tab selectors, combo controllers and tag lists). When you’re building complex views, b8r’s components still reign supreme. And in fact you can build a lot just using b8r and bare HTML, and then swap in the nice web-components as you construct them, which gets to my single biggest annoyance with b8r so far…

it’s hard to bind to value

One of the design goals of b8r was to be able to swap in a b8r component for an HTML <input> element with zero or no friction. An input element has a bunch of nice properties, e.g.

  • it initializes to its value attribute
  • it’s a direct property of the element, i.e. input.value
  • it emits focus and blur events
  • when the user messes with it, it emits input events
  • when the users changes it (blurs it with a new value), it emits a change event

So the goal was that if you had a bound <input data-bind="value=path.to.foo"> you could replace it with a b8r component <b8r-component data-bind="value=path.to.foo"> and nothing would have to be changed.

Now, this isn’t an abject failure. It mostly works. The problems come from (of course) edge cases, often driven by the desire to not share the <input> element’s many less than ideal properties.

  • the value of some inputs is not what you’d think (e.g. checkboxes and radio buttons) and it doesn’t preserve the type of a value passed to it. The value of an <input type="number"> is never a number, nor is the value of an <input type="date"> a Date.
  • don’t get me started about radio buttons.
  • or checkboxes.
  • a value attribute is always going to be a string.

Anyway, so the goal was for b8r to make it easy for a component to replace an idealized input.

While I have lots and lots of gripes about b8r here, it’s not like any other framework even tries to do this, let alone does it well. This is work-in-progress and it just irks me how much is left to do.