<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>generic.cx posts</title><link href="https://generic.cx/" rel="alternate"></link><link href="https://generic.cx/feed/atom/" rel="self"></link><id>urn:uuid:d76f4e3f-6763-33ef-96b5-48ba11809df8</id><updated>2024-05-11T15:24:32Z</updated><author><name></name></author><entry><title>depend-encies</title><link href="https://generic.cx/essays/dependencies/" rel="alternate"></link><updated>2017-05-16T00:00:00Z</updated><author><name>marcos</name></author><id>urn:uuid:2cebee5c-345d-336d-af05-0caa34bffdda</id><content type="html">&lt;p&gt;I've been playing with firebase recently. Firebase is a fun system, it's like hosted couchdb but without the views and like appengine but with more batteries included. Its api is interesting because one of its big selling points is its realtime database.&lt;/p&gt;
&lt;p&gt;For a todomvc-like application, this is the sort of thing your write:&lt;/p&gt;
&lt;p class="codeHeading"&gt;firebase crud events&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`todos/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_added&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// add new child todo snapshot to your local ui&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_deleted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// delete snapshot in your local ui&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_changed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// update your local version of todo with new snapshot&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;here, the data at &lt;code&gt;todos/${uid}&lt;/code&gt; contains a list of todo items, when one is added, a &lt;code&gt;child_added&lt;/code&gt; event is fired on the database ref and a callback is invoked with the new item. when one is deleted, the &lt;code&gt;child_deleted&lt;/code&gt; event is triggered and when one changes, &lt;code&gt;child_changed&lt;/code&gt; is triggered so you can update it locally. This is a pretty solid set of events and you can probably see how you might integrate it into a redux-ey webapp.&lt;/p&gt;
&lt;p&gt;at first glance, you might do something like this:&lt;/p&gt;
&lt;p class="codeHeading"&gt;using componentDidMount&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react-redux&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;firebase&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./todo-actions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`todos/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_added&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_deleted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_changed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;componentWillUnmount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// populated by todoActions&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;
&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;here &lt;code&gt;todoActions&lt;/code&gt; are standard &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;CRUD&lt;/a&gt;-y redux actions. it's not even important what they actually look like because they are pretty boring in the grand scheme of things.&lt;/p&gt;
&lt;p&gt;What is interesting is the &lt;code&gt;componentDidMount&lt;/code&gt; call in the component. This is the standard approach that the react docs suggest for dealing with external xhr dependencies and firebase fits the bill here.&lt;/p&gt;
&lt;h3&gt;firebase and redux&lt;/h3&gt;
&lt;p&gt;What sets firebase apart from other systems (and even stock rest+redux) is that the firebase client manages local data even dealing with synchronizing it to the remote server. That is, when you "change data on firebase" the client code optimistically syncs your changes locally on the client and immediately calls &lt;code&gt;child_added&lt;/code&gt;, &lt;code&gt;child_changed&lt;/code&gt;, or whatever.&lt;/p&gt;
&lt;p&gt;As a &lt;em&gt;user&lt;/em&gt; of firebase, you are totally insulated from this, however, because the firebase client library takes care to always emit the correct events to your appropriate database ref regardless of your connection status, it's only up to you to determine if you want to make your users aware of long-running queries.&lt;/p&gt;
&lt;p&gt;This is pretty neat, but also...&lt;em&gt;why even use redux&lt;/em&gt; when i have this nifty clientside api at my disposal? And you wouldn't be alone in asking this question because there are two somewhat competing libraries trying to bridge this gap—&lt;a href="http://react-redux-firebase.com/"&gt;react-redux-firebase&lt;/a&gt; and &lt;a href="https://github.com/tiberiuc/redux-react-firebase"&gt;redux-react-firebase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Both use the model of redux's &lt;code&gt;connect&lt;/code&gt; &lt;a href="https://facebook.github.io/react/docs/higher-order-components.html"&gt;higher order component&lt;/a&gt; approach to provide firebase data to feed data to a component like so:&lt;/p&gt;
&lt;p class="codeHeading"&gt;react-redux-firebase example code&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;firebaseConnect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dataToJS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react-redux-firebase&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lodash/flow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;firebaseConnect&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;todos/${user.uid}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dataToJs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`todos/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;])(&lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;in both &lt;code&gt;react-redux-firebase&lt;/code&gt; and &lt;code&gt;redux-react-firebase&lt;/code&gt;, the component mounts and firebaseConnect takes care of synchronizing the firebase state with redux, as a result, your firebase data is never really part of your redux store even though it is accessible to your components via &lt;code&gt;connect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And this is a pretty conceptually sane implementation, but, on some level, it is a bit weird that we're hijacking react-redux's &lt;code&gt;connect&lt;/code&gt; this way given that firebase in this case is essentially a parallel redux store with its own thing going on.&lt;/p&gt;
&lt;h3&gt;soooo, why redux?&lt;/h3&gt;
&lt;p&gt;A big selling point of firebase is its moderately low-latency subscription-based datastore with robust client libraries supporting flaky network states. I read those words and all I can think is &lt;em&gt;"exactly &lt;strong&gt;what&lt;/strong&gt; benefit does redux provide?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Stepping back, most apis are &lt;em&gt;not&lt;/em&gt; subscription-based and most do not include a clientside api that is capable of managing offline application state (ignoring time-travel, elm, etc this is arguably the point of having a client store like redux). But in a firebase app, you can &lt;em&gt;always&lt;/em&gt; &lt;code&gt;.push()&lt;/code&gt; new data to some database ref and you will always see immediate &lt;code&gt;child_added&lt;/code&gt; events,&lt;/p&gt;
&lt;p&gt;So what is redux buying you? I think the biggest out-of-the-box benefit from redux is buying into the conceptual wins of using the &lt;a href="https://en.wikipedia.org/wiki/Actor_model"&gt;Actor Model&lt;/a&gt; which is basically enforcing rigorous value-boundaries and abstractions for your services. When you're all-in on the Redux Life you could stop using redux proper and still gain benefits from message-passing in your application.&lt;/p&gt;
&lt;p&gt;redux also allows you to abstract your backend service until it actually exists—by creating well-structured semantics for clientside data mutations, you can largely insulate yourself from your database unti it either exists or until you migrate to some other platform, service or data model. this is the most compelling reason to stick with redux in spite of the many cool benefits firebase offers.&lt;/p&gt;
&lt;h2&gt;composed depend-encies&lt;/h2&gt;
&lt;p&gt;At work, we use &lt;a href="https://www.npmjs.com/package/redial"&gt;redial&lt;/a&gt; to deal with our data dependencies. Redial has some other structure, but the idea is that you define some dependencies to execute either synchronously or asynchronously.&lt;/p&gt;
&lt;p&gt;Redial is really well suited to satisfying a one-time data dependency although you can trigger events arbitrarily with it or you can just use it to initiate a dependency lifecycle. For instance:&lt;/p&gt;
&lt;p class="codeHeading"&gt;using redial with redux-thunk&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;firebase&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;provideHooks&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;redial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./todo-actions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// a thunk&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getAndSubscribeToTodos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`todos/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_added&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_deleted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child_changed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;dbRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;provideHooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getAndSubscribeToTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;])(&lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the case of redial, the hidden benefit is that you can have easyish serverside rendering because you can block rendering on the &lt;code&gt;fetch&lt;/code&gt; hook. That is a legit necessity, but it's still somewhat complex given that firebase is meant to asynchronously populate and update your client with data from their datastore.&lt;/p&gt;
&lt;p&gt;But, i mean, whatever—this works too, and it would be reasonable (you could terminate a subscription with a &lt;code&gt;componentWillUnmount&lt;/code&gt; somewhere). But when i look at this and the prior approach using react-redux-firebase, i realize that both of them do it by &lt;em&gt;enhancing this component so that a data dependency is directly bound to it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And why not, right? This is &lt;a href="https://facebook.github.io/react/docs/higher-order-components.html#use-hocs-for-cross-cutting-concerns"&gt;exactly the approach&lt;/a&gt; the redux docs suggest for dealing with a subscription. As with redial, this sort of makes sense when you attach high-level dependencies &lt;em&gt;to a route&lt;/em&gt; in your application, but as applications grow larger, you more often want to colocate data dependencies with components that need them.&lt;/p&gt;
&lt;p&gt;this is basically the rationale behind graphql via apollo/relay, the way they manage data dependencies is by allowing arbitrary child nodes to define their dependencies and then combining the collected graphql queries into a single query that is accessible to components via a method very similar to &lt;code&gt;react-redux&lt;/code&gt;'s &lt;code&gt;connect&lt;/code&gt; HOC.&lt;/p&gt;
&lt;p&gt;but while that architecture is thoroughly awesome, it got me thinking: what other things asynchronously update your client datastore? How do we treat them and model their behavior?&lt;/p&gt;
&lt;h3&gt;Inputs&lt;/h3&gt;
&lt;p&gt;In a webform, we model form inputs as a set of disconnected inputs to our application loosely bound together by a &lt;code&gt;&amp;lt;form /&amp;gt;&lt;/code&gt; element or by a parent component's &lt;code&gt;state&lt;/code&gt;. In the case of a redux app, form data is usually managed by the application state because most inputs are &lt;a href="https://facebook.github.io/react/docs/forms.html#controlled-components"&gt;&lt;em&gt;controlled&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So what defines an input? Values come in, trigger &lt;code&gt;onChange&lt;/code&gt; events and you accept their data into your application's state (even if temporarily).&lt;/p&gt;
&lt;p&gt;So why not model a firebase subscription as an input?&lt;/p&gt;
&lt;p class="codeHeading"&gt;&amp;lt;FirebaseInput /&amp;gt;&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react-redux&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FirebaseInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./firebase-data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;changeTodo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./todo-actions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FirebaseInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sb"&gt;`/todos/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;onChildAdded&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemAdded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;onChildRemoved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemRemoved&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;onChildChanged&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemChanged&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;itemAdded&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;itemRemoved&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;itemChanged&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;
&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At first glance, this plays &lt;em&gt;much&lt;/em&gt; more nicely with standard redux: my store only knows about actions + values (not firebase) and I can implement this core clientside data modeling &lt;em&gt;before&lt;/em&gt; i add-in firebase by manually dispatching these actions locally.&lt;/p&gt;
&lt;p&gt;But also, in stock redux fashion, my component code's knowledge of firebase is limited to two spots.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;FirebaseInput /&amp;gt;&lt;/code&gt; knows about a path in the firebase store (which, there is sort of no avoiding this component knowing about firebase).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mapDispatchToProps&lt;/code&gt; knows about &lt;a href="https://firebase.google.com/docs/reference/node/firebase.database.DataSnapshot"&gt;&lt;code&gt;DataSnapshot&lt;/code&gt;&lt;/a&gt; and it's arguable whether or not this is a problem, it feels nicely isolated to me.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We could refactor this further using the standard redux container pattern, isolating the&lt;/p&gt;
&lt;p class="codeHeading"&gt;&amp;lt;TodoContainer /&amp;gt;&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react-redux&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FirebaseInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./firebase-data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;changeTodo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./todo-actions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoContainer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemAdded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;itemRemoved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;itemChanged&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FirebaseInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/todos/&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;onChildAdded&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemAdded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onChildRemoved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemRemoved&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;onChildChanged&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemChanged&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;itemAdded&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;itemRemoved&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;itemChanged&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;
&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;TodoContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p class="codeHeading"&gt;&amp;lt;TodoList /&amp;gt;&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;in both cases though, the &lt;code&gt;&amp;lt;TodoList /&amp;gt;&lt;/code&gt; component is receiving &lt;code&gt;connect&lt;/code&gt;ed data: that is, although the data flow appears to be coming from the &lt;code&gt;&amp;lt;FirebaseInput /&amp;gt;&lt;/code&gt; it is always being routed to the redux store in the same way that a form input's data is promoted globally.&lt;/p&gt;
&lt;h3&gt;abstracting the database ref&lt;/h3&gt;
&lt;p&gt;So far, we've ignored the firebase &lt;code&gt;dbref&lt;/code&gt; concept entirely, but it's the preferred handle to push changes remotely to firebase. So how do we expose the ref to components and handlers that need access to it in order to update the firebase database?&lt;/p&gt;
&lt;p&gt;React already provides an analog for this in its own &lt;a href="https://facebook.github.io/react/docs/refs-and-the-dom.html"&gt;callback ref&lt;/a&gt; implementation. Roughly, the idea is that you defne a local class variable and you attach a callback ref function to tho element you want to get a handle on. So let's adapt the idea&lt;/p&gt;
&lt;p class="codeHeading"&gt;using a firebase ref&lt;/p&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;class TodoContainer {
&lt;span class="gi"&gt;+  todoRef = null;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;  render() {
&lt;span class="w"&gt; &lt;/span&gt;    const {uid, todos} = this.props;
&lt;span class="w"&gt; &lt;/span&gt;    const {itemAdded, itemRemoved, itemChanged} = this.props;

&lt;span class="w"&gt; &lt;/span&gt;    return &amp;lt;div&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;      &amp;lt;FirebaseInput path={/todos/${uid}}
&lt;span class="w"&gt; &lt;/span&gt;        onChildAdded={itemAdded} onChildRemoved={itemRemoved}
&lt;span class="w"&gt; &lt;/span&gt;        onChildChanged={itemChanged}
&lt;span class="gi"&gt;+        dbRef={ref =&amp;gt; this.todoRef = ref}&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;      /&amp;gt;

&lt;span class="gd"&gt;-      &amp;lt;TodoList todos={todos} /&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+      &amp;lt;TodoList todos={todos} handleDone={this.toggleDone} /&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;lt;/div&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;  };

&lt;span class="gi"&gt;+  toggleDone = id =&amp;gt; {&lt;/span&gt;
&lt;span class="gi"&gt;+    if (this.todoRef) {&lt;/span&gt;
&lt;span class="gi"&gt;+      const status = this.todoRef.child(`${id}/done`).val();&lt;/span&gt;
&lt;span class="gi"&gt;+      this.todoRef.child(`${id}/done`).set(!status);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+  }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In standard react &lt;a href="http://redux.js.org/docs/basics/UsageWithReact.html#presentational-and-container-components"&gt;container/presentation&lt;/a&gt; fashion, this passes handlers through props that use standard firebase semantics but isolated from the actual implementation of firebase by letting the container manage the ref but hide it from the child component.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;handleDone&lt;/code&gt; can start as a method that initially dispatches a redux action but can later be upgraded to be a function that directly updates firebase. Because the data &lt;code&gt;&amp;lt;TodoList /&amp;gt;&lt;/code&gt; uses still comes from the same location in redux, the cost of migrating data from locally-stored to firebase-managed is pretty minimal.&lt;/p&gt;
&lt;h2&gt;drawbacks&lt;/h2&gt;
&lt;p&gt;I think a better mental-model for modeling complex firebase applications in a client application ends up being closer to relay or apollo. And while it's not terribly difficult to imagine firebase adding a graphql adapter to their datastore in much the same way that graphcool or others do, for simple applications this adds a ton of both conceptual and infrastructure-related overhead that you probably don't really need or want to to deal with when getting started with plain out-of-the-box firebase.&lt;/p&gt;
&lt;p&gt;Another drawback to this approach is that you need to be aware of duplicated dependencies. Without a mechanism to aggregate firebase subscriptions, it's easy to create multiple FirebaseData inputs in your application that duplicate work or perhaps even clobber each other's data in competing callbacks.&lt;/p&gt;
&lt;p&gt;Still, for small applications, modeling asynchronous network data as a plain react component like an &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; adds a low-cost approach to inject external network dependencies along with a simple migration path from locally-managed data to externally managed data.&lt;/p&gt;
</content></entry><entry><title>unhalf-bricking</title><link href="https://generic.cx/essays/unhalfbricking/" rel="alternate"></link><updated>2006-10-25T00:00:00Z</updated><author><name>marcos</name></author><id>urn:uuid:d82f3575-d48e-3f23-bb40-ae37b9537366</id><content type="html">&lt;p&gt;this post by ben northrop &lt;a href="http://www.bennorthrop.com/Essays/2016/reflections-of-an-old-programmer.php"&gt;Reflections of an "old" programmer&lt;/a&gt; struck me a bit, mostly because it covers two topics: trying to understand the role of a senior level engineer and a meditation on the cycle of churn in tech.&lt;/p&gt;
&lt;p&gt;i can't speak to the first part, even though i've been around a while, i don't know that i can speak to his experience or situation, but i can speak to the feeling of "&lt;a href="https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4#.7l9au0iel"&gt;fatigue&lt;/a&gt;" and "&lt;a href="http://www.expatsoftware.com/Articles/happiness-is-a-boring-stack.html"&gt;boredom&lt;/a&gt;".&lt;/p&gt;
&lt;p&gt;partly i feel somewhat confident in talking about my experience because&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;i first stared into the void when i discovered the seemingly endless rat race of &lt;a href="https://en.wikipedia.org/wiki/Microsoft_Certified_Professional"&gt;MCSE&lt;/a&gt; certification. but also&lt;/li&gt;
&lt;li&gt;i seem to have made peace with tech churn's place in my life (to some degree).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;there are two things i can point to that changed my perspective on churn. the first was my math+cs undergrad degree, the second has been my work on the web, on and off, for the past 15 years or so.&lt;/p&gt;
&lt;h2&gt;the degree&lt;/h2&gt;
&lt;p&gt;there's this line in cs education that most people encounter at some point&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;computer science is neither about computers nor science.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The statement sounded pithy in the first few minutes of &lt;a href="https://dspace.mit.edu/handle/1721.1/35852"&gt;6.001&lt;/a&gt;, but 2 years later, knee-deep in an &lt;a href="https://dspace.mit.edu/handle/1721.1/68649"&gt;automata class&lt;/a&gt; learning about computation, state machines, dumbed down proofs, oracles, and decidability the line resounded in my head.&lt;/p&gt;
&lt;p&gt;The other thing which probably hurt me was my school's nearly allergic reaction to talking about and dealing in actual implementations&lt;sup class="footnote-ref" id="fnref-impsplain"&gt;&lt;a href="#fn-impsplain"&gt;1&lt;/a&gt;&lt;/sup&gt;. I'm told this has changed, but at the time i think it was perhaps a bit too principled of a stance on &lt;a href="https://www.jwz.org/doc/worse-is-better.html"&gt;doing the right thing&lt;/a&gt;. So I, at least, spent a lot of time thinking about good solutions &amp;amp; approaches and less time implementing naive ones that would have worked just fine probably.&lt;/p&gt;
&lt;p&gt;This fledgling caution was partly a reaction to seeing the results of bad (sometimes disastrous) theoretical implementations in classes and then reading case studies about even worse practical implementations: you develop a simple of pattern matcher for your hubris and rabbit holes you've seen other projects fall into. You decide not to try your hand at implementing &lt;code&gt;halt(f)&lt;/code&gt; anytime soon.&lt;/p&gt;
&lt;p&gt;When i tell people &lt;em&gt;why&lt;/em&gt; a CS education is valuable, i usually talk about this pattern matching: you develop a good spider sense for counterintuitively bad edge cases, you become highly sensitive to polynomially disastrous performance from ten thousand feet, and you have a good understanding of &lt;em&gt;why&lt;/em&gt; architectures were designed the way they were simply by looking at their exposed APIs. You can absolutely learn to spot these things, to understand this behavior outside of school, but it can be easier when pattern matching is your full time job.&lt;/p&gt;
&lt;h2&gt;ideas&lt;/h2&gt;
&lt;p&gt;when i applied to design school, i cribbed a line i will paraphrase from ellen lupton:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;design is a tool for communicating complex ideas&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;there was this thing going around twitter this summer where you'd list the first six programming languages you learned as a kind of guilt-free rorschach test. Here was my list&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic 1989&lt;/li&gt;
&lt;li&gt;C 1996&lt;/li&gt;
&lt;li&gt;RPL 1997&lt;/li&gt;
&lt;li&gt;HTML 1998&lt;/li&gt;
&lt;li&gt;CSS 1999&lt;/li&gt;
&lt;li&gt;PHP 1999&lt;/li&gt;
&lt;li&gt;Java 2000&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/LPC_(programming_language)"&gt;LPC&lt;/a&gt; 2000&lt;/li&gt;
&lt;li&gt;SQL 2000&lt;/li&gt;
&lt;li&gt;Scheme 2001&lt;/li&gt;
&lt;li&gt;C 2001&lt;/li&gt;
&lt;li&gt;Common Lisp 2003&lt;/li&gt;
&lt;li&gt;Python 2003&lt;/li&gt;
&lt;li&gt;ELisp 2005&lt;/li&gt;
&lt;li&gt;JavaScript 2005&lt;/li&gt;
&lt;li&gt;Ruby 2005&lt;/li&gt;
&lt;li&gt;CSS 2005&lt;/li&gt;
&lt;li&gt;XSLT 2006&lt;/li&gt;
&lt;li&gt;Erlang 2006&lt;/li&gt;
&lt;li&gt;Actionscript 2008&lt;/li&gt;
&lt;li&gt;Objective C 2009&lt;/li&gt;
&lt;li&gt;R 2010&lt;/li&gt;
&lt;li&gt;JavaScript 2010&lt;/li&gt;
&lt;li&gt;Python 2011&lt;/li&gt;
&lt;li&gt;CSS 2011&lt;/li&gt;
&lt;li&gt;Swift 2013&lt;/li&gt;
&lt;li&gt;ES2015 + Flow&lt;sup class="footnote-ref" id="fnref-aboutflow"&gt;&lt;a href="#fn-aboutflow"&gt;2&lt;/a&gt;&lt;/sup&gt; 2015&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The thing i think most about when i look at this list is that each of those years, i was applying or re-learning a language but attempting to solve a new flavor of problem with it (or refining some approach in a problem-domain).&lt;/p&gt;
&lt;p&gt;if you take a computability class like the one i mentioned earlier, you are taught that the &lt;a href="https://en.wikipedia.org/wiki/Turing_machine#Universal_Turing_machines"&gt;kind of language&lt;/a&gt; you need to express all possible computable ideas is pretty straightforward, even if cumbersome. My takeaway was that &lt;em&gt;any syntax&lt;/em&gt; beyond that basic turing-machine-language is an ideologically-aligned mechanism for expressing ideas about some problem domain.&lt;/p&gt;
&lt;p&gt;People talk about Domain Specific Languages (DSL)s all the time (often dismissively!), but all general purpose languages are domain-specific. Nobody writes C because they are in love with memory management, you write it because it's easy to compile for most general purpose CPUs. Or you write it because you're tired of assembly. You don't write it because you feel like &lt;code&gt;malloc&lt;/code&gt;ing your way to health. And even if you do, you're probably looking at Rust sitting pretty in the corner of your hard drive over there. All languages, from Postscript (printing) to Lisp (list processing) to Java (platform independent vm) are built around solving &lt;em&gt;some&lt;/em&gt; problem.&lt;/p&gt;
&lt;p&gt;Sure, the purists say, you &lt;em&gt;could&lt;/em&gt; get away with &lt;em&gt;just lisp&lt;/em&gt; but there are tons of languages out there, why aren't they all lisps? The answer is simple: lisp is a very nice and expressive round hole, but some problems are quite literally square pegs that have their own DSLs built around them. Sure, recreate its api lisp (or transpile!), but why not use that dsl directly? It was made &lt;em&gt;for a reason&lt;/em&gt; and that reason is (almost always) to be expressive and relevant to some problem domain (or, more interestingly, to address the limitations of bygone era's operating system, or hardware limitations, or cultural norms). I'm not saying you should program your next startup in COBOL, write cgi-bins in C, or use the Apache webserver, but have you ever wondered why whole generations of programmers did?&lt;/p&gt;
&lt;h2&gt;the churn&lt;/h2&gt;
&lt;p&gt;two parts of ben northrop's post jumped out at me&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If I learned nothing else from this point forward, I bet that only about a half of my knowledge could I still use in 2026 (long live SQL!), and the other half would probably be of no use (React Native, perhaps?). Now of course I will be gaining new knowledge to replace the dead stuff, but will it be enough? Will I know more (useful) knowledge in 2026 than I do now?&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;Some of what we learned early in our career is now out-dated. All that time "we" (read: I) spent learning GWT? Lost! Essentially, both forces, knowledge decay and knowledge accumulation rate, begin to work against us.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think most professional programmers have grappled with these two aspects so i want to address them&lt;/p&gt;
&lt;h3&gt;what will i retain?&lt;/h3&gt;
&lt;p&gt;i didn't put jquery on that list, but i think lots of people invested a significant amount of their brain real estate on being productive with jquery. But now "everyone" uses react or angular or vue or &lt;a href="http://youmightnotneedjquery.com/"&gt;at least not jquery&lt;/a&gt; and what of all the mental energy you invested solving the web's problems with jquery? is that (like GWT/prototype/mootools/closure/et al) lost?&lt;/p&gt;
&lt;p&gt;why is jquery great? because it papers over browser inconsistencies. at some point, though, we mistook it for a dom interface and its own tech stack. that's fine, but during those heady days we learned to harness the web, we finally had robust tools for making xhr calls, we began to harness animation, we learned what ui we actually needed to build to have productive web pages.&lt;/p&gt;
&lt;p&gt;At some point, we realized that lots of those things had become standardized across many browsers we actually cared about and for some reason it was as if we needed to cut jquery out of our lives like trans fats or sugar or ecigs. that reaction is odd because it ignores the fact that jquery still has a purpose (albeit a very &lt;em&gt;different&lt;/em&gt; one now that we've made a habit of casting aside older browsers)!&lt;/p&gt;
&lt;p&gt;still, regardless your feelings about jquery now, you have to admit that during that time, we learned &lt;em&gt;how to make web applications&lt;/em&gt; where before we were just struggling to make something jiggle consistently at 13fps during &lt;code&gt;hover&lt;/code&gt; events. The lessons learned about toggling ui element styles in response to some user action may be obviated by a more efficient dsl or library (or ux pattern), but it doesn't change that our relationship to solving that problem is now a question of "&lt;em&gt;how do i do this thing nowadays&lt;/em&gt;" vs "&lt;em&gt;is it even possible to do this thing?&lt;/em&gt;"&lt;/p&gt;
&lt;p&gt;i go back to my cs education being a big pattern matching thing: is the fundamental takeaway from a tech experience "wow, i got really good and efficient at using that api" or "i have developed techniques to solve a series of problems." it's fair to have both takeaways, but not at the expense of ignoring the more fundamental "hot damn, i know web kung fu."&lt;/p&gt;
&lt;h3&gt;all that time lost&lt;/h3&gt;
&lt;p&gt;i think people underestimate that lots of programming 'churn' is a reaction to the fact that lots of problems we have now, we &lt;em&gt;didn't&lt;/em&gt; have ten years ago. Is the point of React Native to develope iphone apps or is it, like jquery, a tool for papering over the fact that it's a &lt;em&gt;tremendous pain in the ass&lt;/em&gt; to develop for two mostly different deployment targets at the same time.&lt;/p&gt;
&lt;p&gt;Hey, looking at all you folks in the 90s that used &lt;a href="https://en.wikipedia.org/wiki/Swing_(Java)"&gt;Swing&lt;/a&gt;/&lt;a href="https://en.wikipedia.org/wiki/Abstract_Window_Toolkit"&gt;AWT&lt;/a&gt;/&lt;a href="https://en.wikipedia.org/wiki/Qt_(software)"&gt;QT&lt;/a&gt;, how did that experience turn out? When &lt;em&gt;i&lt;/em&gt; looked at react native, it was tempting to shrug it off for all the same reasons: boring ui, terrible ux, hilarious java-flavored api, etc.&lt;/p&gt;
&lt;p&gt;but i also think about all the lessons i learned from exposure to those systems. maybe those lessons make me a cautions RN dev, or maybe they just give me a different perspective. Who knows. I don't think that's a negative, and certainly, i am able to use that perspective to evaluate if RN is the API i would choose to set sail with for my next cross-platform journey.&lt;/p&gt;
&lt;p&gt;I find it easy to accidentally conflate "time i spent learning arcane details of &lt;code&gt;this&lt;/code&gt; in javascript" with "something important i learned about solving a class of problems." Sometimes they're helpfully related, but other times one is just useless trivia or a detail that is optimized away by a transpiler or minifier (who both know my cargo culting is redundant) or caught by a linter (which is better at remembering thousands of stupid rules than i am) or hopefully code review or eventually in a year or so, the vm.&lt;/p&gt;
&lt;p&gt;I try to be hawkish about differentiating trivia from conceptual knowledge because above all i remain haunted by the career path of somebody who needed to take those stupid mcse certifications every year. We're lucky that nobody is asking us to take yearly exams about the inner workings of VMs or apis to have a bullshit stamp we can show off to people. It's easy to conflate Keeping Current And Up To Date with having one of those stamps, but that's trivia, our job remains solving problems.&lt;/p&gt;
&lt;h3&gt;not having fun anymore&lt;/h3&gt;
&lt;p&gt;The counterargument to all this is every situation we've been &lt;em&gt;forced&lt;/em&gt; or "encouraged" to learn some tech we morally disagree with. It happens all the time, even when it's not foisted on us from up high. Want to learn to use a traditional (oracle/postgres/mysql) database, it's time to make peace with SQL (if you dare). Turn up your nose all you want at javascript, but it's &lt;a href="https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript"&gt;the only game in town&lt;/a&gt; for writing clientside webapps (unless you transpile from coffee, elm et al.). or even until recently, if you wanted to write a performant app for ios, you needed have The Talk about manual memory management.&lt;/p&gt;
&lt;p&gt;I think it would be unfair of me to discount the fact that sometimes we're put in positions where we spend an outsized amount of time solving problems that aren't actually the ones we were hired to deal with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want to write a webapp, but instead you need to know a smattering of linux system administration&lt;/li&gt;
&lt;li&gt;You just want an accordion menu, but you need to debug a memory leak in some library you imported&lt;/li&gt;
&lt;li&gt;You are trying to do string formatting, but a dependency broke semver out from under you, now you manage a private npm repository and are a wizard at pinning and shrinkwrapping your app.&lt;/li&gt;
&lt;li&gt;You invest 40+ hours integrating a complex and poorly crafted library in your codebase, learning its mental model only to realize it's not suitable for production use or that making it usable would require the same amount of time as having written it from the outset.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the sorts of "orthogonal concerns" that i hear when people talk about "wasted time" or "experience that doesn't help my career" except that a good percentage of the time that experience &lt;em&gt;is your career.&lt;sup class="footnote-ref" id="fnref-fossplain"&gt;&lt;a href="#fn-fossplain"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt; And all those experiences, I believe, are valuable data-points that can (and should) trigger your spider-sense in the future when you're stuck with a broken dependency or need to assess how much it will delay a feature.&lt;/p&gt;
&lt;p&gt;It's fair to assert that those are the kinds of pedestrian concerns you don't want to deal with in your career. Like i said, this profession is all about solving problems and eventually you either delegate them away, solve them yourself, or delevop some coping strategy for them sharing space with your day to day life. It's fine to feel grumbly about good tech that got the raw end of some deal. Lots of people write their JS as if it were lisp and a ton write it as if it were C++. Everyone takes their experiences with them on new journeys.&lt;/p&gt;
&lt;h2&gt;who knows where the time goes?&lt;sup class="footnote-ref" id="fnref-titlesplain"&gt;&lt;a href="#fn-titlesplain"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/h2&gt;
&lt;p&gt;As i've gotten older, i think about how even something as "trivial" as a Javascript Promise solves problems &lt;em&gt;i&lt;/em&gt; didn't have when i was learning my first web application with PHP. Wow! Such power! New language features have the capacity to expand our &lt;em&gt;conceptual&lt;/em&gt; vocabulary, so in the future we can talk productively about and address problems we didn't have before.&lt;/p&gt;
&lt;p&gt;It's fine to love a boring stack, especially if the problems you're solving aren't changing much or if those boring parts of your stack are solving a commodity need.&lt;/p&gt;
&lt;p&gt;In general, though, I reject the idea that adopting a new patois is pointless when thinking and learning about new or novel problems. Or that the problem-space has remained static since the 60s (which is reductive). Solving problems is what we do all day long, so it's confounding to hear a drumbeat of folks dismissing out of hand that today's networks and norms around ui have &lt;em&gt;completely changed&lt;/em&gt; the way that we approach problems like latency, optimistic updates, partition tolerance, reconciliation, and so forth. In the past, you could make a good case that the audience for these sorts of 'niche' problems was in the tens of people, now they are part and parcel of something as boring as a shared TodoMVC app.&lt;/p&gt;
&lt;p&gt;&lt;img src="931446.gif" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Even if your work never directly interacts with that lower layer, your UI should be aware of its effects. Is that useless knowledge? People used to talk about paradigm shifts. Thinking about a widget's states from being &lt;code&gt;[on, off]&lt;/code&gt; to &lt;code&gt;[mounting, no-data, off, loading, on, unmounting]&lt;/code&gt; is a legit paradigm shift. Even if the widget doesn't reflect all those states, how do you go back to &lt;code&gt;[on,off]&lt;/code&gt; when &lt;em&gt;you know&lt;/em&gt; that a component's behavior is dependent on so many more things. We've gone from a world where we could expect computers or buttons to either work or not to a world where our digital data feels more like high-resolution analog input.&lt;/p&gt;
&lt;p&gt;This is partly why I remain optimistic about even transitional blips in our knowledge, flashes in the pan whose utility is replaced by standards-track tech. At its best, it gives us new ways to approach old problems and new ways to talk about things we previously ignored. How is that not a net-positive?&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;&lt;li id="fn-impsplain"&gt;&lt;p&gt;at least at my level of education, the only practical place to actually Make Things Go were either on your own time, at MITERS or in your dorm or a club or by taking on undergraduate research jobs. But even then, you would encounter the comic upturned nose to a hack and the accompanying semi-sarcastic rejoinder "what do you mean, no closed form solution!?"&lt;a href="#fnref-impsplain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-aboutflow"&gt;&lt;p&gt;These days, i find something like &lt;a href="http://flowtype.org"&gt;Flow&lt;/a&gt; fascinating because it's such a change in mindset from the classic approach of a strictly typed languages: instead of rocking the type-hairshirt from the outset of your next multi-million dollar web-scale paas, you can &lt;em&gt;decide&lt;/em&gt; to strategically invest in typing areas of a codebase that are type-sensitive while ignoring the ones that aren't. That's not a "new idea" by a mile, but the approach is refreshingly modern for something which has had historically high startup costs in traditional languages.&lt;a href="#fnref-aboutflow" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-fossplain"&gt;&lt;p&gt;i made a &lt;a href="https://twitter.com/nsfmc/status/795352749311291392"&gt;snide comment&lt;/a&gt; about this on twitter because i think most people are pretty blasé about this point: if you take on a dependency, you should be prepared to own it in some capacity (especially if this software is critical to your livelihood). the person i owe this mindset to is, unsurprisingly, craig silverstein, who got me into the habit of forking each dependency and assuming responsibility for merging (and dealing with conflicting) upstream changes.&lt;/p&gt;
&lt;p&gt;i mention this because it's vogue to complain about some package that a developer has stopped maintaining and pin the worries (as in mailing list post from the twitter thread) on concerns like "we aren't able to commit to the main repo" or "the package has stopped receiving active maintenance." Two points here: the first problem is a non-problem especially if the community is larger than just the missing developer. Anybody looking for the package will run across the new fork and see its active development history or discover a mailing list post (or stack overflow mention) of the newer package. The second is probably the bigger issue: if there are active patches dowstream &lt;em&gt;just waiting&lt;/em&gt; for a maintainer, a hostile fork is already at an advantage over the original project. But if the project is beloved by the community but is receiving no active contributions, what exactly is the problem? Further, if the project has met its stated goals, what responsibility does it have to merge any patches? As anyone that has contributed to a project knows: unsolicited work that doesn't solve an existing pain point often goes unmerged. But also, if nobody's contributing work, who's there to complain about a dying project?&lt;/p&gt;
&lt;p&gt;Many people i think lump free/open source software into this bucket of "things i'm glad i didn't write and am glad to take advantage of" without acknowledging just how integral they are to the basic function of their product. It's fine to resent a lib or app for not being a fully fleshed out product but it's irresponsible to ignore the fact that without them, you'd be on the hook for writing that same code yourself. This the argument for &lt;a href="https://www.fordfoundation.org/library/reports-and-studies/roads-and-bridges-the-unseen-labor-behind-our-digital-infrastructure/"&gt;open source as critical infrastructure&lt;/a&gt;. When the access road needs maintenance, do you pay to have it repaved in the interim, or close up shop?&lt;a href="#fnref-fossplain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-titlesplain"&gt;&lt;p&gt;the title of this blog post was originally this heading, but it felt too big and too weighty. instead, i replaced it with unhalfbricking, which is the name of a Fairport Convention album (they also have a song called Who Knows Where The Time Goes). It's good! I would reccomend it heartily!&lt;/p&gt;
&lt;iframe style="width: 100%;" src="https://embed.spotify.com/?uri=spotify:track:2ZjRAKoGcm4oyyl6iikOuC" width="100%" height="80" frameborder="0" allowtransparency="true"&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a href="#fnref-titlesplain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>the death of paper prototypes</title><link href="https://generic.cx/essays/paper/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:5d159ba1-6c53-31c9-97a1-346bbd962f6d</id><content type="html">&lt;p&gt;some have said that &lt;a href="http://www.gv.com/lib/paper-prototyping-is-a-waste-of-time"&gt;paper prototypes are a waste of time&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;what i have observed is that paper prototyping is extremely useful when you haven't decided how you want things to work. that is, if you are having the discussion "wouldn't it be great if we could 'fling' this doo-dad in order to dismiss it?" and then a minute later you have a piece of paper that you can literally fling across another piece of paper or perhaps, an actual ipad.&lt;/p&gt;
&lt;p&gt;in about a minute, you can do "wacky" things like prototype something akin to "what if you crumpled the item with a pinch gesture when you were done with it?" or "what if you swiped successive items, like sheets of paper, you know, like from that production company back in the 80s at the end of all the tv shows." and then you do it and you say something like "oh, yeah, ok let's not do that"&lt;/p&gt;
&lt;p&gt;and then later that afternoon, once you've settled the "dispute" then you can spend the next few hours prototyping the hell out of whatever it is you thought was a good idea. that's when you bust out the quartz composer/html/js/react/cloud/mp3 ripping/mesh network/interface builder/drone snack delivery/framer/etc.&lt;/p&gt;
&lt;p&gt;and if that feels right, then you can go ahead and implement that in your web app or mobile app or whatever given the constraints of your api/scheduling/nosqlbane/datastore/frameworks/etc.&lt;/p&gt;
&lt;p&gt;the danger, the thing you're fighting, is that you spend, up-front, a day or two or ten realizing what could legitimately have been discovered by a piece of paper sitting atop your ipad. it has taken me a comical amount of programming to realize when a paper prototype is useful, but i no longer scoff at it "because i can program." Even the decision to mock something out in keynote or qc or whatever is fraught with the decision that you've committed to an aesthetic, to some animation settings, to your app chrome, etc. sometimes those things don't exist and you're trying to debate things way more elemental. in those cases, paper prototyping can save you lots of time.&lt;/p&gt;
&lt;p&gt;but again, if you've already decided something is a good idea, then in-app or live prototypes are incredibly valuable for being able to 'tweak the knobs' and 'live with your mistakes' while you find easing curves or animation timings or whatever. if you're flying blind, then you could do a whole lot worse than paper-larping for a few minutes while you decide what your mobile strategy is.&lt;/p&gt;
&lt;p&gt;which is to say, if you're at the stage where you're going "head to head with your competitors" then you probably should be doing real prototypes or building real mvcs in whatever system works best. this is effectively the same as what they're saying in the original article: you can't only paper prototype and expect it all to be peaches and cream.&lt;/p&gt;
&lt;p&gt;even as i'm writing this i'm getting a huge fred brooks flashback here because the reality is that making good ui is actually quite time consuming even if you have fancy whizbang frameworks. this is why people love framer or qc—they simplify thinking about interaction to the most basic aesthetic choices as relates to some states: this thing fades out while this thing flies in. or these things pop in at staggered delays when you go to the next part of the tutorial.&lt;/p&gt;
&lt;p&gt;i'm not trying to knock those tools, but the truly hard ui problems are rarely solved by a single motion sketch in any app. and honestly, even those keynote/qc/framer 'high fidelity mocks' are, to my mind, as disingenuous as polished paper prototypes because most of the time their animations are wildly disconnected from the reality of using the app day to day when you're trying to accomplish anything.&lt;/p&gt;
&lt;p&gt;the reality mirrors something closer to the epic 500+ email process thread from the creators of Threes, a thread so epic that it doesn't deserve to be occluded within an html link, i'll just put it here&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    http://asherv.com/threes/threemails/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i guess really what i'm saying is that all these things take time and the 'real world' process is long, but that if you're lucky, you get better at spotting which things your can gut check with paper and which things you need to bite the bullet to just see at least once. and then you just iterate until things get better, i guess that's the atwood strategy.&lt;/p&gt;
&lt;p&gt;there's a sort of stupid bravado here in the valley where people like to eschew 'trying something dumb' for 'making an mvp' and sometimes it's a good forcing function to get out of a funk but often i see it results in lots of wasted time and pointless code that goes nowhere. maybe i'm getting old or something, though.&lt;/p&gt;
&lt;p&gt;obligatory line about it taking a tough man to make a tender chicken&lt;/p&gt;
</content></entry><entry><title>fun with vector fields</title><link href="https://generic.cx/essays/slopefields/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:0e021478-dab0-30c0-b252-72d09faff363</id><content type="html">&lt;p&gt;A while ago, we needed to send out a notification to all our &lt;a href="https://www.khanacademy.org/learnstorm"&gt;LearnStorm&lt;/a&gt; winners inviting them to the Finals event in Mountain View. It's a big deal! Only &lt;em&gt;200 Students&lt;/em&gt; from all of the bay area got invited to the event.&lt;/p&gt;
&lt;p&gt;Because most (if not all) of them were under 13, we couldn't email&lt;sup class="footnote-ref" id="fnref-coppa"&gt;&lt;a href="#fn-coppa"&gt;1&lt;/a&gt;&lt;/sup&gt; them so we needed to make something that would catch their eye the next time they visited the website.&lt;/p&gt;
&lt;p&gt;And, as with the most fun projects, there was very little time to actually work on this: from start to finish, this was bounded to around three days, so in terms of design, it would need to be nice, but it couldn't be 100% totally new shiny design work: compromises would need to be made.&lt;/p&gt;
&lt;h2&gt;step one: notifications&lt;/h2&gt;
&lt;p&gt;You have probably gotten a notification on Khan Academy before, and readily dismissed it like so much purple rain.&lt;sup class="footnote-ref" id="fnref-purplerain"&gt;&lt;a href="#fn-purplerain"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/Mjew.png" alt="a default ka notification in portuguese"&gt;&lt;/p&gt;
&lt;p&gt;And why not, it's certainly... &lt;em&gt;distinctive&lt;/em&gt;, but that purple rectangle is not exactly injecting anybody's retinas with "notification delight," so step one was figuring out how to make the notifications look nicer.&lt;/p&gt;
&lt;h2&gt;step two: learnstorm?&lt;/h2&gt;
&lt;p&gt;The next step of research was teasing apart learnstorm's visual style: the &lt;a href="https://www.khanacademy.org/math/differential-equations/first-order-differential-equations/differential-equations-intro/v/slope-field-to-visualize-solutions"&gt;slope field&lt;/a&gt; motif is the heart of learnstorm's identity.&lt;sup class="footnote-ref" id="fnref-sloperefresh"&gt;&lt;a href="#fn-sloperefresh"&gt;3&lt;/a&gt;&lt;/sup&gt; In making this notification banner I knew that I wanted echo the identity, but from another angle or perspective. To do that I would need to grok that slope field a little better because already at this point, i knew i wanted to incorporate it into the banner in some way.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/JGuX.png" alt="the learnstorm page"&gt;&lt;/p&gt;
&lt;p&gt;But wait a second... math??? we can't even &lt;a href="http://howtocenterincss.com"&gt;center a div&lt;/a&gt;, how can we possibly do anything with slope fields?&lt;/p&gt;
&lt;p&gt;Actually, let's step back even further... differential equations? what's a slope field again?&lt;/p&gt;
&lt;h3&gt;step yak: a math detour&lt;/h3&gt;
&lt;p&gt;My thinking went: our contest is rooted in differential equations, it'll probably help to revisit them. &lt;sup class="footnote-ref" id="fnref-1803revisited"&gt;&lt;a href="#fn-1803revisited"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;And after a brief while you find that a slope field is basically what it claims to be: the slope of a differential equation calculated at periodic points on a plane. And differential equations only express &lt;em&gt;the feel&lt;/em&gt; of an equation, not exactly what it looks like because they're missing several constant values.&lt;/p&gt;
&lt;p&gt;So for a plain ol' circle &lt;em&gt;y^2 + x^2 = 1&lt;/em&gt;, you'd get &lt;em&gt;y' = -x/y + c&lt;/em&gt; (which you could find via &lt;a href="https://www.khanacademy.org/math/differential-calculus/taking-derivatives/implicit_differentiation/v/implicit-differentiation-1"&gt;implicit differentiation&lt;/a&gt;), but again' all that's saying is that the slope for this differential equation at &lt;em&gt;any point&lt;/em&gt; is the &lt;em&gt;x&lt;/em&gt; value divided by the &lt;em&gt;y&lt;/em&gt; value times negative one plus some arbitrary &lt;em&gt;c&lt;/em&gt; (which, for us, we'll just pretend is zero). For whatever point, you just enter x and y into that equation and &lt;em&gt;that's the slope.&lt;/em&gt;&lt;sup class="footnote-ref" id="fnref-slopedetour"&gt;&lt;a href="#fn-slopedetour"&gt;5&lt;/a&gt;&lt;/sup&gt; neat!&lt;/p&gt;
&lt;p&gt;But when we attempt to draw this (say on paper or digitally), we don't actually want &lt;em&gt;y'&lt;/em&gt;, we want whatever &lt;strong&gt;direction&lt;/strong&gt; &lt;em&gt;y'&lt;/em&gt; corresponds to so we can point a triangle in that direction. With a simple equation like &lt;em&gt;y' = -x / y&lt;/em&gt; , we run into this problem where for zeroey &lt;em&gt;y&lt;/em&gt; values we have infinitely positive or negative &lt;em&gt;y'&lt;/em&gt; and what do you do with that?&lt;/p&gt;
&lt;p&gt;Well, &lt;code&gt;Math&lt;/code&gt; to the rescue! Specifically the standard &lt;a href="https://en.wikipedia.org/wiki/Atan2"&gt;atan2&lt;/a&gt; method which returns the angle in radians (from the origin) for the point at &lt;em&gt;(x,y)&lt;/em&gt;. It's a special function which calculates the arctangent &lt;em&gt;but&lt;/em&gt; is smart enough to compensate for zero denominators in pretty much the same way you would.&lt;sup class="footnote-ref" id="fnref-divzero"&gt;&lt;a href="#fn-divzero"&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Looking at the &lt;code&gt;atan2&lt;/code&gt; implementation, you find that it does the kind of "intuitive limit-ey division" that you're often asked to do in the first weeks of a calc class. Instead of &lt;code&gt;NaN&lt;/code&gt; you get ±π/2 and instead of 0 you get 0 or π. It's great! And it's &lt;em&gt;exactly&lt;/em&gt; what we need. For a given &lt;em&gt;(x,y)&lt;/em&gt; pair, you get a corresponding radian value.&lt;/p&gt;
&lt;p&gt;Also, recall, we have a diff eq that is given by &lt;em&gt;y' = -x/y&lt;/em&gt;, we can use &lt;code&gt;atan2&lt;/code&gt; to find the slope of each point on the plane.&lt;sup class="footnote-ref" id="fnref-atan2deets"&gt;&lt;a href="#fn-atan2deets"&gt;7&lt;/a&gt;&lt;/sup&gt; Ok, we know how we might use this, now we just need to get started actually drawing it.&lt;/p&gt;
&lt;h2&gt;step two: prototyping&lt;/h2&gt;
&lt;p&gt;The first step of prototyping this was a small python script that i wrote for &lt;a href="http://plotdevice.io/"&gt;PlotDevice&lt;/a&gt;&lt;sup class="footnote-ref" id="fnref-plod"&gt;&lt;a href="#fn-plod"&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Again, remember that i didn't have too much time to work on this project, I needed to be certain that I could fake out a new spin on learnstorm identity so if I began to run out of time, I could, at the very least, render a cute static image as the background for the banner.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/HB9P.png" alt="a first sketch with nodebox"&gt;&lt;/p&gt;
&lt;p&gt;Well, this looks promising! But the day is young: could i... animate this?&lt;/p&gt;
&lt;p&gt;Stepping back, let's do a small crit&lt;sup class="footnote-ref" id="fnref-crytime"&gt;&lt;a href="#fn-crytime"&gt;9&lt;/a&gt;&lt;/sup&gt; here: part of what makes the learnstorm identity cool is that you get this round vibe &lt;em&gt;even though&lt;/em&gt; everything is stuck on a grid. Breaking the grid in this case would not only require me animating each dart but animating them along circular paths which, i mean, would look &lt;em&gt;awesome&lt;/em&gt; but... umm... maybe let's save that for LearnStorm 2016 when we have more time...&lt;/p&gt;
&lt;p&gt;Also, let's take this moment to visibly and vocally &lt;em&gt;Pivot&lt;/em&gt;.&lt;sup class="footnote-ref" id="fnref-sandhillfootnote"&gt;&lt;a href="#fn-sandhillfootnote"&gt;10&lt;/a&gt;&lt;/sup&gt; Because i spent the morning trying to figure out how to procedurally draw slope fields, i &lt;em&gt;already&lt;/em&gt; have (a sort of) infrastructure for generating slope fields, but the problem is that i want something i can easily animate. Time for a new differential equation!&lt;/p&gt;
&lt;p&gt;Maybe we can just make something up, something... real easyish, like...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;y' = x + y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and if you plot this out correctly, you end up seeing something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/sdKS.png" alt="swooooop!"&gt;&lt;/p&gt;
&lt;p&gt;but say you make a mistake&lt;sup class="footnote-ref" id="fnref-herpderp"&gt;&lt;a href="#fn-herpderp"&gt;11&lt;/a&gt;&lt;/sup&gt; and remove that super "interesting" &lt;code&gt;atan2&lt;/code&gt; we just learned about, you get something... &lt;em&gt;novel.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/4TcL.png" alt="it&amp;#39;s like a wave"&gt;&lt;/p&gt;
&lt;p&gt;What's happening is that if you pick a row or a column, you find that the rotation values are monotonically increasing but at a slightly different starting point and because the atan2 isn't around, the darts just continue rotating and rotating...&lt;/p&gt;
&lt;p&gt;Ok, so now we have a moderately intriguing slope field which, let's be honest, is something of an accidental slope field at the moment.&lt;/p&gt;
&lt;p&gt;So, back to our project: how do we animate it? Well, the neat thing again is that if you walk along the y axis (or x axis) every dart rotates however much you moved. So let's say you start with that static image above and, for each point, you increase &lt;em&gt;y&lt;/em&gt; or &lt;em&gt;x&lt;/em&gt; in that &lt;em&gt;y' = x + y&lt;/em&gt; by a little without actually moving the point, they &lt;em&gt;all&lt;/em&gt; appear to rotate just a little bit more counterclockwise, like so:&lt;/p&gt;
&lt;iframe src="http://gfycat.com/ifr/LinearWideeyedHorsefly" frameborder="0" scrolling="no" width="600" height="200" style="-webkit-backface-visibility: hidden;-webkit-transform: scale(1);"&gt;&lt;/iframe&gt;&lt;p&gt;Which is great and hypnotic. But how do we incorporate this into the announcement banner?&lt;/p&gt;
&lt;p&gt;My first instinct was animated gif: but the gifs ended up being multi-megabyte monstrosities and movies no better. Even the gyfcat embed up a few paragraphs ago is, at worst, 4M and, at best, a 400k webp movie. and the quality... i mean, you have eyes, just &lt;em&gt;look&lt;/em&gt; at it... it's let's just say, not the best. Ugh, this is not a very complicated thing! it's just a bunch of triangles rotating round and round and...&lt;/p&gt;
&lt;h2&gt;wait a minute&lt;/h2&gt;
&lt;p&gt;Instead of actually rendering some background image and scaling it up, why not do the animation in the browser using a technique you may have heard of called &lt;a href="http://en.wikipedia.org/wiki/Dynamic_HTML"&gt;Dynamic HTML&lt;/a&gt;.&lt;sup class="footnote-ref" id="fnref-dhtml"&gt;&lt;a href="#fn-dhtml"&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;staggering animations&lt;/h2&gt;
&lt;p&gt;Here's how I would roughly do it: i would use a &lt;em&gt;single&lt;/em&gt; dart image, lay out a bunch of them on a grid and then rotate them with css. At first i thought&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I can set each dart's from-rotation and it's to-rotation dynamically!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And i would use &lt;code&gt;css&lt;/code&gt; to start each dart's animation at, say, some radian value, setup a keyframe animation to rotate 2π radians over some fixed time scale and then call it a day. Genius!&lt;/p&gt;
&lt;p&gt;But because&lt;sup class="footnote-ref" id="fnref-pleasemaildan"&gt;&lt;a href="#fn-pleasemaildan"&gt;13&lt;/a&gt;&lt;/sup&gt; there's no easy way to create a parameterized css animation with arbitrary start/end points and because it &lt;em&gt;felt&lt;/em&gt; wasteful to specify a new animation starting at some radian value going a full 2π for all radian values, i instead opted for a more, shall we say, lo-fi approach.&lt;/p&gt;
&lt;p&gt;Instead, each dart would use the &lt;em&gt;exact-same 2π rotation animation&lt;/em&gt;, but to stagger the animations, I would set each dart's &lt;em&gt;animation delay&lt;/em&gt; to achieve the same effect. Then, I would set this field of spinning darts on a z-index below an actual html notification.&lt;/p&gt;
&lt;h2&gt;implementing this&lt;/h2&gt;
&lt;p&gt;My &lt;a href="https://gist.github.com/nsfmc/fc241d6f97a4b3b5203d/95b38bcc9707128c2b1d0501d22f48d070eea036"&gt;first attempt&lt;/a&gt; did something comical: because i had already generated the animation above using plotdevice as part of my testing, i already had a series of timing offsets along with x/y positions so i printed the starting rotation for each dart and saved that as a json array which i loaded directly into a &lt;em&gt;very&lt;/em&gt; small react template yielding something approximately like the&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dartRot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.469&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;few&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;megabytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;more&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...]&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dartRot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;animationDelay&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dartRot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I iterated over &lt;em&gt;all the values&lt;/em&gt; and then used that to set the animation delay. My goodness! how ridiculous! That's basically as bad as writing out all those css animations one-by-one. But i mean, the takeaway was that &lt;em&gt;the idea&lt;/em&gt; was not bad &lt;em&gt;even if&lt;/em&gt; my initial implementation was.&lt;/p&gt;
&lt;p&gt;Instead, I set down the project for the day and it took until the next morning to realize that i could do something &lt;em&gt;much&lt;/em&gt; less ridiculous:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dartCols&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dartRows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;animationDelay&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This wasn't even that much more work, i had already done much of it in python,  i just needed to translate it! Here's basically a demo of that react implementation.&lt;sup class="footnote-ref" id="fnref-democaveat"&gt;&lt;a href="#fn-democaveat"&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="jsbin-embed" href="http://jsbin.com/cemewot/embed?output"&gt;JS Bin on jsbin.com&lt;/a&gt;&lt;script src="http://static.jsbin.com/js/embed.min.js?3.34.0"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;The nice thing here is that not only is the code &lt;em&gt;not&lt;/em&gt; packed with magic arrays, it's also wayyyyyy more obvious what's going on. Even setting the x/y positions (&lt;em&gt;not shown&lt;/em&gt;) in the style object is more obvious and the code you end up with allows you to think in terms of a unit grid but render elements by twiddling the knobs of your various scale factors.&lt;/p&gt;
&lt;p&gt;Because the animation works by setting an animation delay, it slowly staggers into being. So when the banner appears, everything starts off pointing to one direction only to eventually fall in line at some point along the animation delay.&lt;/p&gt;
&lt;p&gt;At Khan Academy, we have a neat tool to let you prototype a react component in the context of the site called the React Sandbox. It works on the local dev server and lets you get a quick jsbin/fiddle environment but with all the goodies you've come to expect on the site. Using it, you get something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dl.dropboxusercontent.com/u/406291/Screenshots/quV1.png" alt="an image of the header inside the react sandbox"&gt;&lt;/p&gt;
&lt;p&gt;The design isn't super mind blowing: it's a spin on the regular learnstorm header, but it's... &lt;em&gt;alive&lt;/em&gt;! And, more importantly, it transfers the gravity of the original learnstorm header into celebrating the accomplishments of our regional winners and brings the identity to life even if it doesn't directly animate the circular slope field.&lt;/p&gt;
&lt;h2&gt;lessons learned&lt;/h2&gt;
&lt;p&gt;Well, aside from the fact that it's fun to use math to make cool notifications, the main lesson (i think), is that by thinking about prototyping (and parametrizing) early on, you can come up with interesting ways of procedurally generating animations later on even when your environment (like css) is constrained.&lt;/p&gt;
&lt;p&gt;Part of this was definitely baked into the early differential equation investigations. Later this was teased out using python and in the end, even though the animation was implemented using react, the various explorations let me realize that this is the sort of thing that you could implement with anything else like d3 or even good ol' jQuery or handlebars.&lt;/p&gt;
&lt;p&gt;In the end, somebody who had received the notification by visiting the site would see this (except animating):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dl.dropboxusercontent.com/u/406291/Screenshots/YQtk.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;If you saw the notification, congrats on participating in learnstorm! It was great fun making something for you :)&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;&lt;li id="fn-coppa"&gt;&lt;p&gt;let's put it this way, i am &lt;em&gt;not&lt;/em&gt; a lawyer, but i &lt;em&gt;do&lt;/em&gt; know that &lt;a href="https://www.ftc.gov/tips-advice/business-center/guidance/complying-coppa-frequently-asked-questions"&gt;COPPA&lt;/a&gt; prevents everyone from collecting any personally identifiable information for anyone under 13 (that includes email addresses).&lt;a href="#fnref-coppa" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-purplerain"&gt;&lt;blockquote&gt;&lt;p&gt;... "Purple rain," later the title of a 1984 song, album, and film (and the tour that supported both the album and film), from the artist Prince. Although it is not known if there is actually any connection, both Mikel Toombs of The San Diego Union and Bob Kostanczuk of the Post-Tribune have written that Prince got the title directly from "Ventura Highway". Asked to explain the phrase "purple rain" in "Ventura Highway", Gerry Beckley has responded: "You got me". (via &lt;a href="https://en.wikipedia.org/wiki/Ventura_Highway#Legacy"&gt;wikipedia&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href="#fnref-purplerain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-sloperefresh"&gt;&lt;p&gt;A refresher: a slope field (naturally) is the slope (and often magnitude) of a differential equation plotted along a grid. You can use slope fields to build intuition about the nature of first order differential equations, their tangent points, and so forth.&lt;a href="#fnref-sloperefresh" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-1803revisited"&gt;&lt;p&gt;slope fields make up the lion's share of the early weeks of a diff eq class and are by far the least interesting part: all the applied and domain-specific uses of differential equations end up being wayyyyy more interesting.&lt;a href="#fnref-1803revisited" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-slopedetour"&gt;&lt;p&gt;in a blog post full of detours, i should point out how satisfying it is to end up at an early 'solution' that requires the simplest of computations.&lt;a href="#fnref-slopedetour" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-divzero"&gt;&lt;p&gt;because it takes the denominator as an argument, &lt;code&gt;atan2&lt;/code&gt; avoids the standard movie trope of "tricking" the computer by having it try to divide by zero.&lt;a href="#fnref-divzero" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-atan2deets"&gt;&lt;p&gt;specifically, you're going to ask for &lt;code&gt;radians = Math.atan2(y, -x)&lt;/code&gt;&lt;a href="#fnref-atan2deets" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-plod"&gt;&lt;p&gt;plotdevice is great! it's a python-based drawing application i'm real fond of. Take a look, you might like it! If you like the idea behind processing but you're whatever on java, then you'll probably enjoy it.&lt;a href="#fnref-plod" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-crytime"&gt;&lt;p&gt;a crit (not &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Criterium"&gt;that crit&lt;/a&gt;&lt;/em&gt;) is a time to look over design work with your peers, evaluate and discuss what's succeeding, what's not (and for both, &lt;em&gt;why&lt;/em&gt;), and offer suggestions for how it could communicate what it needs to most efectively.&lt;a href="#fnref-crytime" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-sandhillfootnote"&gt;&lt;p&gt;I mean, we're &lt;em&gt;already&lt;/em&gt; in the bay area, what sort of blog post from a startup doesn't enthusiastically and vigorously talk and rationalise pivoting as a way of life.&lt;a href="#fnref-sandhillfootnote" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-herpderp"&gt;&lt;p&gt;who does that? &lt;em&gt;i&lt;/em&gt; wouldn't do that. who would do such a thing?&lt;a href="#fnref-herpderp" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-dhtml"&gt;&lt;p&gt;"...a more elegant weapon for a more civilized age" -- &lt;a href="https://www.youtube.com/watch?v=0aRtupiY9Dw"&gt;alec guiness&lt;/a&gt;&lt;a href="#fnref-dhtml" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-pleasemaildan"&gt;&lt;p&gt;please, feel free to correct me on hn&lt;a href="#fnref-pleasemaildan" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-democaveat"&gt;&lt;p&gt;In this demo, the darts are all displayed &lt;code&gt;inline-block&lt;/code&gt; just to avoid having to be distracted by setting position manually&lt;a href="#fnref-democaveat" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>why write todo comments?</title><link href="https://generic.cx/essays/comments/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:b416e9d0-90bc-30fe-b622-2d1d83561fec</id><content type="html">&lt;p&gt;The other day while listening to &lt;a href="http://edgecasesshow.com/111-here-be-dragons-style-comments.html"&gt;edge cases&lt;/a&gt;, i was tickled to hear &lt;a href="http://twitter.com/rentzsch"&gt;wolf&lt;/a&gt; talk about comments and how, as he's aged, he prefers really only two flavors of comments, API Comments/Documentation and &lt;a href="http://en.wikipedia.org/wiki/Here_be_dragons"&gt;here be dragons&lt;/a&gt; comments. But sadly, he didn't mention the kind of comments i'm going to talk about here.&lt;/p&gt;
&lt;p&gt;To begin, he cites &lt;a href="http://en.wikipedia.org/wiki/Best_coding_practices#Commenting"&gt;this gem&lt;/a&gt; from wikipedia:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Due to time restrictions or enthusiastic programmers who want immediate results for their code, commenting of code often takes a back seat.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At &lt;a href="http://www.khanacademy.org/careers"&gt;work&lt;/a&gt;, we have a slogan "&lt;a href="http://bjk5.com/post/60760280107/shipping-beats-perfection-explained"&gt;shipping beats perfection&lt;/a&gt;"&lt;sup class="footnote-ref" id="fnref-sbp"&gt;&lt;a href="#fn-sbp"&gt;1&lt;/a&gt;&lt;/sup&gt; and it is much of the rationale for this other kind of comment, which i will call &lt;em&gt;the unblocking comment.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;the unblocking comment&lt;/h3&gt;
&lt;p&gt;The practice, introduced to us via craig silverstein&lt;sup class="footnote-ref" id="fnref-csilvers"&gt;&lt;a href="#fn-csilvers"&gt;2&lt;/a&gt;&lt;/sup&gt;, (and to be clear, khan academy is &lt;em&gt;not&lt;/em&gt; unique here) is to leave comments for the things you recognize are &lt;em&gt;probably&lt;/em&gt; out of immediate scope and that you aren't able to get to right now so that you can &lt;em&gt;get on with your original commit&lt;/em&gt; (i.e. unblocking yourself).&lt;/p&gt;
&lt;p&gt;let me show you a 'for instance' from my super hacky &lt;a href="https://github.com/nsfmc/stumblr"&gt;tumblr editor&lt;/a&gt; package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# TODO(marcos): what if this also fails?&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;more_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;meta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;more_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;response&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# TODO(marcos): maybe check a pref for this&lt;/span&gt;
    &lt;span class="n"&gt;webbrowser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;post_url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;the two &lt;code&gt;TODO(marcos)&lt;/code&gt; comments map to things that i might, at some point get to eventually, but that aren't truly 'blocking' or 'stopships' or whatever. They're basically baby yaks that if i'm not careful, might develop into full grown yaks&lt;sup class="footnote-ref" id="fnref-yakshaving"&gt;&lt;a href="#fn-yakshaving"&gt;3&lt;/a&gt;&lt;/sup&gt; demanding my attention and shears.&lt;/p&gt;
