The Value of Feedback: A Tale of Building An Internal Tool
As most of us software engineers have known, tooling is a very important part of our day. In fact, there was an eloquent post on this subject matter recently. We are not the only discipline that benefit from such tools. Below is a tale wrought with insight on how an iterative feedback cycle influenced a tool we created for our WebEdit team to increase their productivity while creating unstructured content.
Problem Scoping
Background: At America’s Test Kitchen, we are working on a new content type
(internally called an Article
) that is more complicated than our current
content offerings. Instead of having highly structured data, we are designing
this to be free flowing (think one wysiwyg to rule them all).
Stack: Our stack for this piece of content is a Ruby on Rails back-end with a shiny new React front-end utilizing both flux and react-router.
Problem: Our WebEdit team, the people that create all of our wonderful web content, need sophisticated previewing tools for this unstructured offering. And that is what we are building, a semi-realtime previewing engine that gives content creators instantaneous feedback for their free flowing content.
Slight Tangent About Why Article Content Is A Big Deal
This is purely from a technical standpoint.
To date, the majority of our content is denormalized and very rigid in
structure. We have given our WebEdit team a CMS that mostly involves inputting
text and creating necessary associations between our content (there are very
minimal text fields that allow for custom HTML). This general work flow gives us
consistency on our front-end for our users but really doesn’t allow WebEdit to
create custom content from a layout perspective. Look at the two pdfs
below as examples of Article
content:
It goes without saying that these pages are much, much more engaging than our current crop of documents. That is not meant to be a knock on our current documents. It’s just a different means of conveying to our users what went right and what went wrong in the kitchen as we develop recipes. We do not have a content vehicle to effectively communicate what went wrong in the kitchen because failures differ wildly from recipe to recipe. What fails for a brownie may be absolutely different than what can fail for flank steak. Conveying this failure message effectively is the goal of our upcoming Article content.
Minimum Viable Product
Our prototype is going to be a proof of concept. Technologies used:
JSXTransformer
, iframe
, jQuery
, React
. In our current CMS (created with
bootstrap), we are going to have a preview button that brings said iframe
into
view with the click of a button. As someone changes the one html field, it will
re-render the iframe and reload the entire page with the new data (similar
to CodePen.io).
Holy smokes Batman! Well, we are off to a great start. You can see that as we
type, we have a request firing off to reload the page with the new value. The
ArticlePreviewContainer
then transpiles the JSX and evaluates it.
The original thinking behind this was to make it similar to CodePen using some
Base64 shenanigans gist and created
both a Rails route and React route both looking like: /admin/article/:id/:base
.
Using the gist above, we are encoding the value of the body input into Base64
and passing it to the routes for the :base
parameter. We even having it
working with one of our custom React components! Hot Sauce! Here is what our
React Preview Component looks like:
You might ask yourself, “What’s up with that RecipeIndexPage require at the top?”
Currently, it is the only custom component I am allowing to be rendered in the
ArticlePreviewContainer
React component. Requiring it was the only way I
thought to show WebEdit what it looks like when using custom components.
Speaking of WebEdit, it’s time to meet with them to get their thoughts on this.
MVP - Feedback Cycle
(Just going to put my scribbled down notes from WebEdit below…)
- This is really cool, but does it have to be inside the same window? Can we
put it in it’s own window?
- Can we get some sort of error message if we write bad markup (JSX)?
- Can we get documentation for the various components we have access too?
(Some notes I took down when dogfooding it to the engineering team…)
- Iframe? Wat? Why aren’t you using postMessage?
- We should definitely whitelist things that they have access too using
React.Children.map
for this content.
- JSXTransformer
is being deprecated. Use Babel and pass over the transpiledJS
Whoa. All great, great ideas. Definitely going to incorporate this feedback into my MVP.
Version 2 - The previewWindow and postMessage…and linting?
Now here is where the fun begins!
Technologies used: Babel browser.js, eslint, Underscore, postMessage.
Technologies being removed: iframe
, JSXTransformer
, Base64 gist
The only real code to care about here is:
We register a keyup event on the singular input field with a delay using
Underscore’s debounce method. When the callback is invoked, it performs the
following functions:
1) Lints the JSX.
2a) On failure, disable all submit buttons and display an error message.
2b On success, enable all submit buttons and transform the JSX using
babel (and wrap it in an Article
react component for…reasons).
3) After transformation, it checks for our previewWindow and posts the message
to the previewWindow.
Here is what it looks like:
Time for one more round of feedback!
Version 2 - Feedback Cycle
(Going back to my scribbled down notes from WebEdit…)
- This is much better. But when I update the title, it doesn’t update!
- We still need a style guide to let us know what the markup looks like and
what it outputs.
(Going back to my scribbled down notes from engineering…)
- Where is my white list!!?!?
- Where is the friggin white list??
- Moar white list please!
Alright…thanks guys. I can feel the feature already maturing as I received no feedback on the actual implementation of it and mostly received feedback on evolving it and protecting WebEdit where I can (white listing). We are definitely on the right track, so let’s start moving onto version 3!
Version 3 - Final Final Release (Maybe?)
So instead of listening to a single input (on ‘keyup’), we need to bind our postMessage event to more inputs.
Here is the code for said event passing JSON and listening to specific inputs.
White listing didn’t prove very difficult either. We created a mixin that would
traverse the component’s children and return null if it was
not in the list (hence why we wrapped the resultant JSX in <Article>
tags).
Currently, it only supports white listing of HTML elements that React supports.
We will be modifying the conditional logic to also check for a white list of
custom components in the very near future.
Whitelist:
Article Component White Listing:
What’s this look like now?
As for the WebEdit’s need for documented components, we found an open sourced project that has been amazing for conveying both markup and the actual look of the component.
Conclusion
Without a doubt the feedback cycle from both the end user of this tool (WebEdit) and from other engineers vastly improved the quality and maintainability of the tool. Going from MVP to something much more robust and maintainable can only be realized by getting feedback early and often as features are worked on.
Most importantly, WebEdit can now be highly productive in using this near real-time tool to build really amazing content.