Jan Miksovsky’s BlogArchive AboutContact

Introducing Graph Origami

I’ve made a video to introduce Graph Origami: a novel conceptual framework and toolset for building websites and other digital content using graphs as foundational design and development artifacts. This approach makes frontend and backend development in JavaScript enormously productive (and fun!).

Some features shown in the video:

I’ve been researching this approach for over two years and continue to be regularly delighted by what it lets you accomplish. If I can conceptualize what I’m trying to do as operations on a graph, the code required to express that tends to be concise and work the first time.

Graph Origami is not yet a product but is ready for you to try. I’m specifically looking for people who might be interested in collaborating on applying these ideas to their projects — I want to see how this approach works in a wider range of applications, and ensure the project evolves in a direction you’d find useful. If this sound interesting to you, please contact me.

The actual selection factors for new Unicode emoji proposals?

The Unicode emoji proposal process is open to the public in theory, but as far as I can tell, is currently accepting a tiny number of proposals based mostly on “things people like”. I submitted an emoji proposal for Person Pointing at Self, which was rejected.

The subcommittee has a challenging role. Adding emojis to every device (particularly cheap ones) has a real cost, and they want to keep costs low for their vendor members. At the same time, they want to satisfy/appease the world’s screaming demand for more emojis. The subcommittee’s publicly stated selection factors for emoji are:

  1. compatibility
  2. usage level
  3. distinctiveness
  4. completeness

That said, the committee rejected an emoji to represent “I”/“me”: possibly the noun with the highest frequency word across all languages, and one with thousands of years of consistent visual representation. There must be other factors at play.

Reading recent approvals like the new emojis in Unicode 15.0, we can infer that the subcommittee applies additional, undocumented criteria, which may include:

  1. Is it a heart or a face? People like those.
  2. Is it a popular food or charismatic fauna/flora? People like those.
  3. Is it an important cultural totem? Please like those — and it’s hard to tell a large group they don’t deserve representation.

If these really are something like the criteria applied to new proposals, the subcommittee leads people to waste their time crafting proposals that have no chance of being accepted. And limiting new emoji to “things people like” is a crabbed vision of what emoji could do for humanity. Focusing on things people actually talk about seems like a better, less arbitrary metric that would enable more substantive communication.

The one bright spot in the emoji submission process were the wonderful people at Emojination, especially emoji maven Jennifer Lee. She was the producer for the movie The Emoji Story (which is worth a watch), and leads an active and supportive Slack channel for emoji proposals. If you have any interest in submitting a proposal, I’d start there.

Emoji proposal for Person Pointing at Self

A while back I submitted a proposal to Unicode for a new emoji to represent “I” or “me”: Person Pointing at Self.

Proposed person pointing at self emoji

Given the ubiquity of this gesture around the world, I felt the value of this emoji was self-evident.

Harrison Ford as Han Solo pointing at himself

While this emoji proposal grew out of my tinkering on the Emojese emoji language, the emoji is language-independent; you could use it while texting in any language. If you open Emojese and turn on Experimental Emoji, you can try typing “I” or “me” in a sentence and see what it would be like to use Person Pointing at Self for this purpose.

Emojese emoji sentence for "I will send it to you"

Submitting an emoji proposal requires gathering a pile of data to address the emoji selection criteria. One criteria is durability: “Is the expected level of usage likely to continue into the future, or would it just be a fad?” There are examples of written hieroglyphs using this point-at-self gesture from ancient times. The Dongba script in southern China has been used for over 1000 years ago and is still in use today.

Dongba glyph for "me" showing a stick figure pointing at themselves

Your emoji proposal must also assess possible usage of your proposed emoji by comparing web search hit counts for the terms it represents against uses of the reference term “elephant”. Let’s see how searches for “I” and “me” stack up.

Google Trends chart showing many times more searches for "I" or "me" than the baseline "elephant" term

Hmm, the terms I/me are so incredibly popular that the baseline “elephant” term is indistinguishable from the x-axis. People talk about themselves more than anything else! This trend probably holds true across any time period or culture you care to examine — even the ones with lots of elephants.

An emoji intended to represent “I”/“me” doesn’t just beat any existing emoji proposal on usage and durability, it will likely beat all future proposals too.

Spoiler: the proposal was not accepted.

Unicode should add missing emoji for very common words