&lt;p&gt;or more recently, this past week, while playing with &lt;a href="http://github.com/koenbok/Framer"&gt;Framer&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// TODO(marcos): use cycle here&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;collapsed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nx"&gt;scrubBG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Click&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// TODO(marcos): instead of just unclipping the layer&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// animate the text in separately&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And, to be clear, the code works! Right now! it's just that &lt;em&gt;if&lt;/em&gt; i ever want it to work better or if i return to it, i have already outlined a roadmap for myself (or others) on how to do that. The framer code is &lt;em&gt;by definition&lt;/em&gt; a prototype, so let's be honest, who cares if i'm using &lt;code&gt;cycle&lt;/code&gt; correctly, but my intuition to Do The Right Thing™&lt;sup class="footnote-ref" id="fnref-dtrt"&gt;&lt;a href="#fn-dtrt"&gt;4&lt;/a&gt;&lt;/sup&gt; is the sort of yak that might have wasted precious prototyping time.&lt;/p&gt;
&lt;p&gt;These todos, in the context of both projects, are the software development equivalent of a "&lt;a href="http://www.43folders.com/2006/07/24/b2gtd-mind-sweep"&gt;sweep&lt;/a&gt;" in gtd&lt;sup class="footnote-ref" id="fnref-gtd"&gt;&lt;a href="#fn-gtd"&gt;5&lt;/a&gt;&lt;/sup&gt;. Which is to say, that in writing the todos, i've explained what i need to do (but i can't do right now), and by doing so, i've absolved myself of the guilt of doing it &lt;em&gt;this very minute.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Allow me to repeat: by leaving the &lt;code&gt;todo()&lt;/code&gt;, i communicate to others (or future-marcos) my understanding that the problem exists but that rather than investing time in writing possibly unneccessary code, &lt;em&gt;i am moving on.&lt;/em&gt; In the present day, it gets me unstuck and moves me forward towards a working implementation that i can nit-pick later.&lt;/p&gt;
&lt;p&gt;Again, i'm &lt;em&gt;not&lt;/em&gt; promising work on the femtoyak right now, i'm not even committing to it in the future (as my tumblr comments have borne out)—i'm doing two things: publicly acknowledging the issue &lt;em&gt;exists&lt;/em&gt; and punting it so that i can get on with my day. Why even put my name on it, you ask? If somebody does get around to it, they know exactly who to ask for more detail! I'm happy to help them!&lt;/p&gt;
&lt;p&gt;Most of you are shouting at your monitor saying that this is like time-management 101, but it took my subconsciously creating these &lt;code&gt;todo()&lt;/code&gt; comments to get me to &lt;em&gt;truly&lt;/em&gt; internalize their &lt;a href="http://en.wikipedia.org/wiki/Real_Ultimate_Power"&gt;real ultimate power&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The novelty here, the point where this ties back to the quote, is that this is actually &lt;em&gt;the opposite&lt;/em&gt; of the problem outlined in the wikipedia article. In my vim to get towards a working solution, i'm leaving important implementation and polish related comments for myself (and others) so that i can address it better... but only once i have something that merits fixing.&lt;/p&gt;
&lt;p&gt;Just to be clear, though, if you have actual bugs that merit tracking, you really should find a way to get to those in a way that's higher priority than these &lt;code&gt;TODO()&lt;/code&gt;s which really are historic context-rich vignettes of the rabbit-holes you've avoided. By all means, continue logging assertions and running unit tests for your bits that need to remain stable!&lt;/p&gt;
&lt;p&gt;But... i want to repeat the central 'point' here which is that &lt;em&gt;comments don't have to take a backseat to getting things done&lt;/em&gt;. In fact, i hope i've shown that, comments can provide a relatively easy way to get you from "zero to working"&lt;sup class="footnote-ref" id="fnref-mvp"&gt;&lt;a href="#fn-mvp"&gt;6&lt;/a&gt;&lt;/sup&gt; while providing a legitimate roadmap for you to get to something sustainably developed in the future.&lt;/p&gt;
&lt;h3&gt;dealing with guilt&lt;/h3&gt;
&lt;p&gt;I've been trying to find a good way to end this essay, some sort of meditation on why something as mundane as a todo comment would even be necessary to write about. It took the span of a few weeks for the podcast gods to answer my call in the form of an &lt;a href="http://atp.fm/episodes/95"&gt;episode of ATP&lt;/a&gt;, going full-circle.&lt;/p&gt;
&lt;p&gt;The episode is interesting because it deals with deeper issues of obligation and guilt in software development and maintenance. It's a highly relatable episode for anyone that has participated in the open source ecosystem. In particular, it pairs well with this 100% insightful essay called &lt;a href="http://lucumr.pocoo.org/2013/11/28/emotional-programming/"&gt;Emotional Programming in Open Source&lt;/a&gt; by Armin Ronacher. You should definitely read it, but I'm going to pull a whole chunk of it here:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I found it quite hard this year to work on my own projects because the bug trackers were full of things I personally did not really care about. The things I wrote over the last few years all work so well for me, that I don't need to fix problems or add things. When there is something that needs fixing, I will work on it, but otherwise I don't necessarily get the motivation to work on it.&lt;/p&gt;
&lt;p&gt;... the whole porting to Python 3 thing has killed so much motivation for working on my project for me. I cannot use Python 3 in practice and having to deal with issues I don't have myself is one of the most frustrating things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He calls out an important distinction between bugs which are currently breaking a build and in-utero yaks which shouldn't be on your radar just yet. Todos, in contrast, aren't traditional 'bug' reports, they're &lt;em&gt;opportunities;&lt;/em&gt; for refactoring, future architecture changes, performance optimizations, etc. Maybe they're nice to haves, maybe you discover they're actual bugs: time will tell.&lt;/p&gt;
&lt;p&gt;The effect of writing and revisiting &lt;code&gt;TODO()&lt;/code&gt;s is better understanding the dynamic between problems you have &lt;em&gt;right now&lt;/em&gt; and issues that are actually non-issues. If a todo lingers for a while, it's probably &lt;em&gt;not&lt;/em&gt; an issue you really have and certainly not one that's keeping your project from functioning in the short term. Maybe if you're bored you hit it up. Maybe a contributor decides to attack it.&lt;/p&gt;
&lt;p&gt;But the problem that many of us have, is that &lt;em&gt;we know&lt;/em&gt; what The Right Thing &lt;em&gt;is&lt;/em&gt; but our time and priorities are at odds with that. Does that choice make us worse programmers? Should it hold us up from &lt;a href="https://news.ycombinator.com/item?id=8132939"&gt;releasing a project&lt;/a&gt;? Would it be better if we just pulled our project? No to all. It's a calculus of writing code, our time available, obligation to our code's users and our own guilt. Those are challenging things to balance!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=0SARbwvhupQ"&gt;Developing software in the open is complicated&lt;/a&gt; &lt;em&gt;even&lt;/em&gt; when when you're bootstrapping it in private. But it gets more difficult if making progress is fraught with guilt and obligation to some all-seeing &lt;a href="https://twitter.com/karlvanhoet"&gt;Van Hœt&lt;/a&gt;. So many of these issues, these problems in casual development are complex issues of time management, guilt and obligation. Leaving todos can free you from worry and push you towards fixing issues you care about right now so you can start enjoying the software you make.&lt;/p&gt;
&lt;h3&gt;thanks&lt;/h3&gt;
&lt;p&gt;it wouldn't be a &lt;em&gt;real&lt;/em&gt; thousand word internet blog post without an artisinal colophon, so thanks for the feedback and comments: &lt;a href="http://twitter.com/marcia_lee"&gt;@marcia_lee&lt;/a&gt;, &lt;a href="http://twitter.com/andy_matuschak"&gt;@andy_matuschak&lt;/a&gt;, &lt;a href="http://twitter.com/kamens"&gt;@kamens&lt;/a&gt;, &lt;a href="http://twitter.com/pgbovine"&gt;@pgbovine&lt;/a&gt;, &lt;a href="http://twitter.com/dmnd_"&gt;@dmnd_&lt;/a&gt;, and &lt;a href="https://github.com/chrisklaiber"&gt;chrisklaiber&lt;/a&gt; and &lt;a href="https://twitter.com/dylanvee"&gt;@dylanvee&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have any thoughts on this and have made it all this way, i'd love to hear from you. Feel free to send me an email.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;&lt;li id="fn-sbp"&gt;&lt;p&gt;its background is this other phrase, &lt;em&gt;&lt;a href="https://dschool.stanford.edu/groups/k12/wiki/548fb/Bias_Toward_Action.html"&gt;bias towards action&lt;/a&gt;&lt;/em&gt;, which, to be honest, i don't think i'd heard until this past week. It is, however, an interesting cousin of the more contentious Worse is Better™[^wib]&lt;a href="#fnref-sbp" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-csilvers"&gt;&lt;p&gt;craig has been responsible for many fantastic process gems, so i feel very lucky to be able to crib this one from him almost entirely wholesale for my own &lt;a href="https://www.youtube.com/watch?v=xdhLQCYQ-nQ"&gt;internet fame&lt;/a&gt; and glory.&lt;a href="#fnref-csilvers" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-yakshaving"&gt;&lt;p&gt;&lt;img src="malcolm-in-the-middle.gif" alt=""&gt;, &lt;a href="http://raganwald.com/2014/02/28/a-programmers-story.html"&gt;which see&lt;/a&gt;&lt;a href="#fnref-yakshaving" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-dtrt"&gt;&lt;p&gt;i'm pretty sure spike lee never thought &lt;a href="http://en.wikipedia.org/wiki/Do_the_Right_Thing"&gt;his movie&lt;/a&gt; would ever be referenced so often in terms of software engineering discussions&lt;a href="#fnref-dtrt" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-gtd"&gt;&lt;p&gt;A sweep, briefly, is this nifty idea where in order to be able to prioritize all the task you possibly have, you put everything you can think that &lt;em&gt;might&lt;/em&gt; be a task into a task list. By moving all those ideas out of your head and into a place you're likely to look at later, you allow your mind to only wander to tasks that are relevant &lt;em&gt;now.&lt;/em&gt; gtd® is &lt;a href="http://www.5by5.tv/b2w"&gt;copyright&lt;/a&gt; DavidCo 2001.&lt;a href="#fnref-gtd" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-mvp"&gt;&lt;p&gt;although philosophically aligned, this is &lt;em&gt;not&lt;/em&gt; going to devolve into an essay about minimal/&lt;a href="http://www.allenpike.com/2013/maximum-viable-products/"&gt;maximal&lt;/a&gt; viable products. phew.&lt;a href="#fnref-mvp" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>chaining font descriptors in uikit</title><link href="https://generic.cx/essays/font-descriptors/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:3d95c2c5-fa62-3a3c-9761-f7f0b7176028</id><content type="html">&lt;p&gt;A  &lt;a href="http://healthyhackathon.khanacademy.org"&gt;few months ago&lt;/a&gt; i decided to try my hand at doing some work in swift but i ended up yak shaving my way into finally understanding what's going on in textkit. One of the neat things i came across was this idea of chaining font descriptors to progressively activate opentype features on a font. Now that we're in &lt;a href="http://hackweek.khanacademy.org"&gt;full blown hack week&lt;/a&gt; at khan academy, i'm taking the time to use this code for a project i'm working on.&lt;/p&gt;
&lt;p&gt;This isn't a particularly new idea, but inspired by &lt;a href="http://nshipster.com/alamofire/"&gt;alamofire's neat post about chaining&lt;/a&gt; vis-a-vis http requests and the current '&lt;a href="http://ilovetypography.com/2014/10/25/why-a-better-opentype-user-interface-matters/"&gt;uproar&lt;/a&gt;' about opentype features, i thought i'd write up what i learned then. Plus it's cute because swift™ and also because chaining!&lt;/p&gt;
&lt;!-- more --&gt;

