Why we built our message editor twice

Building a sound, intuitive message editor for use everywhere in Intercom proved to be a much more challenging, time-consuming, and expensive task than we ever imagined.

I explained how we overcame those hurdles and the importance of this investment at a recent Intercom event, On Product. What follows is an edited transcript of my talk, along with a handful of relevant presentation slides.

Up until mid-2014, Intercom used a simple text area to create new messages. But this text area was just that, for text only – no formatting, no images, and no rich media. We supported a mixture of markdown and HTML to get around this, but there were problems: it was tricky to compose, you needed to be somewhat technical, and it was very easy to make mistakes. It was also difficult to render consistently on all devices. Mobile applications, for example, need a WebView to render HTML.

We can build it better

Around this time, we kicked off what we thought would be a small project, to build a better editor. To start, we defined a few guiding principles:

  1. Everyone should be able to create beautiful messages.
  2. Messages should be composed of simple constructs; constraining options would help users craft effective and consistent messages.
  3. Messages should look great on all platforms, so no HTML.

We then came up with a simple data format we call “Blocks”. These have a flat structure and support a limited set of types, including headers, paragraphs, images, buttons, and videos.

Above is a JSON representation of those Blocks. We would use this format for both database storage and client application APIs.

Our initial prototype used ContentEditable as the input surface. This is very straightforward; you add the property tag to an HTML element and it gains super powers. You can paste into it, and it supports lists, along with bold and italic formatting. You also get undo/redo support and keyboard shortcuts.

It does, however, produce HTML. But, we’d be fine as long as we could convert that HTML into Blocks format – or so we thought. We kicked off a six-week project to build this new editor but soon realized there are a great many quirks with ContentEditable. For example, when a user presses a return key in an empty ContentEditable element:

With Firefox you get a <br> tag. IE gives you two paragraph tags. With Chrome it’s two <div> and two <br> tags. It turns out there are no real standards with ContentEditable. As we built our editor we discovered many more weird cases. We’d find a quirk, create a workaround, and find another quirk. It was rinse and repeat.

This project turned out to be a lot more complex than we had first imagined. But after a while we felt what we’d built was sufficiently better than our previous text area.

There was a beta program, and some more bugs came in; we fixed them, all quite normal. Nothing seemed too difficult, and after a while we released it to everyone.

Live complications

Suddenly the issues came flooding in. We had 60 new issues in the first week – many more problems than we had known about. We closed 30 of those issues during that first week and another 50 came in. A common one: ContentEditable would get into an unexpected state. We’d end up with JavaScript exceptions or do a bad job of converting the HTML to a valid block structure.

We had run lots of trials, but our editor’s architecture made it difficult to test what we now discovered were its most brittle parts. This was a very bad situation, but we couldn’t go back. Our customers had used this editor to create content and messages that we couldn’t convert back to the old format.

We didn’t think we could fix these bugs and build upon our current editor. A new approach was necessary, one that avoided the complexity of ContentEditable. If we wanted to take full control of the editing experience, we needed to build a Blocks editor and not a HTML editor.

Building the BOM

We sketched out a new architecture we called the Block Object Model (BOM).

We would intercept all key user actions such as backspace, return, and paste, and run these against our BOM. The BOM would become the canonical representation of the message being edited, and we would be in complete control of all rules for content manipulation. This was going to be a massive project and would take many months to build.

The cupcake principle

At Intercom we ship fast and iterate based on feedback. For new projects, we usually apply the cupcake principle: the first time you bake a cake it should be a cupcake not a wedding cake. If you make mistakes, they’re not so costly; you learn from them and make better cakes.

But we knew exactly what we needed to build. We couldn’t ship half an editor, so we accepted the mammoth project. After six months of hard work by a lot of engineers, we shipped it, and it’s now used everywhere in Intercom.

This simple editor enables lists, and formatting such as bold, italic, and links. The auto message editor enables headings, data attributes, and videos. Our custom undo/redo stack is unremarkable – meaning we did a good job diligently recreating the native behavior.

We can paste images and insert other block types such as buttons, which have nice live updating editors. Messages can take a number of forms. We can also send as email and use predefined or custom templates.

The shared inbox enables our customers to manage a large number of conversations with customers. The composer supports images, emoji, and stickers. Private notes can contain mentions through a simple shortcut and typeahead. There are also shortcuts for inserting saved replies.

To guarantee the ongoing quality of the composer we built up a suite of more than 1,000 test cases using a simple DSL.

Applying hindsight to the future

Looking back at these two projects, a couple of things become clear. First, we completely underestimated the difficulty in building a solid message editor, so much so that had we known how challenging it was we may not have even attempted it. But as we built the second version, we realized how important this component is to our product.

Whatever Intercom becomes in 10 years time, messaging will still be at its heart. Although the initial project was a failure, we learned a huge amount about how a solid editor should be built. That first failure allowed us to nail it the second time, and we now have a great foundation to build upon.