While developing vocabulary for the Emojese emoji language, I’ve found many common words that might benefit from new emoji. You can turn on Experimental Emoji in the app to see how these could feel in practice.

I think it would be worthwhile to deliberately fill out Unicode with emoji for the many common words without obvious emoji representations. Some of these concepts are quite abstract; any visual representation might be challenging, especially at small sizes. The Unicode Emoji Subcommittee requires new emoji proposals to be legible at 18x18 pixels, which is quite small. Still, you can get a sense for just how many of these concepts can be represented well.

The Emoji Subcommittee is very conservative these days when it comes to approving new emoji — but in recent years the same subcommittee approved 150 emojis for a single idea: 💑 Couple with Heart.

People justifiably want to see themselves represented, and in the case of 💑 that requires supporting two people, three genders (female, male, neutral), 6 skin tones (including the default yellow tone and the 5 skin tone modifiers). The combinations add up quickly.

💑 💑🏻 💑🏼 💑🏽 💑🏾 💑🏿 👩‍❤️‍👨 👩‍❤️‍👨🏻 👩🏻‍❤️‍👨 👩‍❤️‍👨🏼
👩🏼‍❤️‍👨 👩🏽‍❤️‍👨 👩‍❤️‍👨🏽 👩🏾‍❤️‍👨 👩‍❤️‍👨🏾 👩‍❤️‍👨🏿 👩🏿‍❤️‍👨 👩🏻‍❤️‍👨🏻 👩🏻‍❤️‍👨🏼 👩🏻‍❤️‍👨🏽
👩🏻‍❤️‍👨🏾 👩🏻‍❤️‍👨🏿 👩🏼‍❤️‍👨🏻 👩🏼‍❤️‍👨🏼 👩🏼‍❤️‍👨🏽 👩🏼‍❤️‍👨🏾 👩🏼‍❤️‍👨🏿 👩🏽‍❤️‍👨🏻 👩🏽‍❤️‍👨🏼 👩🏽‍❤️‍👨🏽
👩🏽‍❤️‍👨🏾 👩🏽‍❤️‍👨🏿 👩🏾‍❤️‍👨🏻 👩🏾‍❤️‍👨🏼 👩🏾‍❤️‍👨🏽 👩🏾‍❤️‍👨🏾 👩🏾‍❤️‍👨🏿 👩🏿‍❤️‍👨🏻 👩🏿‍❤️‍👨🏼 👩🏿‍❤️‍👨🏽
👩🏿‍❤️‍👨🏾 👩🏿‍❤️‍👨🏿 👨‍❤️‍👨 👨🏻‍❤️‍👨 👨‍❤️‍👨🏻 👨‍❤️‍👨🏼 👨🏼‍❤️‍👨 👨🏽‍❤️‍👨 👨‍❤️‍👨🏽 👨‍❤️‍👨🏾
👨🏾‍❤️‍👨 👨🏿‍❤️‍👨 👨‍❤️‍👨🏿 👨🏻‍❤️‍👨🏻 👨🏻‍❤️‍👨🏼 👨🏻‍❤️‍👨🏽 👨🏻‍❤️‍👨🏾 👨🏻‍❤️‍👨🏿 👨🏼‍❤️‍👨🏻 👨🏼‍❤️‍👨🏼
👨🏼‍❤️‍👨🏽 👨🏼‍❤️‍👨🏾 👨🏼‍❤️‍👨🏿 👨🏽‍❤️‍👨🏻 👨🏽‍❤️‍👨🏼 👨🏽‍❤️‍👨🏽 👨🏽‍❤️‍👨🏾 👨🏽‍❤️‍👨🏿 👨🏾‍❤️‍👨🏻 👨🏾‍❤️‍👨🏼
👨🏾‍❤️‍👨🏽 👨🏾‍❤️‍👨🏾 👨🏾‍❤️‍👨🏿 👨🏿‍❤️‍👨🏻 👨🏿‍❤️‍👨🏼 👨🏿‍❤️‍👨🏽 👨🏿‍❤️‍👨🏾 👨🏿‍❤️‍👨🏿 👩‍❤️‍👩 👩🏻‍❤️‍👩
👩‍❤️‍👩🏻 👩‍❤️‍👩🏼 👩🏼‍❤️‍👩 👩🏽‍❤️‍👩 👩‍❤️‍👩🏽 👩‍❤️‍👩🏾 👩🏾‍❤️‍👩 👩🏿‍❤️‍👩 👩‍❤️‍👩🏿 👩🏻‍❤️‍👩🏻
👩🏻‍❤️‍👩🏼 👩🏻‍❤️‍👩🏽 👩🏻‍❤️‍👩🏾 👩🏻‍❤️‍👩🏿 👩🏼‍❤️‍👩🏻 👩🏼‍❤️‍👩🏼 👩🏼‍❤️‍👩🏽 👩🏼‍❤️‍👩🏾 👩🏼‍❤️‍👩🏿 👩🏽‍❤️‍👩🏻
👩🏽‍❤️‍👩🏼 👩🏽‍❤️‍👩🏽 👩🏽‍❤️‍👩🏾 👩🏽‍❤️‍👩🏿 👩🏾‍❤️‍👩🏻 👩🏾‍❤️‍👩🏼 👩🏾‍❤️‍👩🏽 👩🏾‍❤️‍👩🏾 👩🏾‍❤️‍👩🏿 👩🏿‍❤️‍👩🏻
👩🏿‍❤️‍👩🏼 👩🏿‍❤️‍👩🏽 👩🏿‍❤️‍👩🏾 👩🏿‍❤️‍👩🏿 👨🏼‍❤️‍👩🏿 👨🏽‍❤️‍👩 👨🏽‍❤️‍👩🏻 👨🏽‍❤️‍👩🏼 👨🏽‍❤️‍👩🏽 👨🏽‍❤️‍👩🏾
👨🏽‍❤️‍👩🏿 👨🏾‍❤️‍👩 👨🏾‍❤️‍👩🏻 👨🏾‍❤️‍👩🏼 👨🏾‍❤️‍👩🏽 👨🏾‍❤️‍👩🏾 👨🏾‍❤️‍👩🏿 👨🏿‍❤️‍👩🏻 👨🏿‍❤️‍👩🏿 👨🏿‍❤️‍👩🏾
👨🏿‍❤️‍👩🏽 👨🏿‍❤️‍👩🏼 👨‍❤️‍👩🏿 👨🏿‍❤️‍👩 👨‍❤️‍👩🏾 👨‍❤️‍👩🏽 👨‍❤️‍👩🏼 👨🏻‍❤️‍👩 👨🏻‍❤️‍👩🏻 👨🏻‍❤️‍👩🏼
👨🏻‍❤️‍👩🏽 👨🏻‍❤️‍👩🏾 👨🏻‍❤️‍👩🏿 👨‍❤️‍👩🏻 👨‍❤️‍👩 👨🏼‍❤️‍👩 👨🏼‍❤️‍👩🏻 👨🏼‍❤️‍👩🏼 👨🏼‍❤️‍👩🏽 👨🏼‍❤️‍👩🏾