&lt;p&gt;One of the things i was hoping to do was exploit as much of the power of Proxima Nova in an iOS app. Like most modern fonts, proxima includes a bevy of opentype features including true small caps, old style figures, stylistic sets and so forth. You can learn &lt;a href="http://www.marksimonson.com/fonts/view/proxima-nova"&gt;more about proxima&lt;/a&gt; or &lt;a href="http://www.marksimonson.com/assets/content/fonts/ProximaNovaOverview.pdf"&gt;read its overview (pdf)&lt;/a&gt; which explains most everything you might want to turn on and off.&lt;/p&gt;
&lt;p&gt;The neat thing about chaining font descriptors is that it gives you a very handy way to grab a &lt;code&gt;UIFont&lt;/code&gt; that has the opentype features you want available. For instance, a font which has small caps and forced uppercase-&amp;gt;small caps (i.e. &lt;code&gt;SMCP&lt;/code&gt; &amp;amp; &lt;code&gt;C2SC&lt;/code&gt;), you can do something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;styledFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ProximaNova-Regular&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;14.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithSMCP&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithC2SC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithONUM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These are three of the most basic (and popular) kinds of &lt;a href="https://www.microsoft.com/typography/otspec/features_ae.htm"&gt;opentype features&lt;/a&gt; on fonts, namely: Small Caps (&lt;code&gt;SMCP&lt;/code&gt;), Caps to Small Caps (&lt;code&gt;C2SC&lt;/code&gt;), and Oldstyle Figures (&lt;code&gt;ONUM&lt;/code&gt;). I used these 4-letter feature abbreviations because they most closely map onto the features you end up seeing in font brochures and are the exact same features that type designers implement when they're writing up opentype code.&lt;/p&gt;
&lt;p&gt;but... how? and what does this mean? Let's find out!&lt;/p&gt;
&lt;h2&gt;how fonts in ios work&lt;/h2&gt;
&lt;p&gt;&lt;img src="1520000234_46b91b992e_b.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;in iOS, there's this idea of a font as a specific instance of a typeface at a specific size. Actually, the photo up above sort of explains the idea perfectly. A font in that case would be "10pt Garamond." If you were instantiating it, you might say&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;oldFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Garamond&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and for any run of text in iOS, you are allowed to set this UIFont property however you like. If you want to have a run of text with lining figures followed by a run of text with &lt;a href="http://en.wikipedia.org/wiki/Text_figures"&gt;old style figures&lt;/a&gt;, you need to do the equivalent of&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;liningFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Garamond&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;osfFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Garamond-scOSF&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and whenever you need to use both of them, what you do is create and &lt;a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSAttributedString_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003622"&gt;attributed string&lt;/a&gt; that contains a run of text whose font may be &lt;code&gt;liningFont&lt;/code&gt; and then append a second run of text whose font is &lt;code&gt;osfFont&lt;/code&gt;. In the abstract, i think this doesn't sounds &lt;em&gt;totally&lt;/em&gt; crazy, but it is a bit verbose. the above example reads like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;liningRun&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;NSMutableAttributedString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;01234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NSFontAttributeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;liningFont&lt;/span&gt;&lt;span class="p"&gt;!])&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;osfRun&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;NSAttributedString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;567890&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NSFontAttributeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;osfFont&lt;/span&gt;&lt;span class="p"&gt;!])&lt;/span&gt;
&lt;span class="n"&gt;liningRun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;appendAttributedString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;osfRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which results in something like&lt;/p&gt;
&lt;p&gt;&lt;img src="1b9X.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Attributed strings can set a bunch of &lt;em&gt;attributes&lt;/em&gt;: its font (which we've just seen), the kerning or tracking for a region of text, its use of ligatures, its color, &lt;a href="https://developer.apple.com/library/ios/documentation/uikit/reference/NSAttributedString_UIKit_Additions/Reference/Reference.html"&gt;or any number of other attributes&lt;/a&gt;. The key is that these attributes are settings that you can apply to areas of text. But important to keep in mind is that these settings, while similar to a Font's settings, are different.&lt;/p&gt;
&lt;p&gt;In contrast, switching a font's size, small caps, oldstyle figures, stylistic alternates and other opentype features require creating a new &lt;a href="https://developer.apple.com/library/ios/documentation/uikit/reference/UIFont_Class/Reference/Reference.html#//apple_ref/occ/cl/UIFont"&gt;font object&lt;/a&gt; with those features enabled rather than specifying that feature as an attribute of a region inside an attributed string.&lt;/p&gt;
&lt;p&gt;As an aside, i personally think it's weird that something like kerning or use of ligatures is assumed to apply equally to multiple fonts in the same attributed string but something like an opentype feature like old stlyle figures is assumed to be a feature on a font, but that's just the world we live in.&lt;/p&gt;
&lt;p&gt;In core text and by proxy, text kit, there are two ways to create a font. The first is by creating a new &lt;a href="https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIFont_Class/index.html#//apple_ref/occ/clm/UIFont/fontWithName:size:"&gt;&lt;code&gt;UIFont&lt;/code&gt;&lt;/a&gt; with a font name and a font size. The second is by using a &lt;a href="https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIFontDescriptor_Class/index.html#//apple_ref/occ/cl/UIFontDescriptor"&gt;UIFontDescriptor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Font descriptors are an oddly optimistic technology. You basically say something like "i want a font on this device that matches, as closely as possible these properties" and the system is responsible for returning a font which has as many of those attributes as possible. I'll talk later about why i think font descriptors are overly optimistic, but let's get to the codey parts.&lt;/p&gt;
&lt;h3&gt;futzing with descriptors&lt;/h3&gt;
&lt;p&gt;the typical approach to muddling around with fonts, in particular, activating opentype features, is something like what you see in the fantastic 2013 wwdc session "&lt;a href="http://devstreaming.apple.com/videos/wwdc/2013/223xex5xsgdfh1ergtjrqwoghbj/223/223-SD.mov?dl=1"&gt;using fonts with text kit&lt;/a&gt;" session by ned holbrook:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithONUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;oDesc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;baseFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontDescriptor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;AnyObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;UIFontDescriptorFeatureSettingsAttribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;UIFontFeatureTypeIdentifierKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;kNumberCaseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;UIFontFeatureSelectorIdentifierKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;kLowerCaseNumbersSelector&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;newDesc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oDesc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontDescriptorByAddingAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;newDesc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The idea is that you serialize your UIFont instance into a font descriptor, mutate that by adding new features and finally create a brand new Font seeded by that modified descriptor (using &lt;code&gt;0.0&lt;/code&gt; as the font size preserves the descriptor's font size) Here is how you might make use of this code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;myFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ProximaNova-Regular&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;14.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;myFancyFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fontWithONUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myFont&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's not wrong, per sé, but it's not exactly the nicest syntax for this sorta thing and you can imagine the dreaded lisp-style fingernailcuttings for a partucilurly extravagant font with many features. The point of this blog post, though, is to suggest that PERHAPS there is a nicer looking way of doing this. Let's see it!&lt;/p&gt;
&lt;h3&gt;chaining like a boss&lt;/h3&gt;
&lt;p&gt;Instead of passing in a font and getting back another font, why not actually just start by extending UIFont? We can do this by writing basically the EXACT same sort of code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;AnyObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;AnyObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;originalDesc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontDescriptor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="bp"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;AnyObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;UIFontDescriptorFeatureSettingsAttribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="n"&gt;UIFontFeatureTypeIdentifierKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;UIFontFeatureSelectorIdentifierKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;newDesc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;originalDesc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontDescriptorByAddingAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;newDesc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Two changes here: first, i'm extending &lt;code&gt;UIFont&lt;/code&gt; with this method. This means that any &lt;code&gt;UIFont&lt;/code&gt; instance can now call this method to its heart's content.&lt;/p&gt;
&lt;p&gt;But also notice that the substantive change that's happened here is that rather than hard-coding the values for &lt;code&gt;UIFontFeatureTypeIdentifierKey&lt;/code&gt; and &lt;code&gt;UIFontFeatureSelectorIdentifierKey&lt;/code&gt; i instead pass them directly into the method as its only arguments (i no longer have to pass in the font because it's available as &lt;code&gt;self&lt;/code&gt;.) So for any &lt;code&gt;UIFont&lt;/code&gt; instance, you can now imbue it with any set of features easily.&lt;/p&gt;
&lt;p&gt;But instead of dealing with features abstractly, we'd like to at least have a way of talking about them without having to resort to actual key values like savages.&lt;/p&gt;
&lt;p&gt;So we do something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// repeated from above&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithSMCP&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;kLowerCaseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kLowerCaseSmallCapsSelector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithC2SC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;kUpperCaseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kLowerCaseSmallCapsSelector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithONUM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;kNumberCaseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kLowerCaseNumbersSelector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you're wondering what other options exist other than &lt;code&gt;kNumberCaseType&lt;/code&gt;, you can find other available features by looking at the &lt;code&gt;&amp;lt;ATS/SFNTLayoutTypes.h&amp;gt;&lt;/code&gt; header in xcode or, more legibly and with comments, the newly &lt;a href="https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type37"&gt;redesigned truetype reference manual&lt;/a&gt;. In particular, this wacky naming convention i've chosen for my methods is due to me consciously ignoring the ATS/&lt;a href="http://en.wikipedia.org/wiki/Apple_Advanced_Typography"&gt;AAT&lt;/a&gt; abstractions (which &lt;em&gt;presumably&lt;/em&gt; exist as a result of the churn of font and type layout technologies GX/AAT/TTF/OTF tech during the late 90s and early aughts) in favor of OpentType tags.&lt;/p&gt;
&lt;p&gt;Anyhow, with these three methods, you can do something along the lines of writing code like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;styledFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ProximaNova-Regular&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;14.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="c1"&gt;// explicitly unwrap since init -&amp;gt; UIFont?&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithSMCP&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithC2SC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithONUM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and that would give you a font that would turn "A small caps font with 1234567890" into:&lt;/p&gt;
&lt;p&gt;&lt;img src="B3Xl.png" alt=""&gt;&lt;/p&gt;
&lt;h3&gt;gripes&lt;/h3&gt;
&lt;p&gt;I obliquely mentioned that i have 'mixed' feelings about font descriptors and it's mostly because they're explicitly unreliable and because they assume the existence of exhaustive font families, such that you'd be able to query for a font with some properties like number types or special characters and so forth.&lt;/p&gt;
&lt;p&gt;The system is designed to let you ask, but makes no assurances you'll get what you're looking for which can make it difficult to distinguish errors in your own code and errors in the font provided. Case in point &lt;a href="rdar://17624040"&gt;rdar://17624040&lt;/a&gt; (closed as dupe of &lt;a href="rdar://17624040"&gt;rdar://17624040&lt;/a&gt;), a bug in iOS' bundled version of Avenir Next that prevents SMCP and C2SC from activating even though the features are present in the font (or if queried via &lt;a href="https://developer.apple.com/library/ios/documentation/carbon/reference/CTFontRef/index.html#//apple_ref/c/func/CTFontCopyFeatures"&gt;&lt;code&gt;CTFontCopyFeatures&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Which is sort of disappointing because those sorts of silent errors make it harder to trust that the more ambitious family-querying and filtering qualities of Font Descriptors work &lt;em&gt;at all&lt;/em&gt;. That said, this is what you've got to work with.&lt;/p&gt;
&lt;p&gt;Since this was mostly an exploration of activating features, i thought i'd add in another method for selectively activating stylistic sets as a bonus for anyone that made it all the way to the end. Enjoy!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;pass in a positive integer to activate that stylistic set&lt;/span&gt;
&lt;span class="cm"&gt;or pass a negative integer to deactivate that set.&lt;/span&gt;
&lt;span class="cm"&gt;0 deactivates all features.&lt;/span&gt;
&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fontWithSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stylistic_set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;UIFont&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// this cheats a bit, assuming the enum values for the&lt;/span&gt;
    &lt;span class="c1"&gt;// stylistic sets stay numbered the same, but... oh well...&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;stylistic_set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;ss&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;kStylisticAlternativesType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;ss&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stylistic_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;kStylisticAlternativesType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)...(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;ss&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stylistic_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fontWithFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;kStylisticAlternativesType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;// in the worst case, return the font&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;epilogue&lt;/h3&gt;
