We always know less than we should. Creating products is a constant movement
into a better understanding of our business needs, market demand, and technology
constraints. We embark on a new project adventure to understand and solve a
problem. We are so full of ambition and oh so naïve!
Our clients come in full of excitement, too. And maybe even bring some pain and
baggage from previous (or current) experiences with technology and the creation
of it and their ideas of how products are made.
We bring to the table all of our ideas of how the client works, or how their
But here’s the thing: we have an incomplete and inaccurate view of what we’re
trying to express in very detailed instructions for a computer to execute. We
work in languages and systems that are frought with side effects. Even if
documentation was complete and perfect, our understanding is not. It is our understanding that is required to author the code and move the bits around to
build these products.
The tools and processes and patterns we employ should embrace naïvety, changing
requirements, and evolving understanding.
Declarative and Imperative UIs
I’m speaking primarily to the user interface of products because this is where
I spend the majority of my time. I can’t speak with confidence to domain models
and distributed systems, though I suspect that anyone with some expertise in
these other worlds would echo these sentiments.
Imperative User Interfaces
Imperative programming—which is the most likely kind of programming that you
do—does not generally take misjudgements with much grace. Imperative programming
is when you painstakingly write each step of the process for the computer. In
browser-UI land this might look something like the following:
- Render each of these email messages with this exact DOM structure.
- Stick that structure into a DOM node with the id
(2b. Hope there aren’t more than one DOM nodes with that id.)
- When you hear a click event on an email message, replace the DOM node with
email-messageswith the DOM structure for an email message that looks like this (pretend there is a complex HTML template here)
- Decrement the unread count on the sidebar.
Cake, right? Now at any given point in time—could be the same general time by
another developer, or by yourself in a week when you return to this code—the
backend now has an API to get the unread count that you use to populate that
data. Without changing the previous code at all—maybe even completely unaware of
its existence—the following instructions are added to the codebase:
- Every 10 seconds fetch the unread count from https://website.com/api/unread-count
- Grab the unread count from the response and render it in the sidebar.
We could keep going with evolving requirements like showing the unread count in
the favicon, or show the unread count alongside the username in the header bar,
but all of these situations are only more examples of the current bugs in our
What guarantee do you have that the unread count in the left navigation is correct?
None. At all. The source of truth for this application is non-existent and
arbitrary pieces of our application code can touch anything else. You may argue
this problem is confined to hostile runtimes like the browser, but I would open
up your bug tracker and point out the many scenarios where you have issues due
to similar practices.
Declarative User Interfaces
Using a declarative programming model, the UI becomes a projection of the app
state. I am of the opinion that a UI is the expression of your product, which
is all of the business logic that culminates in a given application state.
Twitter’s core product is not twitter.com, or the Twitter iOS application.
Their product is the network of connections and conversations you have and are
able to tap into. Twitter.com, Twitter for iOS, etc. are interface expressions
to their product.
Last year at feops conf, Seth
Walker made a comment about the
throwawayability of UI code. Etsy is a heavily data driven company that is
running numerous experiments at all times. The ability to write a local UI
experiment that doesn’t bleed into other aspects of the application are
A global and stateful environment like the DOM and CSS actively work against
you attempting to do this at scale. At Skookum, we found that React helps us
tackle concerns at the appropriate level of abstraction. This is a topic you
will see us talking about more in the future, and much has been written by
others on the subject already.
With a declarative paradigm, the previous example becomes something more akin to
- App state -> UI
- Change app state due to that 10 second interval AJAX call -> UI
All state should flow through the same pipeline to generate a predictable
result. React and Flux are not the first to these ideas, but they are modern
implementations of these ideas; implementations that operate well in stateful
environments and at scale.
Service-oriented architecture (SOA) is the approach we’ve (we referring to our
industry, not only us at Skookum) been taking to build our back-end services
that power our businesses. Marrying ourselves to one technology or one codebase
has not panned out on the server, and it hasn’t worked out on the client,
So let’s take a moment to talk about the browser world. We have an unknown
number of user-agents currently active in the world. (Maybe it’s knowable, but
it’s not grokable. It is past the number that a human can understand, comprehend
and hold in their head.) The screen size, capabilities, and power of these
our concerns. It’s these interactive expressions of our data and how they
compose together. It’s how those strings of HTML and CSS get to the browser and
how they are updated in the browser.
Are the tools we’re creating and choosing to implement helping or hindering us
from the goals we are aiming to achieve? While some frameworks and tools help us
write less, do more, and move faster, other frameworks help us think more,
consider more, and comprehend more.
Time and again React.js and kin have provided sane evolutionary paths forward
from poor judgements, whereas every other tool I’ve used did nothing to protect
me from myself.
For Humans, By Humans.
We create for humans. Our creations manifest themselves through computers. But
we don’t write code for computers. We write code for humans. The humans who
come after us to maintain systems. To evolve systems to new business
requirements. To evolve systems to new technology requirements.
Let’s create and use tools that make us better, stronger, safer. Let’s not
simply continue to make the same buggy software faster.