Clearly “love between two people” is an important concept, but we do talk about many other things in our daily lives, and a substantial number of those don’t have any clear emoji representation yet.

Imagine if we introduced 150 new emojis to represent actions and ideas which are extremely common in everyday conversation but which have no obvious representation today! Surely that would be worthwhile?

Emoji’s destiny is to become a global written pidgin language

Emoji week continues! With emoji as an expanding foundation for cross-cultural communication, I don’t think it’s a stretch to envision the eventual emergence of a basic grammar and the identification of existing emoji for specific abstract meanings. Emoji will form the basis of a written pidgin language: a simplified language that is incomplete but can nevertheless fill a useful role.

Emoji dramatically extend the world’s set of commonly-understood symbols. There were universal symbols before emoji, but they were generally confined to domains like math, science, and music. Emoji has increased that universal set by an order of magnitude. I assume it’s also shifting what can be understood across cultures.

As a software designer in the 1990s, I received instruction from international localization teams to avoid using gestures like 👍 in icons because some cultures interpreted them as offensive. As software has eaten the world, the culture of its makers has spread as well. I assume that in the context of software most people around the world can now recognize the intended positive meaning of 👍 regardless of their cultural background.

Any specific constructed emoji language like the Emojese emoji language is unlikely to take root — but we can watch for something similar to spontaneously emerge and spread around the world. Isn’t that a beautiful idea worth encouraging?