&lt;p&gt;If you're actually going this route, it might not be totally crazy to actually create your own shell around UIFont rather than extending it. In particular, you'll end up doing &lt;em&gt;something&lt;/em&gt; like this if you do end up supporting Dynamic Type with non-helvetica in your app.&lt;/p&gt;
&lt;p&gt;Also, i'm totally open to the very likely idea that i have gotten something wrong here. Let me know if i've gotten anything wrong via twitter &lt;a href="http://twitter.com/nsfmc"&gt;@nsfmc&lt;/a&gt; or via email, which you should be able to find over to the left. hit me up with your feedback, please! thanks for reading!&lt;/p&gt;
</content></entry><entry><title>under the dome</title><link href="https://generic.cx/essays/under-the-dome/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:ee3c3534-7434-35f0-a505-80ccd77d335a</id><content type="html">&lt;p&gt;i came to Under the Dome after being disappointed at the second episode of the show, so i started the audiobook and about 50 percent of the way through &lt;i&gt;that&lt;/i&gt;, i started reading it proper. It was a true multimedia experience.&lt;/p&gt;
&lt;p&gt;First thing though, i went in knowing the rough gist of the ending and I found that this did not ruin much for me. Getting there isn't half the fun in this case, it's &lt;i&gt;all the fun.&lt;/i&gt; So if an ending can ruin something you enjoyed the other 99% of, you should sort of understand that the book is less about exploring "what caused the dome to trap this town?" than exploring "what happens after the dome goes down (and how)." Especially since, i think, the book allocates something like ten (disappointing) pages in total to fleshing out the "why" whereas it spends its other 99% dealing with the "what" and the "how."&lt;/p&gt;
&lt;p&gt;So after watching and listening and reading, i think it's best to think of this book/story like a TV serial because King is actually &lt;i&gt;really good&lt;/i&gt; at writing tv-style drama, down to the sort cheesy dialog, end-of-scene suspense and the instantly recognizable and memorable cast of new england townies. Each chapter has something between fifteen and twenty-five sections that simultaneously revolve around the chapter's major-arc and also serve to slightly advance the main plot. The book is something like twenty-five chapters long, so you can easily imagine treating it like a self-contained season-in-a-book. Or, i mean, a book.&lt;/p&gt;
&lt;p&gt;The advantage the book has over the show (or almost any show) is that if you enjoy the first chapters, the writing doesn't really change much and the plotting remains consistent throughout. We don't get superfluous chapters dealing with some minor character's drug problem or 'a day at the hospital'/bottle episode or really any other season-filler-trope. Certainly there are chapters which heavily focus on 'one thing,' but these chapters also make up critical events in the course of the story.&lt;/p&gt;
&lt;p&gt;Other reviews here point out how much of the ending is simply glossed over and that's &lt;i&gt;true&lt;/i&gt; and distressing because so much of the book loves exploring incredibly brief events and traumas in stunning 120fps at 1080p.&lt;/p&gt;
&lt;p&gt;It's implied that this brevity in explanation is due to the human inability to understand a deus-ex-machina and that, like most myths, the explanation is (probably) irrelevant. Should we &lt;i&gt;really&lt;/i&gt; consider Zeus' motivations? Do they matter considering he's not usually the focus of any story? Those sorts of question are at the heart of whether or not you'll be bummed by the book's resolution (as it is). Are you more interested in the fate of the good people of Troy or are you more interested in why the gods magically mess with Achilles or Paris when it suits the story.&lt;/p&gt;
&lt;p&gt;In contrast to contemporary TV plotting, the book doesn't have much by way of nonlinearity (ala flashforwords or chris nolanisms) and in a way that's great, but you start to think the book would have been better off being entirely bounded from the outset. Instead, it's not clear how long we're going to ride the chester's mill rollercoaster and so it's hard to calibrate our expectations for how meaningful any action is. Except you know that you're rapidly running out of pages. What do you make out of that situation? Suddenly two hundred pages to go, you end up in a horror novel where your sense of doom drives much of the suspense. There's a lot you get to see, but there's &lt;i&gt;a lot&lt;/i&gt;i&amp;gt; that you don't see and it really gets you thinking. Fifty pages to go and you start to believe &lt;i&gt;anything could happen&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;And then it's over.&lt;/p&gt;
</content></entry><entry><title>private use</title><link href="https://generic.cx/essays/private-use/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:77324efc-0473-38b7-a29e-4ea9be88a7dc</id><content type="html">&lt;p&gt;this isn't an essay, it's more like a journal. i'm putting things in here that i find interesting and i try to string them together. i will probably update this over time, it will change as i learn more but i will try to keep the learning-spirit of it present.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;the Private Use Areas are three ranges of code points (U+E000–U+F8FF in the BMP, and planes 15 and 16)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;-- &lt;a href="http://en.wikipedia.org/wiki/Private_Use_(Unicode)"&gt;Private Use Area (unicode)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I did the math and realized that's 137,469 possible glyphs. That's a lot of glyph.&lt;/p&gt;
&lt;p&gt;Here &lt;a href="http://www.khanacademy.org/"&gt;at work&lt;/a&gt;, I'd been trying to rationalize our use of various icons and so forth. But how? It's challenging because we have icons that are ka-specific but we also like having things like &lt;a href="http://fontawesome.io"&gt;fontawesome&lt;/a&gt; around.&lt;/p&gt;
&lt;p&gt;A while back, I made the decision to fork fontawesome so we could add in a few superfluous glyphs. &lt;a href="https://github.com/FortAwesome/Font-Awesome/network"&gt;Lots of peolpe do this actually&lt;/a&gt; but, maybe i'm missing something because I don't hear very much about it or their experiences forking an open source icon font.&lt;/p&gt;
&lt;h2&gt;what's the big deal?&lt;/h2&gt;
&lt;p&gt;The main challenges, as far as i'm concerned, at least as far as this all goes, is that when you're forking a stable project, you're creating some cognitive dissonance for your team who are already used to using the upstream version. Second, you're making a conscious decision to ignore or absorb upstream changes. Third, you may be trying to find a way to keep your project merging safely with the upstream project's.&lt;/p&gt;
&lt;p&gt;Iconfonts are a strange beast because (unlike most text faces) they have the capacity to be &lt;a href="http://icnfnt.com/"&gt;incredibly modular&lt;/a&gt;, but the tooling around them does not encourage it. And you basically have four options: fontlab, fontforge, glyphs, and robofont (to say nothing of fontographer and type tool), all of which natively use a different file format.&lt;/p&gt;
&lt;h2&gt;and why, again?&lt;/h2&gt;
&lt;p&gt;At the time i had two goals: add extra glyphs to the font and change fontawesome's &lt;code&gt;[class="icon-*"]&lt;/code&gt; selector so that it cost us less per-page-load. It turns out the selector change landed in 4.0, so at least i didn't spend too much time on that. But the issue of adding new glyphs remained legitimate.&lt;/p&gt;
&lt;p&gt;The secondary benefit, which i hadn't considered at first, but which now seems quite legitimate, is reducing the glyph count. Of my earliest experiments when beginning this process, was seeing how many font-awesome glyphs we used. Here's some handy shell incantation you can use to figure this out for yourself&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ack -ho "(?:[\" ])(icon-[a-z\-]+)" --html | \
sed 's/.\(.*\)/\1/' | \
sort | \
uniq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;if you have looked at fontawesome recently, you will find upwards of 350 glyphs in use. do you need all those? clearly services like the (mostly) disemvoweled &lt;a href="http://icnfnt.com/"&gt;icnfnt&lt;/a&gt; are aware of this disconnect, but naturally a question is how to generate a version of the font with the above information without having to bother with clickity clicking a website.&lt;/p&gt;
&lt;h2&gt;generating a font&lt;/h2&gt;
&lt;p&gt;by far the easiest way of going about this is by running the Generate Font action that every font editor has available. but... are there other ways? yes! there are.&lt;/p&gt;
&lt;p&gt;for example, if you're using a .ufo-based workflow, then you have at your disposal the afdko which now (natively) generates opentype fonts. If you're building one in release mode, the incantation goes something, for font awesome, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ makeotf -f FontAwesome.ufo \
-o FontAwesome.otf
-ff fdk/features \
-gf fdk/glyphOrder \
-mf fdk/menuname \
-r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which assumes you have a folder sitting around called fdk which contains opentype-specific features/glyphorder/etc. for reference, adobe publishes a &lt;a href="http://download.macromedia.com/pub/developer/opentype/Example-Font-Sources.zip"&gt;reference implementation&lt;/a&gt; for Minion. Miguel Sousa wrote a small thing about how to generate this in a &lt;a href="http://typophile.com/node/42076"&gt;typophile GlyphOrderAndAliasDB thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Robofont generates this folder when you run the Generate Font command&lt;/p&gt;
&lt;p&gt;if you are using robofont, then the app adds an entry to the font's &lt;code&gt;lib&lt;/code&gt; called &lt;code&gt;public.glyphOrder&lt;/code&gt; so you could, for example, do something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import robofab.world