My guess: a group of charismatic young musicians stretches emoji into a pidgin as a way to connect with their global fanbase of teens, who pick up the pidgin to express their enthusiasm and connect across a language boundary. Thirty years later, those fans can stay at an AirBnB with checkout instructions like ◐⏰ 🫵 ⎋, 📦⬇️ 🚮⬚ 📥 🗑️ ⩕ 🚗Ⓟ📍

Strategies for picking emoji for words that don't have emoji

Creating a written language from emoji is challenging. Any constructed visual language faces the problem of identifying vocabulary. This problem is even harder with the Unicode emoji set, which is skewed to faces, hearts, animals, occupations, foods, flags, and cultural totems. There are many common words without obvious emoji representations.

To compensate for this, the Emojese emoji language resorts to a number of strategies to identify emoji sequences that can represent something new.

A particularly productive strategy is to designate certain emoji or glyphs to communicate abstract ideas and incorporate these into larger glyphs to contribute those abstract meanings. Chinese and Japanese kanji have characters called “radicals” that express such core meanings. For an emoji language, we can pick specific emoji and Unicode glyphs to fill the same role as radicals in emoji sequences:

The goal is to produce sequences which, while perhaps not obvious on first reading, are good enough and consistent enough to be remembered. Using this and other strategies, Emojese now has 1000+ common words, which is large enough to be general purpose and to communicate some complex ideas.

Emojese: a written emoji language

The Emojese emoji language is rich enough to express complex thoughts entirely in emoji and other Unicode glyphs.

I think emoji languages are a fun idea, but when I looked for one in 2020, I couldn’t find anything that was: a) satisfying, and b) sendable as real text. My kids and I explored ideas which I formalized into Emojese and embodied in the above app for writing and reading sentences. Emojese is a pidgin language with a handful of grammar suggestions and emoji definitions for 1000+ words.

A friend and I had a ton of fun texting back and forth in Emojese for several months — a 🕵️ Secret Decoder Ring for texting! Before long, we could read many messages before decoding them in the app. I expect you would experience similar results.

All emoji languages initially look silly/overwhelming — it’s hard to read so many unfamiliar images, the choice of symbols feels arbitrary, and the constraints of existing emoji result in unfamiliar pairings like 👉☀️ for “today”.

The best I can hope for is that, after seeing a translation, you feel it’s reasonable and that the few unavoidable abstract symbols are memorable. If the language is consistent in using 👉 as “this” and “☀️”, then 👉☀️ for “this day”/“today” begins to feel acceptable.

This lets you write things roughly like Randall Monroe did with a 1000-word vocabulary in his Up Goer Five comic and Thing Explainer book — only in emoji. 🔮⋯ ⬚👈 ▶️ 👉⟿ ↬ 🌌 ⇒ 🫵 ⤻💁 👎 😬💦 & 🫵 ⤻🚫 → ↬ 🌌 👉☀️

One of my kids and their high school friends had fun playing with Emojese messages on their Discord server. It took about 10 minutes for the conversation to veer into insults like 🫵の👩‍🍼. I was happy they were entertained — the whole point of the project is let people have fun.

Consolidating blog content

For the new year, I consolidated content from three earlier blogs to create a new blog at https://jan.miksovsky.com. This includes content from: a) flow|state, a UI design blog I wrote from 2005–2014, b) a blog for my UI component framework called QuickUI from 2012–2013, and c) the blog for my second startup, Component Kitchen, which was generally focused on web components from 2014–2020.

My goal has been to create a single site for my professional work. I’ve been invigorated by the resurrection of interest in blogging spurred by Twitter’s recent self-immolation, and by my general growing interest in removing corporate gatekeepers from our public presences.

I did the porting and consolidation, as well as the ongoing serving of it at this new location, using a new set of tools I hope to begin discussing publicly soon.

Building a Storybook-like demo browser with web components — a much simpler way to get most of the benefits

I recently tried Storybook, a popular tool for browsing the demos and documentation for a UI component library. I think Storybook offers a good user experience, but its developer experience entails complexity out of proportion to its benefits. I tried creating a simpler web component library browser using web components, and am happy with the result.

Storybook: The good parts

Storybook is a good idea. It’s helpful to be able to quickly browse a component library and see the range of what each component can do.

Storybook offers useful features like:

How to define a demo written in HTML? Hmm…

My own experiments with Storybook did not pan out, and I found it too heavy for the relatively simple problem I was trying to solve. (You can find my notes from that experience in an appendix following this post.)

For me, the crucial turning point came when I was trying to define my first story demo in Storybook. Since my project is creating plain web components, I just had a bit of regular HTML I wanted to use for a story.

But to define my HTML demo in a common Storybook configuration, I had to create a markdown document of some flavor I’d never seen before (“.mdx”), and then put something like this in it:

```js
export function MyElement() {
  return html`<my-element>Hello, world.</my-element>`;
}
```

Here we’ve got HTML inside of a lit-html JavaScript template literal inside of a JavaScript function inside of a JavaScript code block inside of a markdown document.

Whew! All that to display some HTML.

Huh. If only web browsers gave us some native way to write HTML…

Oh, right — they do! It’s called HTML.

What I want to write is an .html file that includes the demo as plain HTML:

<my-element>Hello, world.</my-element>

A web component library browser made of web components

Stepping back, the Storybook application runtime UI is actually not that complex. There are certain useful UI elements that appear on Storybook pages, like the story index and the “View code” buttons. The web already provides an easy, standard way to implement reusable UI elements: web components.

I built a simple component library browser that achieves perhaps 60% of what I want out of Storybook, but in a more straightforward fashion using web components. It’s meant for local development, but I’ve quickly posted an unbundled version of that if you want to see the Elix web component library demos.

The first iteration closely followed the Storybook UI:

Building this with web components, and generally using the web platform more directly, makes it possible to do much (not all) of what Storybook does much more simply and flexibly.

Since there’s not much going on here, there’s very little new that someone has to learn to use it. If someone needs to be taught how to do something in HTML — then they’re learning something useful for the rest of their career! The HTML pages they create with this approach are as future-proof tech as one can ask for, and are simple enough to work until the heat death of the universe.

I eventually changed the page styles to get a plainer look which I felt put more attention on the demos:

What I left out

Here are some of the Storybook features I did not implement:

Defining the key story browsing UI in web components lets you maintain complete control of the top-level pages and project infrastructure. The separate aspects of this approach are simple, small, loosely-coupled pieces that can easily be replaced as needs change.

It’d be pretty interesting to see this approach built out into an ecosystem of web components and other simple parts which work well together. That could ultimately comprise a compelling, web-oriented alternative to Storybook.

Conclusion

A component library browser like Storybook is a useful tool. After developing the initial story browsing components for the Elix library, I discovered small UI regressions in a couple of demos, simply because it was easier for me to quickly experience all the demos in action.

But I think the benefits of a component library browser can be achieved in ways that work with the grain of the web, squeezing every advantage out of solid technology you already know well.

 


Appendix: Notes on trying Storybook

My goal in writing the above post was not to focus on Storybook, but on addressing what it does in a simpler way. From what I can see online, many people love Storybook. If that’s you, that’s great! I’m glad you’ve found a tool that meets your needs.

If you’re considering adopting Storybook, and are interested in knowing ahead of time what its downsides might be, I’m including my notes from my experiments with it here. Your Mileage May Vary.

  1. Storybook appears to have been originally designed to browse React component libraries. It shares with React an approach that covers up much of the browser platform with proprietary JavaScript abstractions in the name of developer ergonomics. I am personally skeptical of that approach.
  2. Storybook is a full-blown application server in its own right that must be accommodated inside the host project.
  3. Installing and configuring the Storybook application in an existing project is a non-trivial task.
  4. Various project generators exist that can pre-populate a project with the required files. These all assume that you don’t want to understand how the resulting application actually works. They create a lot of files, tell you how to start the application, and then you’re on your own.
  5. The results of running one of these project generators are, in my experience, rigid and brittle. Attempting to diverge from the generated project is likely to break the Storybook application in ways which are difficult to diagnose or resolve.
  6. Storybook is still focused primarily on React development. There are project generators aimed at web component developers, but these don’t yet feel like first-class citizens of the Storybook ecosystem.
  7. To the extent Storybook does support web component development, it focuses on frameworks like Polymer/lit-element rather than plainer JavaScript web component development.
  8. The extensive toolchain imposed by Storybook, and the run-time environment of the Storybook UI, can complicate debugging. When you’re trying to debug a UI element, any other UI code on the page can make it harder to isolate the problem. So while Storybook may make it easier for designers and other developers to browse your component, it can get in the way of letting you actually create those components in the first place.
  9. Storybook forces use of a build process, even if the underlying web components don’t need a build process. Even for a minimal web component project, Storybook builds are slow.
  10. Storybook adds a set of huge set of files. Depending on which starting point you use, the configuration can be massive.
  11. Storybook has an extensive set of configuration options, add-ons, etc. It’s probably possible to get whatever UI you want in a Storybook application. However, the way you configure UI in Storybook will be substantially different than how you would create the exact same UI in an application of your own.
  12. In other words, learning how to do UI inside of a Storybook app isn’t teaching you anything about building app UI outside of Storybook. When the day comes that you decide to present your component library in any other way — or work on literally anything else — your Storybook knowledge will not help you.
  13. Storybook appears aimed first at local developer use, and only secondarily at the task of making documentation for internal or external consumers of component libraries. Many organizations creating UI components need both.