def glyphorder(font_path='/path/to/font.ufo', dest='glyphOrder'):
    MAX_POINTS = 1114113
    font = robofab.world.OpenFont(font_path)

    if font.lib.get('public.glyphOrder', None):
        # for a robofont font, you can get
        order = font.lib.get('public.glyphOrder')
    else:
        # otherwise, you can do
        order = font.keys()
        sorted(order, key=lambda g: font[g].unicode or MAX_POINTS)

    with open(dest, 'w') as out:
        for g in order:
            outstr = "%s  %s" % (g, g)
            if font[g].unicode:
                outstr += "  uni%04X" % font[g].unicode
            out.write(outstr+"\n")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Removing glyphs from the glyphOrder file doesn't &lt;em&gt;really&lt;/em&gt; change anything and won't remove any of the glyphs from the font. to do that you need the &lt;code&gt;tx&lt;/code&gt; tool or you would need to build a new version of the font from the source ufo.&lt;/p&gt;
&lt;h2&gt;fixing the font version string&lt;/h2&gt;
&lt;p&gt;For whatever reason, &lt;code&gt;makeotf&lt;/code&gt; will generate an obtuse version string. It would be nice to be able to include things like "the git commit that yielded this font" rather than just a version number which may be stale.&lt;/p&gt;
&lt;p&gt;To do this, you need &lt;code&gt;ttx&lt;/code&gt; from the fonttools package. you can &lt;code&gt;pip install fonttools&lt;/code&gt; for that. Having it, you can then run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ttx myfont.otf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this will generate a file called myfont.ttx which is, semi-conveniently, an xml file.&lt;/p&gt;
&lt;p&gt;You can then modify it by running a python script like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;"""versionit.py - call like `python versionit.py font.ttx vcs_hash'

it will make a file called font-vcs_hash.ttx
"""
import sys
import os
import xml.etree.ElementTree as ET

def versionit(ttx_file, vcs_hash):
    ttx_root = ET.parse(ttx_file)
    version = ttx_root.find('CFF/CFFFont/version').get('value')

    for name in ttx_root.findall('name/namerecord[@nameID="5"]'):
        name.text = "Version %s; %s" % (version, vcs_hash)

    filename, extension = os.path.splitext(ttx_file)
    ttx_root.write('%s-%s%s' % (filename, vcs_hash, extension),
        xml_declaration=True, encoding='utf-8')