Stepping back, Storybook is essentially a complete CMS (Content Management System). It’s reinventing CMS infrastructure with the presumption that browsing web component libraries is a special task that deserves its own ecosystem. I question that premise.

Even if the premise were true, companies that provide web component libraries already have a CMS for everything else they do. Storybook isn’t large enough to encompass an entire corporate site, and it’s doubtful that companies would want to switch their developer site CMS just to be able to offer a prebuilt web component library browsing UI.

So if your company is using Storybook and ever decides to make its components available to external developers, you will have to reconcile your internal and external documentation platforms. You will need to either: a) shoehorn Storybook into your developer site, b) reimplement the Storybook functionality in the context of your existing developer site so people can browse the same demos there, or c) rewrite all your demos and documentation in whatever form your existing site needs, and then try to keep those in sync going forward. None of those options is attractive.

Perhaps the most puzzling thing to me is that, for a project designed to showcase UI component libraries, Storybook itself is not presented as a collection of UI components. It’s built internally from components, presumably React components, and it looks like the project is beginning to make that UI componentry available to developers. But components are still not the dominant paradigm for working with Storybook.

I spent half a day trying to get Storybook working in the context of an existing web component project, with an eye towards someday using it to document the general-purpose components in the Elix web component library.

Unsurprising code and magic — optimizing for the first time vs the nth time

I’ve read some recent back-and-forth on Twitter regarding whether “magic” in a framework or library is something to be avoided or sought. We’ve spent a fair amount of time over the past year rewriting the core of the Elix web components project to remove what we felt were magical aspects. I wanted to write down some reasoning for that, both to better understand my own instincts, and also as a way of explaining those changes in Elix.

Writing for the 1st or nth time

Dealing with code to solve a problem more than once invokes two opposing forces in tension: 1) how much effort someone needs to invest to understand, read, or write something the first time and 2) the effort someone needs to expend to understand, read, or write that same something for the nth time.

We could represent the possible resolutions of this tension on a spectrum, where the left end represents optimizing for the first time and the right end represents optimizing for the nth time:

Optimize for first time ⟷ Optimize for nth time

The Principle of Least Astonishment, also known as the Principle of Least Surprise, suggests that “a component of a system should behave in a way that most users will expect it to behave; the behavior should not astonish or surprise users.” This property sounds highly relevant to the left side of the spectrum. We could characterize that end as “straightforward” or “unsurprising”; it also tends to be “flexible”. A negative characterization might be “verbose”.

On the other end, positive characterizations of the right end of that spectrum might be “concise” and “efficient”; negative characterizations might be “surprising” or “hard to learn”, and possibly “inflexible”.

Programmers evidently disagree on a definition for “magic”, but when I find code I personally consider to be magic, it’s at the right end of this spectrum.

A coding tour of this spectrum

Let’s walk through some examples of how an identical bit of functionality might be expressed at different points along this spectrum, working our way from left (first time, unsurprising) to right (nth time, potentially very surprising).

Suppose we have a class Foo that wants to log a message “Hello” in its constructor, and the output should be “Foo: Hello”. (Let’s take it as a given that we want to create a class; if all we really want to do is log a message, we obviously don’t need a class.)

Solution 1

A completely straightforward solution at the far left end of the spectrum:

class Foo {
  constructor() {
    console.log("Foo: Hello");
  }
}