if __name__ == '__main__':
    filename = sys.argv[1]
    vcs_hash = sys.argv[2]
    versionit(filename, vcs_hash)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this is super-basic, but you could extend this however you like, presumably by having it call ttx again on the resultant file and the&lt;/p&gt;
&lt;h2&gt;the pua&lt;/h2&gt;
&lt;p&gt;in case it's not obvious, it's quite common to work with the pua, the good folks at &lt;a href="http://scripts.sil.org/cms/scripts/page.php?cat_id=UnicodePUA"&gt;SIL&lt;/a&gt; document their use of it (mostly so others can understand how they've assigned ideographs and symbols).&lt;/p&gt;
</content></entry><entry><title>Category Theory.</title><link href="https://generic.cx/essays/category-theory/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:2c07ac50-a53d-3301-b74b-985d992d025c</id><content type="html">&lt;p&gt;I find myself getting more and more curmudgeonly over time. I bought a receiver for my tv, but i found configuring it to be arduous. I use new software and wonder if i'm missing something by only &lt;em&gt;skimming&lt;/em&gt; the menus rather than devoting myself to them.&lt;/p&gt;
&lt;p&gt;As an unfashionably nerdy child, i consumed product manuals and software specs with vigor. I was a master VCR programmer. I was an early tech support weenie. The common retort to this is "Young people have energy."&lt;/p&gt;
&lt;p&gt;But maybe it wasn't a matter of energy (you're free to disagree) but an easy comfort to relish in Rules; to understand and play out the system someone else had designed for me to live in (absolving myself the responsibility of choice).&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;There's an interesting article from Harper's Magazine&lt;sup class="footnote-ref" id="fnref-tensepresent"&gt;&lt;a href="#fn-tensepresent"&gt;1&lt;/a&gt;&lt;/sup&gt; about the concept of a "Democratic Spirit" via the (now prosaic) notion that language is shaped and observed by two camps, Descriptivists and Prescriptivists&lt;sup class="footnote-ref" id="fnref-descriptivists"&gt;&lt;a href="#fn-descriptivists"&gt;2&lt;/a&gt;&lt;/sup&gt;. You can look anywhere and find these sorts of folks: the kinds that are fascinated with following rules for a system (meet anyone with a vague interest in DnD) and those that are like cultural sponges, remixing style and systems to suit their needs. And it's not a binary-choice either, it's more like a gradient between the two.&lt;/p&gt;
&lt;p&gt;Design is an opinionated field, informed by concepts like "systems" and "visual languages" and &lt;a href="http://www.aber.ac.uk/media/Documents/S4B/semiotic.html"&gt;semiotics&lt;/a&gt;. The AIGA &lt;a href="http://www.aiga.org/guide-whatisgraphicdesign/"&gt;describes design&lt;/a&gt; as a "creative process that combines art and technology to communicate ideas." and design crits are littered with gems like "it just doesn't speak to me" or "what is this trying to &lt;em&gt;say&lt;/em&gt;?" That design is a communicative medium should surprise nobody, least of all designers. It should also be no surprise that there are two camps of designers: the kind that treat design&lt;/p&gt;
&lt;p&gt;Folks, myself included, like to make the distinction between "design aesthetic" and "design function" as if they're different things, but from a certain standpoint they're both a &lt;em&gt;kind of vernacular&lt;/em&gt;; each is a style and method for making a point. Sometimes 'how you say it' &lt;em&gt;is&lt;/em&gt; as important as 'what you say.'&lt;/p&gt;
&lt;p&gt;&lt;img src="printer.jpg" alt="Turn off, then on"&gt;&lt;/p&gt;
&lt;p&gt;Let's go back to tv receivers &amp;amp; software. When it comes to things like software and hardware, there's this Jakob Nielsen/Don Norman-esque belief that objects should be obvious, clear, reuse metaphors, etc. These are not bad goals at all. Interaction and interface design are important &lt;em&gt;because&lt;/em&gt; they aim to create intelligible and reusable mental models for what are often complex ideas.&lt;/p&gt;
&lt;p&gt;Over time, these ideas coalesce into a semi-standardized design language. At their outset, though, apps and devices try to find a way to express their newfound utility and differences through icons, metaphors and novel interface elements&lt;sup class="footnote-ref" id="fnref-winampvsitunes"&gt;&lt;a href="#fn-winampvsitunes"&gt;3&lt;/a&gt;&lt;/sup&gt;. John maeda once noted&lt;sup class="footnote-ref" id="fnref-maedavolvo"&gt;&lt;a href="#fn-maedavolvo"&gt;4&lt;/a&gt;&lt;/sup&gt; that his digital camera's manual was twice as large as the one for his Volvo sedan. Digital camera technology still, to some degree, actively improves "the basics" enough that we still need to deal with these changes. In contrast, cars largely refine existing features nowadays rather than innovating fundamental things like the accelerator. If your &lt;a href="http://www.apple.com/ios/carplay/"&gt;car gets an upgrade&lt;/a&gt; these days, you don't need to relearn how to use the brakes or accelerator.&lt;/p&gt;
&lt;p&gt;The goal of converging standards in ui design is to democratize the components of interfaces. In the development of &lt;a href="http://www.indexhibit.org"&gt;indexhibit&lt;/a&gt;, daniel eatock said that he wanted to create 'archetypal' &lt;a href="http://www.indexhibit.org/"&gt;online portfolios&lt;/a&gt; which democratically favored &lt;em&gt;content&lt;/em&gt; rather than the design of the portfolio. Back when voicemail &lt;em&gt;wasn't&lt;/em&gt; tied to a touchscreen ui, you could mostly count on the 7 key to mean 'delete'. When navigating indexhibit, you know what the portfolio contents are. When using a doorknob, you usually know how it's going to behave.&lt;/p&gt;
&lt;p&gt;Standards exist to simplify common things, which is goood, nobody disputes that.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;There's a term for a certain flavor of exaggerated gramattical etiquette called "hypercorrection"&lt;sup class="footnote-ref" id="fnref-gmau"&gt;&lt;a href="#fn-gmau"&gt;5&lt;/a&gt;&lt;/sup&gt; where people, afraid of sounding 'stupid' always use "I" instead of "me", despite the word "me" being 'more appropriate' in the context as a grammatical object. Languages and their grammars have lots of rules, mostly so you can make a point as unambiguously as possible to as many people as possible.&lt;/p&gt;
&lt;p&gt;In design, as in a grammar, these rules are often byzantine. There are accepted &lt;a href="http://instagram.com/p/YyJNA6Jb8F/"&gt;numerical guidelines&lt;/a&gt; for justifying text in inDesign on the numeric end and there are flavor-profiles for &lt;a href="http://www.typography.com/email/2010_03/index_tw.htm"&gt;mixing and matching type&lt;/a&gt; on the abstract end. There are rules as to what you can do and &lt;a href="http://www.thinkingwithtype.com/contents/extras/#Type_Crimes"&gt;what you shouldn't do&lt;/a&gt; and what has, historically, looked 'good,' and what "&lt;a href="http://alistapart.com/article/more-meaningful-typography"&gt;Golden Ratios&lt;/a&gt;" and &lt;a href="http://lamb.cc/typograph/"&gt;musical scales&lt;/a&gt; have been used since time immemorable. These rules are passed down in the best Prescriptive manner by instructors and classmates.&lt;/p&gt;
&lt;p&gt;Graphic Design is largely prescriptive. It thrives on &lt;em&gt;correct&lt;/em&gt; things and as a result it has its fair share of hypercorrection. Why do so many designs look similar? Why do hierarchies often look the same between websites? Why are standardized grids so common? Part is pragmatism but the other part is often falling back on standards. But when you start falling back on prescriptive rules and pre-fab systems to Design something you are consciously making a choice to make &lt;em&gt;that aspect&lt;/em&gt; of your design &lt;a href="http://gmunch.home.pipeline.com/typo-L/misc/ward.htm"&gt;invisible&lt;/a&gt;. This is roughly the same argument for using &lt;a href="http://en.wikipedia.org/wiki/Standard_written_English"&gt;Standard Written English&lt;/a&gt; to make your point.&lt;/p&gt;
&lt;p&gt;My impression has been that Fine Artists think this manner of rule-using and setting is laughable since Fine Art thrives on people challenging norms and conventions. As in Fine Art, Design rewards 'successful' challenges to convention. The &lt;a href="http://trendlist.org"&gt;successes are duplicated&lt;/a&gt;, they evolve, they change, they take on a life of their own. They are memes in the highest-brow sense. The others are simply 'wrong' until a subculture fetishizes them and champions them on their own merits&lt;sup class="footnote-ref" id="fnref-subculture"&gt;&lt;a href="#fn-subculture"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;For an example of this, you can look at David Carson, the poster-child for designers wanting to tout that "design doesn't &lt;em&gt;have&lt;/em&gt; to play by the rules." And, since he existed at a time when grunge was very much culturally &lt;em&gt;de stijl&lt;/em&gt;, you can witness the accompanying success of his cohort of zuzana and neville and paula and stefan and all the gang who just wanted to eff things up real nice&lt;sup class="footnote-ref" id="fnref-grungecoda"&gt;&lt;a href="#fn-grungecoda"&gt;7&lt;/a&gt;&lt;/sup&gt;. The exact same people who massimo was &lt;em&gt;very&lt;/em&gt; publicly pillorying and, if you've watched Helvetica, you can see him slighting with gusto.&lt;/p&gt;
&lt;p&gt;To say that Design, as a field, is conflicted about its place in the Art world is something of an understatement&lt;sup class="footnote-ref" id="fnref-whatisdesign"&gt;&lt;a href="#fn-whatisdesign"&gt;8&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="klute.jpg" alt="2012 headline - by gareth hague"&gt;&lt;/p&gt;
&lt;p&gt;The comedy of iOS7 was chiefly the '&lt;a href="http://designerscomplaining.tumblr.com/"&gt;designer reaction&lt;/a&gt;,' mostly for their bare-fisted prescriptivism (at quantities unseen since the reveal of wolff olins' london 2012 logo&lt;sup class="footnote-ref" id="fnref-london2012"&gt;&lt;a href="#fn-london2012"&gt;9&lt;/a&gt;&lt;/sup&gt;). iOS7 was all &lt;em&gt;wrong&lt;/em&gt; they said. Helvetica as system font was &lt;a href="https://twitter.com/espiekermann/status/344394584798949377"&gt;wrong&lt;/a&gt;. The grid was "OK", but its &lt;a href="http://mrgan.tumblr.com/post/53308781143/wrong"&gt;application was wrong&lt;/a&gt;. Even the gracious crits toed the party line vis-a-vis type, color, icon design, etc&lt;sup class="footnote-ref" id="fnref-chimero"&gt;&lt;a href="#fn-chimero"&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Then, just as soon as the crits began, the consensus turned again to the fact that iOS7 &lt;a href="http://www.macworld.com/article/2042556/ios-7-proves-once-again-that-change-is-the-way-apple-does-business.html"&gt;wasn't complete yet&lt;/a&gt;, and finally that we were &lt;a href="https://medium.com/design-ux/3c4f01573adc"&gt;judging iOS7 prematurely&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But the point is not that iOS's design was divisive (uninteresting) but that the &lt;em&gt;kind of reaction&lt;/em&gt; is highly divisive. You see arguments that fall back on prescribed rubric, on taste, on 'correctness' and so forth. The opposing viewpoints have mostly centered around "give it time" and "it's new, let's see what happens."&lt;sup class="footnote-ref" id="fnref-gigaom"&gt;&lt;a href="#fn-gigaom"&gt;11&lt;/a&gt;&lt;/sup&gt; Certainly there's probably some hedging going on, but a rationale for reserving judgment that centers on "i don't understand what this means yet" implies a descriptivist's assessment that iOS' success is largely dependent on what everyone else does in relation to it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Actually, let me return to london 2012 for a second:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The typeface needed to be part of this world, be linear and graphic, not typographic, not have the formal typographic subtleties of the typefaces its been compared to, or their reference points of blackletter or cartoon.&lt;/p&gt;
&lt;p&gt;Such comparisons have missed the point and have not considered idea of the identity or the type in context, or context according to its original brief. Or they have just not liked the identity per se, so have expressed a general dislike of all aspects of the identity, not considering the inadequacies or otherwise of the type as a supporting element to the logo.&lt;sup class="footnote-ref" id="fnref-alias"&gt;&lt;a href="#fn-alias"&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The overarching critique of the games' identity was formal&lt;sup class="footnote-ref" id="fnref-formallingo"&gt;&lt;a href="#fn-formallingo"&gt;13&lt;/a&gt;&lt;/sup&gt;, but for a &lt;em&gt;system&lt;/em&gt;, this is something of a red herring. The above plea is not asking that folks accept the aesthetic choices that produced the logo or font, rather it's begging that people evaluate the type in the &lt;em&gt;context&lt;/em&gt; of the logo and the universe it spawned.&lt;/p&gt;
&lt;p&gt;And if we tolerate, as a profession, the notion that design is a tool for communication, it is unfortunate that a vocal subset of influential designers expect design to communicate instantly intelligible ideas. Certainly, much of design services simple ideas, but any communication platform will at some stage try to express or present a legitimately new idea. &lt;a href="http://paulgraham.com/avg.html"&gt;What then&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;This isn't an excuse for needless complexity in design. Nobody likes that, but most interaction designs tend to be compromises between convention and the peculiarities of whatever new mental model is being presented&lt;sup class="footnote-ref" id="fnref-mentalmodel"&gt;&lt;a href="#fn-mentalmodel"&gt;14&lt;/a&gt;&lt;/sup&gt;. The canonical example is File Sync (which is Hard, btw). As of 2005, the best solution was something like &lt;a href="http://www.cis.upenn.edu/~bcpierce/unison/"&gt;Unison&lt;/a&gt;. And then dropbox showed up and instead of letting people deal with a complex config file, just gave people a magic folder with blue and green icons to let you know if your files had finished syncing. Sync is still hard, but dropbox acknowledged that most people just don't care as long as you create conflicted versions and give them a few gigs of free storage space.&lt;/p&gt;
&lt;p&gt;And some conceptual simplifications are helpful but others are &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky abstractions&lt;/a&gt; and as a result, when we begin to critique anything new, we look for the familiar and try to make sense of elements we haven't seen before.&lt;/p&gt;
&lt;blockquote&gt;&lt;ul&gt;
&lt;li&gt;What you get right, nobody mentions it.&lt;/li&gt;
&lt;li&gt;What you get wrong, people bitch about.&lt;/li&gt;
&lt;li&gt;What is difficult to understand you have to explain to people over and over again&lt;sup class="footnote-ref" id="fnref-elixir"&gt;&lt;a href="#fn-elixir"&gt;15&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;In mathematics, Category Theory&lt;sup class="footnote-ref" id="fnref-mitcattheory"&gt;&lt;a href="#fn-mitcattheory"&gt;16&lt;/a&gt;&lt;/sup&gt; is a way of Strictly/Formally defining 'things' and mappings or relations between them and how it is possible to combine those relations/mappings. The allure and the beatuy of category theory is that the rules for defining a category are quite straightforward and they allow you to define a rational system &lt;em&gt;from the outset.&lt;/em&gt; There can be no ambiguity because in order to get to the point where you're talking about the interactions between mappings and groups, you must necessarily agree with the initial context.&lt;/p&gt;
&lt;p&gt;What's appealing is that this system provides you a set of guidelines for understanding how this world works and then lets you figure out how you go about mixing and matching the components in a way that both creates some newish thing also within the bounds of the rules you all agreed to.&lt;/p&gt;
&lt;p&gt;The problem with design is that it is &lt;em&gt;not&lt;/em&gt; in any way like category theory. The rules are always changing, the way the world deals with design is unbounded and ill-defined. Critics champion random things, rules are broken, people like Wrong Things for reasons that defy logic. And further&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;But part of this whole vcr love as a child but receiver disappointment as an adult is that I have developed more complex ideas about how receivers &lt;em&gt;could&lt;/em&gt; work. A VCR is not a complicated device and, as a six year old, i don't think i had very complex demands of them. But as time goes on, there are more things i expect devices or designs or anything to "get right."&lt;/p&gt;
&lt;p&gt;And naturally, in this, prescriptivism is a comfortable stance to take. But you define your own prescriptivism as well, you set down rules for how the world or design or logos should work and those define the edges of what others get right or wrong. I'm not treading new ground here by saying this, we all know this is how taste and subjective opinions play out.&lt;/p&gt;
&lt;p&gt;And naturally it's difficult to let go of your own rule-systems: you've invested lots of energy in creating them and there's nothing which objectively proves anyone has it figured out any more than you have.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;&lt;li id="fn-tensepresent"&gt;&lt;p&gt;Here cementing my yuppie bona fides, &lt;a href="http://instruct.westvalley.edu/lafave/DFW_present_tense.html"&gt;Tense Present&lt;/a&gt;, David Foster Wallace. Haper's Magazine April 2001. I am underselling how great this essay is: it starts by showing the context of the prescriptivist / descriptivist debate and afterwards challenges you to determine which is Right. Fine, but &lt;em&gt;spoilers&lt;/em&gt;, the essay then wallops you with a review for a reference guide that does the same thing Wallace does in prose.&lt;a href="#fnref-tensepresent" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-descriptivists"&gt;&lt;p&gt;For the purposes of the article, Descriptivists watch language play itself out and document it as well as they can. Meanwhile, Prescriptivists (as their name implies) &lt;em&gt;prescribe&lt;/em&gt; historically correct and proper usage of language.&lt;a href="#fnref-descriptivists" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-winampvsitunes"&gt;&lt;p&gt;Remember the difference between Winamp and iTunes back in the day (if you remember that far back)?. Winamp was particularly hands-off about your music collection and unskinned, it &lt;em&gt;looked&lt;/em&gt; like a piece of component audio hardware. Its main ui element was the playlist editor which gobbled up m3us or whole directory structures. By contrast, iTunes was very much designed to not only play, but &lt;em&gt;collect&lt;/em&gt; your music. Playlists were available, but they were secondary to its Library view.&lt;a href="#fnref-winampvsitunes" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-maedavolvo"&gt;&lt;p&gt;This musing appears in his &lt;a href="http://www.ted.com/talks/john_maeda_on_the_simple_life.html"&gt;ted talk&lt;/a&gt; and also featured prominently in his run-up to his Laws of Simplicity book. And with good reason, it's an astute observation that technological innovation has the capacity to make once-obvious things complicated. Having a tiny screen on the back of your camera is valuable for providing you feedback, but it has the effect of encouraging you to perform your feedback loop as you're taking photos rather than editing your photos much later.&lt;a href="#fnref-maedavolvo" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-gmau"&gt;&lt;p&gt;garner's modern american usage, Hypercorrection. The problem is not limited to I/me, but also who/whom, and so forth.&lt;a href="#fnref-gmau" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-subculture"&gt;&lt;p&gt;see for example: pixel art / ascii art / scanner glitches over rococo tapestries / etc&lt;a href="#fnref-subculture" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-grungecoda"&gt;&lt;p&gt;or Weingart if you want to go further back.&lt;a href="#fnref-grungecoda" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-whatisdesign"&gt;&lt;p&gt;this debate goes pretty much all the way back to the 1920s or so when typographers debated whether or not the title Graphic Designer was an affront towards honest typographers. Is Design layout? Is it Art? Is it Typography? Is it Illustration? These questions (and more) are fodder for most MFA seminars.&lt;/p&gt;
&lt;p&gt;Take a second and ask yourself if it's problematic if design aspires to art. For art to be 'successful' do you have to 'get it' the same way you have to 'get' a design? Certainly, design has some leeway to express itself creatively, but is it the same leeway we allow to something like, say, R.Mutt's &lt;a href="http://en.wikipedia.org/wiki/Fountain_(Duchamp"&gt;Fountain&lt;/a&gt;)?&lt;a href="#fnref-whatisdesign" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-london2012"&gt;&lt;p&gt;it's, at this point, not totally beyond the pale to suggest that the logocalypse that was foretold by vocal designers was completely overblown. Why this was an issue &lt;em&gt;at all&lt;/em&gt; is ridiculous. Did the logo violate the sanctity of the games any more than the 2000 sydney games or the 2008 beijing games (both of which might as well have been cut from the same cloth)?&lt;a href="#fnref-london2012" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-chimero"&gt;&lt;p&gt;This is perhaps an unkind assessment of Frank Chimero's &lt;a href="http://frankchimero.com/blog/2013/06/generosity-of-perspective/"&gt;honest realization&lt;/a&gt; that time smooths over many design wounds but even as rational as his argument was, he couldn't help but get a dig in.&lt;a href="#fnref-chimero" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-gigaom"&gt;&lt;p&gt;om malik's ios7 &lt;a href="http://gigaom.com/2013/06/10/ios-7-love-it-hate-it-either-way-designers-are-talking-about-it/"&gt;designer roundup&lt;/a&gt; is disturbingly informative.&lt;a href="#fnref-gigaom" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-alias"&gt;&lt;p&gt;Naturally, Gareth Hague, the designer of Headline 2012, &lt;a href="http://alias.dj/blog/a-spiky-typeface-for-a-spiky-logo/"&gt;weighed in&lt;/a&gt; on the vocal reactions to the 2012 logo. Note, even in this quote and in the rest of his reaction the use of the word 'context.'&lt;a href="#fnref-alias" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-formallingo"&gt;&lt;p&gt;here i use the word &lt;em&gt;formal&lt;/em&gt; as "Relating to the form or structure of something" rather than its more common use as "ceremonial" or "official"&lt;a href="#fnref-formallingo" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-mentalmodel"&gt;&lt;p&gt;This was made apparent to me the first time i used Lightroom and the first time i attempted to use final cut pro. I had zero frame of reference for film and so the whole thing seemed to me needlessly complex. In contrast, when i used lightroom, i immediately understood what they were trying to do, possibly because i had spent plenty of time thinking in "the photo space."&lt;/p&gt;
&lt;p&gt;I actually don't know if final cut is considered a Good application, but i do know that i lack a mental frame of reference from which to judge it which makes me think that&lt;a href="#fnref-mentalmodel" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-elixir"&gt;&lt;p&gt;joe armstrong's Three Laws of Programming Languages re: &lt;a href="http://joearms.github.io/2013/05/31/a-week-with-elixir.html"&gt;elixir&lt;/a&gt;. It's worth mentioning if it's not obvious that this gem comes from the designer of a Programming Language, a person intimately aware of the limitations of trying to express complex ideas in a strict grammar.&lt;a href="#fnref-elixir" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-mitcattheory"&gt;&lt;p&gt;another good description with more historical context appears in Spivak's &lt;a href="http://category-theory.mitpress.mit.edu/chapter001.html#lev_1-1"&gt;category theory for natural sciences&lt;/a&gt;. Here i resist the urge to use the word &lt;em&gt;functor&lt;/em&gt;.&lt;a href="#fnref-mitcattheory" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>keys</title><link href="https://generic.cx/essays/keys/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:8f1d175a-9e7c-35ab-bcc6-5f4c27fc3035</id><content type="html">&lt;h2&gt;what's going on&lt;/h2&gt;
&lt;p&gt;In what is likely to be the ultimate yak, i recently discovered that the &lt;a href="https://vsco.github.io/keys/"&gt;vsco keys&lt;/a&gt; app was open sourced. I don't think i was ever the app's target demographic, but it's an interesting tool that assigns lightroom actions and presets to keyboard keys so you can speed up your photo editing workflow. It had a webapp that generated keymap configs that you could import back into the main client.&lt;/p&gt;
&lt;p&gt;So what? it got open sourced. it was a bigger support drain than it was a profit center, so it's understandable, but when an app goes open, you get to learn all sorts of things about the decisions people made when developing an app. In my case, i have a clear plan: i want to fill the gaps that are currently missing for keys (primarily because it is a fun design challenge) but the process to get there is totally an exercise in archaeology and trying to understand an application i never got to use.&lt;/p&gt;
&lt;p&gt;So this is going to be a journal in something like five parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The config/savefile format&lt;/li&gt;
&lt;li&gt;The webapp that generates keymaps&lt;/li&gt;
&lt;li&gt;Designing around my dvorak life&lt;/li&gt;
&lt;li&gt;The os x application&lt;/li&gt;
&lt;li&gt;The lightroom plugin&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Almost every application that deals with keypresses also just assumes that you're using qwerty and not, say, dvorak or colmak. Few apps are good at dealing with this. Starcraft 2, for instance, can detect your keymap and will remap the 'grid' layout so that the labeled 1-4, q-r, a-f, z-v keys do what you expect them to (but they conveniently label them in the ui with their dvorak equivalents). On mac-os, dota2 for instance, gets confused and will still listen for &lt;code&gt;qwer&lt;/code&gt; instead of &lt;code&gt;',.p&lt;/code&gt;, which requires you to remap these keys (or always use qwerty at the expense of typing replies slowly).&lt;/p&gt;
&lt;p&gt;Because the whole purpose of vsco keys (presumably) is to give spatial regions of your keyboard functional uses, having to dance around qwerty-&amp;gt;dvorak remapping needs to be part of the work.&lt;/p&gt;
&lt;p&gt;The project itself is fairly contained, too: figuring out the filetype should be fairly straightforward. Generating the config files will probably be the most time-intensive aspect. With any luck, i won't have to deal with the lightroom plugin (whose source, btw, was not provided). That leaves the osx host application which, as time goes on, strikes me as the most fragile. Thankfully, its source is available and needs to be updated to integrate with whatever webapp i end up making.&lt;/p&gt;
&lt;h2&gt;the savefiles&lt;/h2&gt;
&lt;p&gt;The project provides some pretty minimal guidance for how to create your own configuration: they have a &lt;a href="https://github.com/vsco/keys/blob/master/Layout/sampleLayout.keysjson"&gt;&lt;code&gt;sampleLayout.keysjson&lt;/code&gt; file&lt;/a&gt; and two ancillary files, one a json array with &lt;a href="https://github.com/vsco/keys/blob/master/Layout/keymap.json"&gt;keycodes and key labels&lt;/a&gt; and another with what appears to be the name of &lt;a href="https://github.com/vsco/keys/blob/master/Layout/toolkitlistlr4.json"&gt;every single lightroom command annotated&lt;/a&gt; with ranges and pretty-printed label. Your goal as end-user is to take the two supplementary files and generate new config files with them, by hand, &lt;a href="http://5by5.tv/b2w"&gt;like an animal&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;defaults&lt;/h3&gt;
&lt;p&gt;When you manage to get the app installed, you notice the following preinstalled presets:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/hTyC.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;These seem like they should be good fodder for figuring out how you might develop a full-featured config file, so i started searching for them and found... nothing? eventually, after searching for a while i decided to search for the word &lt;code&gt;standard&lt;/code&gt; and came across something that looked promising initially&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/X4Gb.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;but on closer inspection...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;marcos@Dalarna in ~
○ hexdump ~/Downloads/VSCOSimple.vkeys
0000000 ae d2 0b e2 d3 66 00 82 52 c9 54 34 8c 11 05 78
0000010 67 1d 60 e6 5e e2 44 fa bf 7c df aa cb ae 8a 3d
0000020 12 c8 96 32 31 20 cb f0 16 28 3c a9 ef e3 0c 5c
0000030 09 ff dc ad 67 01 59 41 9b f7 8d c0 23 cf 31 d3
....
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and from here you can see&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;marcos@Dalarna in ~
○ file ~/Downloads/VSCOSimple.vkeys
/Users/marcos/Downloads/VSCOSimple.vkeys: data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ok, so it's not, sadly, gzipped json, so my next guess is that maybe i'm just looking at a marshalled NSDictionary. but before i decided to investigate that route, i noticed that the app needed to support two separate extensions: &lt;code&gt;vkeys&lt;/code&gt; and &lt;code&gt;keysjson&lt;/code&gt; and thinking ever farther back, the apps are cross-platform (win/mac), so whatever the format is, it's probably not just a raw NSData dump, it's probably something at least easy to port between platforms.&lt;/p&gt;
&lt;p&gt;the next trick was searching for &lt;code&gt;vkeys&lt;/code&gt; and seeing what came up and, lo and behold&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/EtVo.png" alt="a list of #define consts"&gt;&lt;/p&gt;
&lt;p&gt;But... and here's the funny one, you'll notice the &lt;code&gt;KEYFILE_AES_KEY&lt;/code&gt; const just hanging out there, but my initial guess was that it was used to verify licenses or valid keyfiles. instead, i find the &lt;code&gt;- (void) importKeyFile&lt;/code&gt; method that seems to call &lt;code&gt;- (NSMutableDictionary) decryptAndParseKeyfile&lt;/code&gt; that &lt;a href="https://github.com/vsco/keys/blob/ae007d227536814ba380af73ed5446fb9e37daad/VSCOKeys/VSCOKeys/KeyControl.m#L693"&gt;calls&lt;/a&gt;, effectively, &lt;code&gt;[[NSData dataWithContentsOfFile:filePath] decrypt]&lt;/code&gt;. Also, this may not be obvious, but &lt;code&gt;- (NSData *) decrypt&lt;/code&gt; is &lt;em&gt;not standard&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The code for it looks like this, though&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NSUInteger dataLength = [self length];
uint8_t *unencryptedData = malloc(dataLength + kCCKeySizeAES128);
size_t unencryptedLength;