To a JavaScript developer, there is zero surprise in this code: it’s all standard JavaScript syntax and a single call to a well-known web platform API to log a string. When they run this code, the resulting console message will be 100% expected. This code is also completely flexible: if the developer wants to change the message, or log two messages, or whatever, they have the freedom to do so.

In this trivial example, we could easily decide we want to stay on this unsurprising side of the continuum, and ask everyone on our project writing one of these classes to copy-and-paste this boilerplate into their code. That’s an eminently reasonable answer.

Solution 2

But maybe our little logging task gets more complex, and the amount of boilerplate creeps up to a few lines, or a dozen. In that case, we might want to factor things out. We want to trade off a tiny bit of conceptual load for brevity. We begin to consider how much support we want to give ourselves for writing this code for the nth time. We feel tugged along to the right of this spectrum.

If logging begins to entail some complexity, we could create a utility function to log our message:

import log from "./log.js";

class Foo {
  constructor() {
    log("Foo: Hello");
  }
}

This has the potential to be ever-so-slightly surprising to a new project member. They’ll be told to use the boilerplate, but they can’t be completely sure what log is going to do unless they read the source. We’re relying here on the obviousness of the library function name “log” to eliminate or reduce surprise. If we choose our name well, a dev will be able to imagine what the API will do. In the above case, seeing a console message will not be surprising.

Solution 3

There may come a point where we have other requirements for our classes, and see the introduction of a number of other API methods our devs should remember to call. We could introduce a bit of framework for these classes in the form of a base class to provide those APIs:

import Base from "./Base.js";

class Foo extends Base {
  constructor() {
    super();
    this.log("Foo: Hello");
  }
}

The functionality is equivalent — although we’ve introduced a potential for a great deal more surprise because the constructor needs to call super. That could be helpful; there may be useful initialization the Base superclass can provide. Significantly, some of that useful functionality could be added to Base in the future, and the Foo class here would benefit even if its code were left unchanged.

The tradeoff is that our class author now has a bit of framework under their feet, and may be a little surprised what it does, especially if that behavior changes underneath them without notice.

Solution 4

If devs on our project occasionally forget to call this.log(), one recourse would be to move the logging call to the superclass, and have the subclass pass the message-to-be-logged to the super constructor:

import Base from "./Base.js";

class Foo extends Base {
  constructor() {
    super("Foo: Hello");
  }
}

If we’re using a type-checking system like TypeScript, we can define the signature of the Base class constructor such that the message is required. In that way, if the dev forgets to pass a message, or forgets to define a constructor entirely, the type-checker will produce a compile-time error. That should serve as an effective reminder.

That’s a powerful degree of enforcement — but note that we’ve lost some of the clarity of our code. That string parameter to the super constructor has no obvious identity or purpose. When the superclass logs that message to the console, that could easily surprise the developer.

Another point worth noting is that our solution is now less flexible. The downside of requiring a string parameter in the constructor is that the developer must always supply a string. If they don’t want to log a message, or want to log two messages, etc., they’ll have a harder time figuring out the best way to do so.

Solution 5

We could restore some clarity by introducing a property which the base class constructor will retrieve from the subclass:

import Base from "./Base.js";

class Foo extends Base {
  get message() {
    return "Foo: Hello";
  }
}

Although the purpose of the string is now a bit clearer — it’s a message of some kind — its involvement in the class is quite unclear. From the above, it’s not apparent whether the message code gets called at all.

The message property is essentially a new lifecycle method defined by the Base class. That’s a powerful technique, but the timing and behavior of a lifecycle method are opaque unless one reads the documentation. It’s unclear, for example, when that property will be retrieved, whether it might be retrieved once or multiple times, whether it should return the same value each time, whether the value will be cached, etc.

Solution 6

One thing we may notice as our team copies-and-pastes this boilerplate is that people may occasionally forget to update the name of the template class “Foo” in the message string. To address that, we could have the Base class itself obtain the class name from the subclass using reflection, e.g., by retrieving this.constructor.name.

import Base from "./Base.js";

class Foo extends Base {
  get message() {
    return "Hello";
  }
}

This is more concise, but when the class logs “Foo: Hello”, it may take the dev a minute to figure out where the “Foo” part came from. That’s possibly surprising.