CCCryptorStatus status = CCCrypt(
  kCCDecrypt,
  kCCAlgorithmAES128,
  kCCOptionPKCS7Padding,
  KEYFILE_AES_KEY,
  kCCKeySizeAES128,
  NULL,
  [self bytes],
  dataLength,
  unencryptedData,
  dataLength,
  &amp;amp;unencryptedLength);

return [NSData dataWithBytes:unencryptedData
                      length:unencryptedLength];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and if you look close, you realize that &lt;code&gt;KEYFILE_AES_KEY&lt;/code&gt; is showing what its purpose actually is.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;An aside here: as a kid i always though binary file formats were magic until i realized (much later) that they were often just often just spewed out representations of lists and dictionaries. When i learned that some of them were simply marshalled datatypes i felt like i had just pulled back a curtain.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;anyhow, a quick trip to google reveals that this CommonCrypto incantation is a stock AES 128&lt;sup class="footnote-ref" id="fnref-aes128"&gt;&lt;a href="#fn-aes128"&gt;1&lt;/a&gt;&lt;/sup&gt; incantation, which means that even though you could could reuse that code to make a cli application&lt;sup class="footnote-ref" id="fnref-cli_disclosure"&gt;&lt;a href="#fn-cli_disclosure"&gt;2&lt;/a&gt;&lt;/sup&gt;, you could also (probably) just feed a &lt;code&gt;vkeys&lt;/code&gt; file to openssl and decrypt it there successfully.&lt;/p&gt;
&lt;p&gt;this makes sense, too, if you stop and think about the app being cross-platform: if the key files are generated via webapp, that means that the webapp has to be able to output some format that can easily be parsed by c# in windows and objective c on the mac, so the webapp is unlikely to be spewing forth the result of &lt;code&gt;[internalRepresentation writeToUrl: foo atomically: YES]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;An aside though: why encrypt the &lt;code&gt;vkeys&lt;/code&gt; files? I can sorta understand the copy-protection angle, but from my perspective, being able to view the exported data doesn't help you much (especially given how user-hostile json is for most people, even devs, to edit.)&lt;/p&gt;
&lt;p&gt;The other thing to note is that if the app is going to be backwards compatible for older users, when they open source it, they need to disclose that key in order to retain compatibility for users with legitimately generated keymaps. I think they could actually have gotten pretty good mileage out of gzip in this case with a changed the extension assuming that their application would always intercept the file when opening.&lt;/p&gt;
&lt;p&gt;Anyhow, so we have these keyfiles, how do we decrypt them? A cursory glance on the internet reveals that the openssl command to decrypt files looks roughly like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openssl enc &amp;lt;cypher&amp;gt; -d -in VSCOSimple.vkeys \
-out VSCOSimple.keysjson -K &amp;lt;encryption key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;looking back at the CommonCrypto incantation, you see that it's AES 128 (and peeking at the commoncrypto docs, you find that the default is AES 128 CBC and that the command to decrypt it specifices a &lt;code&gt;NULL&lt;/code&gt; initialization vector (&lt;code&gt;iv&lt;/code&gt;).) This is good to know, because the options for cyphers are&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Cipher commands (see the `enc' command for more details)
aes-128-cbc    aes-128-ecb    aes-192-cbc    aes-192-ecb    aes-256-cbc
aes-256-ecb    base64         bf             bf-cbc         bf-cfb
bf-ecb         bf-ofb         cast           cast-cbc       cast5-cbc
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we can update the command to&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openssl enc -aes-128-cbc -d -in VSCOSimple.vkeys \
-out VSCOSimple.keysjson -K &amp;lt;encryption key&amp;gt; -iv 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you investigate the arguments to &lt;code&gt;openssl enc&lt;/code&gt; you find&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-K/-iv         key/iv in hex is the next argument
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which means that you &lt;strong&gt;need&lt;/strong&gt; to convert the &lt;em&gt;string&lt;/em&gt; key value in the app source code to a hex value. You can do this in &lt;em&gt;ye olde python&lt;/em&gt; with this command (it prints the hex of each character in the original key)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -c "print ''.join(['%x' % ord(x) for x in '4Nfurb94B6iW64QD'])"
344e6675726239344236695736345144
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;armed with this, you can now fix the incantation&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openssl enc -aes-128-cbc -d -in VSCOSimple.vkeys \
-out VSCOSimple.keysjson \
-K 344e6675726239344236695736345144 -iv 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and easy-peasy-lemon-squeezy, you get&lt;/p&gt;
&lt;p&gt;&lt;img src="http://dl.dropboxusercontent.com/u/406291/Screenshots/5h52.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;With that, i proceeded to &lt;a href="https://github.com/nsfmc/keys/commit/a63e9bbb4fa35ac74777dfc83cc1b52a72ea618b"&gt;upload them&lt;/a&gt; to &lt;a href="https://github.com/nsfmc/keys"&gt;my fork&lt;/a&gt; of the keys project.&lt;/p&gt;
&lt;p&gt;That's all for now, but i'll keep updating this when i have time to hack on the project.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;&lt;li id="fn-aes128"&gt;&lt;p&gt;which kind of aes, we'll discuss later&lt;a href="#fnref-aes128" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-cli_disclosure"&gt;&lt;p&gt;which, if we're being honest, this is what i did initially just to peek at a more complete file, but i think the openssl approach is more interesting.&lt;a href="#fnref-cli_disclosure" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>what you don't know</title><link href="https://generic.cx/essays/norms/" rel="alternate"></link><updated>2024-05-11T15:24:32Z</updated><author><name>marcos</name></author><id>urn:uuid:51ca6e67-c778-3eb2-bc18-83e7ec2d1915</id><content type="html">&lt;p&gt;In may of this year, shortly before giving an &lt;a href="http://www.ted.com/talks/zeynep_tufekci_we_can_t_control_what_our_intelligent_machines_are_learning"&gt;acclaimed ted talk&lt;/a&gt; elaborating on many of these points, &lt;a href="http://mobile.nytimes.com/2016/05/19/opinion/the-real-bias-built-in-at-facebook.html"&gt;zeynep tufekci&lt;/a&gt; wrote the following in the new york times.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If a bridge sways and falls, we can diagnose that as a failure, fault the engineering, and try to do better next time. If Google shows you these 11 results instead of those 11, or if a hiring algorithm puts this person’s résumé at the top of a file and not that one, who is to definitively say what is correct, and what is wrong? Without laws of nature to anchor them, algorithms used in such subjective decision making can never be truly neutral, objective or scientific.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- more --&gt;

&lt;blockquote&gt;&lt;p&gt;Programmers do not, and often cannot, predict what their complex programs will do. Google’s Internet services are billions of lines of code. Once these algorithms with an enormous number of moving parts are set loose, they then interact with the world, and learn and react. The consequences aren’t easily predictable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First, i want to break down two issues here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One is the unintended consequences of designing an algorithm.&lt;/li&gt;
&lt;li&gt;The Second, are the unexpected consequences of performing a complex task atop a set of opaque abstractions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The two seem similar, they're definitely related, but they &lt;em&gt;are&lt;/em&gt; different. I'm going to talk about the second because it's more insidious than the first. It is easier to look at an algorithm and see outright bias, it is harder though when the behavior is a side-effect of the unexamined limitations made about the underlying system.&lt;/p&gt;
&lt;p&gt;I want to focus on zeynep's google example first because when i was in college, my &lt;a href="http://web.mit.edu/6.033/www/"&gt;system design class&lt;/a&gt; devoted a section of reading to looking at large scale systems. One of the fun ones at the time (2005) was google's mapreduce: designed both to make parallelism accessible to lowly mortals and  simultaneously show off their clever use of commodity hardware, as you can see in the &lt;a href="http://research.google.com/archive/mapreduce.html"&gt;mapreduce paper's abstract&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Programs written in this functional style are automatically parallelized and executed on a large cluster of &lt;strong&gt;commodity machines&lt;/strong&gt;. The run-time system takes care of the details of partitioning the input data, scheduling the program's execution across a set of machines, &lt;strong&gt;handling machine failures&lt;/strong&gt;, and managing the required inter-machine communication. This allows programmers without any experience with parallel and distributed systems to easily utilize the resources of a large distributed system.&lt;/p&gt;
&lt;p&gt;Our implementation of MapReduce runs on a &lt;strong&gt;large cluster of commodity machines and is highly scalable&lt;/strong&gt;: a typical MapReduce computation processes many terabytes of data on thousands of machines. Programmers find the system easy to use: hundreds of MapReduce programs have been implemented and upwards of one thousand MapReduce jobs are executed on Google's clusters every day.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Emphasis mine&lt;sup class="footnote-ref" id="fnref-commoditysplain"&gt;&lt;a href="#fn-commoditysplain"&gt;1&lt;/a&gt;&lt;/sup&gt; because the whole crux is that mapreduce allows for the peasant ails of &lt;em&gt;commodity machines&lt;/em&gt; while doing the best it can to insulate you from catastrophic failure and returning &lt;em&gt;some&lt;/em&gt; usable results (and performantly!). Plus, you don't even really need to understand parallelism to make good use of it!&lt;/p&gt;
&lt;p&gt;There was a moment while we discussed the paper where we had this cute illustration, i'll do my best to explain it since it's basically a funnel: there was The Cloud™, it represented all possible data on the internet. A subsection of that cloud was the amount of the public-facing internet that google had indexed. A subsection of that cloud was the entirety of the &lt;em&gt;results google knew about&lt;/em&gt; for your given query. And finally, a subset of &lt;em&gt;that&lt;/em&gt; cloud represented the results of your query that had survived the search job* either due to timeouts/latency/commodity hardware/solar flares/etc&lt;sup class="footnote-ref" id="fnref-mapsplain"&gt;&lt;a href="#fn-mapsplain"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The point being that for almost all queries, there were an impossibly large number of possible results for the &lt;em&gt;specific thing&lt;/em&gt; you were looking for and (improbably) the &lt;em&gt;least&lt;/em&gt; novel thing was getting your search results within some window of time&lt;sup class="footnote-ref" id="fnref-capsplain"&gt;&lt;a href="#fn-capsplain"&gt;3&lt;/a&gt;&lt;/sup&gt;. In a &lt;code&gt;mapreduce&lt;/code&gt; world, the system 'deals' with the issue of a result not showing up by returning &lt;em&gt;whatever it has&lt;/em&gt; at the end of some timeout. This works because pretty much any internet user, has &lt;em&gt;absolutely no clue&lt;/em&gt; how many of the results &lt;em&gt;could&lt;/em&gt; be of any actual value (to say nothing of &lt;em&gt;how many results there were to begin with&lt;/em&gt;). So what if pages 4-6 of the Total Results don't show up? Do you even go that far? &lt;em&gt;Would you notice?&lt;/em&gt; (at the time, and still today, people tend to not look past the first page of results). Even if one or two links are missing, you still get most of the relevant data back (better than nothing).&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;slowly learning a few things about distributed systems &lt;a href="https://t.co/C2gRnHgau4"&gt;pic.twitter.com/C2gRnHgau4&lt;/a&gt;&lt;/p&gt;&amp;mdash; Julia Evans (@b0rk) &lt;a href="https://twitter.com/b0rk/status/793288477060263936"&gt;November 1, 2016&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;When the number of results for your query is in the tens of thousands, does it matter if 4500-4520 are missing (or even just slightly misordered)? You may scoff, but this is a legitimate question! If it matters, much like a tree in the woods, does it matter if you never look at the results (or are statistically unlikely to look at them). Does it matter if the results are correctly ordered the next time you reload the page?&lt;/p&gt;
&lt;p&gt;This is not to say that there isn't bias in google's search result algorithm (it thankfully is biased against some kinds of harmful content), but that there is a belief that &lt;em&gt;any result is better than no result.&lt;/em&gt; (for something not mission critical, you can argue that focusing on Availability is not at all unreasonable, at least that's my claim)&lt;/p&gt;
&lt;p&gt;Facebook's lee byron (in a &lt;a href="https://hashnode.com/post/architecture-how-would-you-go-about-building-an-activity-feed-like-facebook-cioe6ea7q017aru53phul68t1/answer/ciol0lbaa02q52s530vfqea0t"&gt;very interesting post about building a feed&lt;/a&gt;) makes the following observation about the benefits of &lt;a href="https://en.wikipedia.org/wiki/Lazy_loading"&gt;lazy-loading&lt;/a&gt; a dataset presented to somebody browsing a feed.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;How many followers will the most popular content sources have?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Twitter used to call this the "Bieber problem": when he tweeted, a fan-out operation occurred, placing this new tweet into the feeds of every one of his followers which was a massive job that locked up whole machines. In Twitter's earlier years, a couple quick @justinbieber tweets in a row could "fail whale" the whole service.&lt;/p&gt;
&lt;p&gt;The "Bieber problem" is an illustration of a critical decision of the architecture of a feed service: do you build a user's feed at write time or at read time? Twitter's architecture placed tweets into feeds as soon as they were written into the service. By contrast, Facebook's architecture waits for a user to read their feed before actually looking up the latest posts from each followed account.&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;Should the story at the very top of your activity feed be the very most latest thing that happened, or should it be the very most interesting thing that happened? This is more of a product decision than an architectural one, but it has serious implications on your feed architecture.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I would argue that he understates how much archituctural choices can &lt;em&gt;retcon&lt;/em&gt; their way into product decisions. If a tweet is deleted from my feed as i'm reading, can i really be all that angry? "Who's right" in that case aligns with the moral quandry of the probabalistically failing google mapreduce server: if @justinbieber deletes a tweet but you're not finished reading it, should you have been entitled to finish it?&lt;/p&gt;
&lt;p&gt;A similar bit of architectural-influenced thought recently surfaced in a post from tristan harris, &lt;a href="https://medium.com/@tristanharris/how-technology-hijacks-peoples-minds-from-a-magician-and-google-s-design-ethicist-56d62ef5edf3#.numxml2vr"&gt;a google design ethicist&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;For example, imagine you’re out with friends on a Tuesday night and want to keep the conversation going. You open Yelp to find nearby recommendations and see a list of bars. The group turns into a huddle of faces staring down at their phones comparing bars. They scrutinize the photos of each, comparing cocktail drinks. Is this menu still relevant to the original desire of the group?&lt;/p&gt;
&lt;p&gt;It’s not that bars aren’t a good choice, it’s that Yelp substituted the group’s original question (“where can we go to keep talking?”) with a different question (“what’s a bar with good photos of cocktails?”) all by shaping the menu.&lt;/p&gt;
&lt;p&gt;Moreover, &lt;strong&gt;the group falls for the illusion that Yelp’s menu represents a complete set of choices for where to go.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;which, emphasis &lt;em&gt;mine.&lt;/em&gt; and while i personally feel less morally pigeonholed by my phone (or yelp) (and i believe that tristan believes that people ought to be empowered by theirs) i really don't know that i am legitimately perturbed by yelp's incomplete view of the universe (to be clear, i &lt;em&gt;don't&lt;/em&gt; want yelp &lt;em&gt;asking me&lt;/em&gt; why i feel compelled to visit a bar right now (and self-improvement aside, i don't know that others would either)).&lt;/p&gt;
&lt;p&gt;I'm reminded of this &lt;a href="http://www.smithsonianmag.com/arts-culture/teller-reveals-his-secrets-100744801/?no-ist=&amp;amp;page=2"&gt;classic 'quote' from magician Teller&lt;/a&gt; about magic:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If you are given a choice, you believe you have acted freely. This is one of the darkest of all psychological secrets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and again from tristan&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;We don’t miss what we don’t see.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don't believe that any programmer (or ethicist) at google (and, from the way lee byron writes, those at facebook), would pretend that their algorithms behave 100% predictably at all times (as we nerds would say &lt;code&gt;deterministically&lt;/code&gt;) each time they're run. The behavior of a feed or a search result page is, by necessity, the hybrid product of an architectural performance optimization, the whims of the hardware on a given day, solar flares, and hosts of other reasons, many of which are product/business-oriented (or, ways for you to make the service profitable).&lt;/p&gt;
&lt;p&gt;Which, again, i want to return to that first excerpt from zeynep's piece:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If Google shows you these 11 results instead of those 11, or if a hiring algorithm puts this person’s résumé at the top of a file and not that one, who is to definitively say what is correct, and what is wrong? Without laws of nature to anchor them, algorithms used in such subjective decision making can never be truly neutral, objective or scientific.&lt;/p&gt;
&lt;p&gt;Programmers do not, and often cannot, predict what their complex programs will do.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While i go along with much that zeynep is argues for, trying to compare physical structures to the inner-workings of a database strikes me as completely out of whack. Similar to people who trust that "digital" provides some kind of archival panacea but don't realize that digital data (unless redundantly archived on &lt;a href="http://group47.com/what-is-dots/"&gt;resiliant physical media&lt;/a&gt;) is subject to corruption, bit flips and other sundry damage&lt;sup class="footnote-ref" id="fnref-dotcaveat"&gt;&lt;a href="#fn-dotcaveat"&gt;4&lt;/a&gt;&lt;/sup&gt;. Once your datastore spans multiple "availability zones"(continents) and needs to make interesting guarantees about atomicity, consistency and partition tolerance, i argue that you've gone way beyond the complexity of a bridge.&lt;/p&gt;
&lt;p&gt;&lt;img src="jUQA.png" alt="was this photo... backed up?"&gt;&lt;/p&gt;
&lt;p&gt;to claim that a system that gracefully tolerates hardware failure exhibits a bias &lt;em&gt;is 100% true&lt;/em&gt;, and there are other biasing factors at work in any of these systems (which, &lt;a href="https://www.propublica.org/article/machine-bias-risk-assessments-in-criminal-sentencing"&gt;see the depressing end of the spectrum&lt;/a&gt;). But it also gives the laws of nature an easy pass—what does it mean to have a law of nature anchor a search result? Do we choose a system that behaves predictably at the expense of convenience? Certainly the decision to "show the truth" vs "show a good approxmation" is an ethical one, but how big of one is it? &lt;strong&gt;When does data-center latency become ethically fraught?&lt;/strong&gt; What sorts of disclaimers do services need to furnish us with? What about our friends?&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jvns.ca/"&gt;Julia Evans&lt;/a&gt; has written a fair bit about distributed systems and what people mean when they talk abou things like &lt;a href="&amp;quot;consistency vs availability&amp;quot;"&gt;http://jvns.ca/blog/2016/10/21/consistency-vs-availability/&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;... I know what availability is! It means I don’t get paged because the system is down. Basically it means if you ask, you can always get an answer! I like that. What’s wrong with that?&lt;/p&gt;
&lt;p&gt;So! let’s say we have 100 computers in a “cluster”. I think “consistency” kinda means “if you query any computer in the cluster at the same time it will tell you the same thing”. It doesn’t really mean that (that is actually legit impossible because of the speed of light) but let’s go with it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There was a time when we were lucky enough to interact with services that could plausibly run without concern for CAP and our conception of data-morality was simpler: either the server was down, or it wasn't. A successful query represented the &lt;em&gt;only&lt;/em&gt; truth at single point in time. The massively redundant world we live in now is much &lt;em&gt;much&lt;/em&gt; more complex. But developers, designers, and project leads are often insulated from this sort of infrastructure and &lt;em&gt;what it means&lt;/em&gt; for data being returned from a query. If you lead or design a project, when was the last time you asked about whether a backing api request favored being "consistent" or "available" and what that meant for your product's consumers?&lt;/p&gt;
&lt;p&gt;When you ask yelp for a bar &lt;em&gt;and it suggests a park&lt;/em&gt;, what sort of moralistic puritanic editorializing is that? also, how do you know it's &lt;em&gt;not&lt;/em&gt;? when a hardware failure prevents you from seeing a retweet for a minute (but lets you see the rest of your feed), what &lt;em&gt;kind&lt;/em&gt; of bias is that? Why are we so offended (and scandalized!) when we're presented with the equivalent of a gap in memory (or best-available information)? Is it because we don't know the reason behind it? Is it because it exposes the nature of our relationships with these services? When you're shopping around for services do you factor in Consistency vs Availability in your decision?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Some other unstructured thoughts:&lt;/p&gt;
&lt;p&gt;My system design class also introduced another article about the &lt;a href="http://sunnyday.mit.edu/therac-25.html"&gt;Therac 25&lt;/a&gt; that left many in the class somewhat scarred. For many of us, the short-term takeaway was "thank god i don't have to write medical firmware," but you see the moral fallout of the therac &lt;a href="https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/"&gt;if you peek at any code or system&lt;/a&gt; for any appreciable length of time.&lt;/p&gt;
&lt;p&gt;I see this argument quite often that engineers have it &lt;em&gt;easy&lt;/em&gt; because physics and statics can be modeled in a predictable way. When bridges fall due to hardware failure or poor design (or freak accidents!) &lt;em&gt;people can die.&lt;/em&gt; When your CRUD application gracefully handles a backend failure and rolls back an optimistic ui update, i don't really know what to tell you. They're not on-par.&lt;/p&gt;
&lt;p&gt;I also think it's strange that the counterpoint to dumb ml-algorithms are suggestions which seem to defeat the purpose of the relevant platform (and often with a *very WASP-ey bent!): i want a popular bar (have you tried a park?). I want to see what my friends posted (are you being challenged enough?). I want some search results (who is john galt?).&lt;/p&gt;
&lt;p&gt;I'm not going to defend google or facebook, both of whom have had &lt;a href="https://twitter.com/nsfmc/status/792167307627728896"&gt;significant issues&lt;/a&gt; due to their scale and the tragic effects of systems applied to a heterogeneous world. but it goes without saying that it's problematic to treat their unintended consequences like a therac-25's (which, again to be clear, i am not giving them a pass, please &lt;a href="https://www.propublica.org/article/machine-bias-risk-assessments-in-criminal-sentencing"&gt;read this troubling article&lt;/a&gt; for a counterpoint to my claim) when they are much more like a highly opinionated video store employee that reads you (without asking) and then suggests a movie they're not really that into, but think &lt;em&gt;you'd&lt;/em&gt; probably like. Even when google and co. get it wrong, we aren't often talking about damage on par with the effects of shoddy bridge (or car) engineering (although again, that sentencing algorithm and minority-excluding ad dashboard is again the absurdly dark counterpoint).&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Facebook influences more people than any country, it has a moral responsibility to be factual and educational. Hasn&amp;#39;t risen to it. &lt;a href="https://t.co/pMdbLbxhqy"&gt;https://t.co/pMdbLbxhqy&lt;/a&gt;&lt;/p&gt;&amp;mdash; Stephan Ango (@kepano) &lt;a href="https://twitter.com/kepano/status/789291078788624384"&gt;October 21, 2016&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;When the stakes are legitimately low, do we need transparency in how google or facebook make decisions about our newsfeed? that's a very good question! should we have transparency as to why my server is upselling today's special appetizer? should the server tell me that today's french dip soup is the result of some roast beef's last day of usable shelf life? should i ask the record store employee what cultural basis grounds all the recommendations they give me? Should a sales person be required to tell me up-front they work on commission? Or do we regulate some of these things instead: what if in addition to a privacy policy, each website advertised its Consistency or Availability infrastructural choices?&lt;/p&gt;
&lt;p&gt;Here's a funny story: the other day i came down with a terrible head cold leaving me comically congested. So i walked to the pharmacy and began to buy a pack of sudafed. However, the Methamphetamine Menace of the early aughts and War on Drugs &lt;a href="https://en.wikipedia.org/wiki/Combat_Methamphetamine_Epidemic_Act_of_2005"&gt;require that I supply my driver's license&lt;/a&gt; when purchasing pseudoephedrine to ensure that i haven't been &lt;a href="https://web.archive.org/web/20070303172042/http://www.wqad.com/Global/story.asp?S=5477392&amp;amp;nav=menu132_5"&gt;purchasing untold quantities&lt;/a&gt; of pseudoephedrine for my purported walter white side-gig. But i say “began” because the &lt;a href="https://www.deadiversion.usdoj.gov/meth/q_a_cmea.htm#11"&gt;electronic logbook&lt;/a&gt; that particular safeway used was offline. Or maybe it wasn't the website, but the network of the safeway pharmacy. Or who knows. At a very tangible level, my sinuses were the victims of a law that, for liability reasons, was implemented by everyone &lt;em&gt;assuming&lt;/em&gt; a stable &lt;a href="https://en.wikipedia.org/wiki/Link_layer"&gt;link-layer&lt;/a&gt; and an Available web service. Laws are meant to be general-purpose but when they imply a hard dependency on things like a database, what sorts of laws govern its audits and security? Should safweay be accountable to the question of security of their logbooks? (the violations are steep and result in a federal misdemeanor). Given the risks, would you buy sudafed from a pharmacy or state that had a poorly implemented logbook? We talk about transactions being problematic because you could double-charge a consumer, but what about an unthrottled double-entry of a controlled pharmaceutical that triggers an immediate police response?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I'll close this out with a thought on google glass. I still believe that google glass' failure wasn't a foregone conclusion, but it &lt;em&gt;was&lt;/em&gt; poorly socialized. There were absolutely &lt;em&gt;no norms&lt;/em&gt; around it and so it became odd to see people taking it to the extreme as users and other folks assuming the worst when going about their day. It was too many new things at once. But if you &lt;a href="http://www.theverge.com/2016/9/23/13039184/snapchat-spectacles-price-release-date-snap-inc"&gt;limit the scope&lt;/a&gt;, maybe you can have a better go at things.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Spectacles are going to up many people&amp;#39;s Snapchat game, but I can&amp;#39;t get past the power of capturing moments I never had a chance to capture &lt;a href="https://t.co/htjmMAEn8Z"&gt;pic.twitter.com/htjmMAEn8Z&lt;/a&gt;&lt;/p&gt;&amp;mdash; Sean O&amp;#39;Kane (@sokane1) &lt;a href="https://twitter.com/sokane1/status/799643479995457538"&gt;November 18, 2016&lt;/a&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;p&gt;w/r/t services, i am not going to cop-out and say "you don't have to use it," because the choice isn't always ours if we intersect with somebody else's use of some bias-ridden service. How do we resolve being tagged in a service we're not members of? Do we pass it off on the person doing the tagging or do we enforce policies on services? What about when the result isn't just annoyance but actively harmful?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For many of us that have used some form of irc/icq/aim/zephyr/jabber/etc for upwards of 20 years, the norms around slack communication feel oddly formal compared to prior systems which felt truly asynchronous (often responding to a message 12 or 24 hours later). The pitch "&lt;a href="https://www.youtube.com/watch?v=kJsYKhEV6o0&amp;amp;feature=youtu.be&amp;amp;t=110"&gt;slack is the email killer&lt;/a&gt;" subconsciously migrates social expectations from email (attentiveness, timely replies) to the norms of irc or im (not all messages need an &lt;code&gt;ACK&lt;/code&gt;). That's also under-selling the issue, but it's definitely part of it. Do workplaces that caveat 'important things need to be emailed' experience the same level of slack-anxiety? most workplaces eventually establish norms around communication, but it feels like slack gets left out, which has caused its own set of problems.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-cards="hidden" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;One of the hallmarks of bad software is it believes it is something more than a tool you use to accomplish a task. &lt;a href="https://t.co/YRarXyNdq1"&gt;pic.twitter.com/YRarXyNdq1&lt;/a&gt;&lt;/p&gt;&amp;mdash; Steven Frank (@stevenf) &lt;a href="https://twitter.com/stevenf/status/758711059028914176"&gt;July 28, 2016&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;i talk about norms because when we go to a restaurant, &lt;em&gt;we know&lt;/em&gt;(or have learned) why our server is upselling us. When our friends tell us to skip the bar, &lt;em&gt;we understand&lt;/em&gt; the reason behind the diversion. We have built up norms around these sorts of social situations: servers want to sell specials and perform well (and depending on the establishment, pad out their tip), our friends may no longer be drinking, or maybe their throat hurts from shouting at last night's bar or whatever.&lt;/p&gt;
&lt;p&gt;So what of the societal norms around the apps we use? We've been around long enough now to see at least one and a half dot-com busts and we've been witnessing the effect of companies who put off monetization for easily five to ten years now (or end their &lt;a href="https://ourincrediblejourney.tumblr.com"&gt;incredible journeys&lt;/a&gt; as the result of &lt;a href="https://twitter.com/rus/status/791681274339622913"&gt;selling their company&lt;/a&gt; to somebody else). Still, each time we act offended when we discover that these free apps are also working to earn their investors and employees a buck. &lt;em&gt;Next time&lt;/em&gt; it will be different, though.&lt;/p&gt;
&lt;p&gt;As a field, we designers spend a lot of time talking about how to design with empathy and grace. Still, i suspect the problem is mostly that people expect apps to behave like utilities, deterministic and honest in their focused execution. Now, we're discovering there's something strange about our landlord or our electric company talking to (and empathizing with) us like our best friend and then shaking us down for extra money when the conversation takes a turn.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;latest &lt;a href="https://twitter.com/magic"&gt;@magic&lt;/a&gt; growth hack starts by acting like an old friend, ends by asking to &amp;quot;hop on a call for 10 minutes&amp;quot; 😒😬😡 &lt;a href="https://t.co/PyQedn5yeh"&gt;pic.twitter.com/PyQedn5yeh&lt;/a&gt;&lt;/p&gt;&amp;mdash; Michael Grinich (@grinich) &lt;a href="https://twitter.com/grinich/status/755132086210617345"&gt;July 18, 2016&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;&lt;li id="fn-commoditysplain"&gt;&lt;p&gt;although the meaning is now coded, at the time, the whole point of using the phrase "commodity machine" implied "this computer can barely gurantee an SLA" and "lol @ the lavish costs others spend for sparc/dec/ibm/etc servers running commercial bsd/vax/irix/etc." which made the abstract a sort of boastful triumph of architectural cleverness over cost and software over the unreliable nature of cheap hardware.&lt;a href="#fnref-commoditysplain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-mapsplain"&gt;&lt;p&gt;(*map-reduce was, at least then, not actively used for search, but it was a good enough proxy for the behavior of a large fault-tolerant system that favored partial output to no-output)&lt;a href="#fnref-mapsplain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-capsplain"&gt;&lt;p&gt;In the world of Databases and Computer Science, this "you get what you get and don't be upset" behavior is usually referred to as Availability and is usually pitted against Consistency, whose behavior is more like "i'll give you the most honest answer i have, but it may take a while (or &lt;a href="https://open.spotify.com/track/5VSAonaAPhhGn0G7hMYwWK"&gt;you may never really get it&lt;/a&gt;)". The sophie's choice between the two of them is discussed up in the &lt;a href="https://en.wikipedia.org/wiki/CAP_theorem"&gt;CAP theorem&lt;/a&gt;.&lt;a href="#fnref-capsplain" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-dotcaveat"&gt;&lt;p&gt;and for a multitude of reasons, the physical copy has its own problems.&lt;a href="#fnref-dotcaveat" class="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry></feed>