What will be extremely surprising is compiling or minimizing this class and discovering that it logs something like “a: Hello”. Build tools may transform source code, including class names, so the class Foo might be renamed a for brevity. This interference between library abstraction and build tools can often be baffling, especially if you’re familiar with only one layer or portion of a complex system.

Solution 7

If our team is writing dozens of these classes, we might decide to optimize for brevity even further. We might decide to use still-unstandardized JavaScript decorators, say:

import Base from "./Base.js";

@message("Hello")
class Foo extends Base {}

This is quite concise. In exchange, it’s also quite limited. For one thing, since decorators are not yet standard JavaScript (as of this writing), we’re forcing the project to use a transpiler to process the decorator.

For another thing, depending on how it’s implemented, the decorator may run with different timing than the earlier message getter. The decorator is also more constrained than a handwritten property getter. Depending on the size of our project, we might decide that such constraints are beneficial — or we might discover that they place significant limitations on a developer trying to handle edge cases.

[Update: @pmdartus pointed out that the message decorator would have to be imported from somewhere.]

Solution 8

Suppose our team is now writing hundreds of these classes. Maybe those classes are now directly related to our company’s core business, and we make money for every class we write!

We could create a domain-specific language just to crank these classes out. We could, say, define a JSON format, and put the following in Foo.json:

{
  "message": "Hello"
}

And then have a build tool consume this format to generate a class with the name and message we want. That’s extremely efficient, but it’s getting pretty hard to tell from the above what’s going to happen. This also requires a new developer to install and learn a proprietary tool, which is going to drive up the time required to write their first class.

And the result is now quite rigid: since there’s no place here for the dev to contribute code, there’s no ability for them to accommodate unusual situations.

Solution 9

Why stop there? Let’s define a new file format that contains nothing but the message we want to log, and take the class name from the name of the file. We create a new file called Foo and put the message into it:

Hello

We compile this to generate a class, run that, and see “Foo: Hello” in the console. Was that expected?

We’re working at a theoretical edge now, as every bit of information is tuned to our task. We’ve got a system that’s highly optimized for our needs. You may have a different definition for “magic”, but this solution feels pretty magic to me.

This solution is probably quite surprising to the uninitiated. The line of “code” above certainly looks simple, but it’s impossible to guess what it will do until you run it.

The solution would be extremely efficient for teams that need to crank out these classes, but the solution is also completely rigid. It can’t do anything other than what it’s designed to do.

Visualizing this trade-off

We can do some simplistic analysis of these solutions. For starters, while code size doesn’t equate directly to nth time efficiency, it’s a reasonable proxy. We could assume that having to write less code for the nth occurrence means being more efficient over the long term.

It’s rather more difficult to represent the on-boarding work required to be able to read or write the code the first time, which gets to the very appeal of the left end of the spectrum. For the sake of argument, I imagined an exponential increase in the time required to get a new developer on board: the minutes required to read docs, install and learn build tools, ask questions when things don’t work, etc.

This representation doesn’t capture everything.

This entire example is contrived, but surely there are correlations at work like this whenever we’re resolving the 1st time/nth time tension.

Significantly, that tension applies to reading and understanding code, not just writing and maintaining it. A solution at the left end of the spectrum might entail more code then one at the right end, but a new developer should be able to more readily understand what code at the left end does and how it actually works.

I think it’s interesting to consider this spectrum through some analytical lens. Surely we could take a more data-driven approach to assessing where we are — and deciding where we want to be — on this spectrum.

Reflection

Positions along this spectrum are not objectively good or bad — it depends on what you’re trying to achieve and optimize for.

In the case of the Elix project, we want to optimize for: a) a large audience of professional web developers that can quickly understand the code, and b) a high degree of flexibility. We want people to build a wide range of solutions with the project’s web components, so we prefer to preserve flexibility and possibly sacrifice nth time efficiency. In other words, we deliberately target the left end of this spectrum.

As a case in point, we recently deprecated an Elix helper function that manipulated a web component template in a particular way. The helper allowed for efficiency, but was novel, and we ultimately felt that it wasn’t all that much more efficient than calling the underlying DOM API directly. We’d prefer to just encourage developers to call a DOM API they already know extremely well. We may someday feel that we understand the problem space better, and decide to better support nth time use by adding back such a helper, but for now we’re happy to keep the Elix code unsurprising.

Older posts