Good debugging is putting yourself in your user’s shoes
Typos, mutability, and the world’s worst coat check
I woke up one morning to a notification on Slack saying [email protected] iOS feedback:” with a link to an article by H. Nemesis Nyx.
Oh shit.


Not only is a bug like this embarrassing, it’s been affecting our users in an extremely negative way. At the time of writing there are 11 responses to that post, most of which chime in with a “me too!” and “so glad this isn’t only happening to me!”.
I was devastated, to say the least. My number one priority as a developer is to provide users of the software I write a great experience. Having random words disappear from your Medium post is far from a great experience, but worse than that, it breaks the trust we work hard to build with our users. I responded to the author hoping others would see it, letting them know that fixing this bug was now my top priority.
I’m Tyler, an iOS engineer here at Medium. I just wanted to say thank you so much for reporting this issue as we were…medium.com
But where do I even begin? I hadn’t been able to reproduce this behavior myself, and even if I could, how would I track down such an obscure bug?
I decided to put myself in the shoes of the author and type out their post word for word. You’ll notice, the author of the open letter highlighted pieces of the article that were lost on publish. Perfect! The tone is passionate — they likely wrote most of the words in one go, going back to make corrections later. They probably added styles to the paragraphs after most of the words in a given paragraph were written. For example, there’s a large section of block quotes in the middle:
Every post. Every single one. When I publish, shows up with missing words when the finial post is shown to me.
Single
Post
There are a couple of interesting things happening here. First, there’s a typo in the first sentence. My gut told me that the author attempted to fix that typo before publishing, but that fix didn’t show up in the final version. The second red flag was the incorrect grammar in the one word phrases. There should probably be an “Every” before “Single” and “Post”. Finally, there is another section of multiple pull quotes further down in the piece that have a bunch of missing words as well. This is the same block of paragraphs I was able to easily reproduce the bug with. I typed out the three paragraphs, changed each one to a pull quote, then went back to edit the first sentence to fix the typo. A few seconds later I saw the follow log statement in my Xcode console:
[INFO]: Save called with nothing to save
Welp, that’s not good.
The implementation of our editor saves your post every couple of seconds to make sure you don’t lose any data. To do this we have a timer set up to fire every 5–10 seconds which bundles up the current changes (deltas) and sends them to our servers. If the save method is ever called without any deltas to send, we log the above info message. Unfortunately, there was something to save: fixing the typo!
If you’ve ever written anything in C, you’re familiar with pointers. A pointer is just a reference to something that lives somewhere else. A pointer is like that little slip they give you when you check your coat at a party — a small and portable object that uniquely identifies your item. But what happens if the coat check operator gives someone else a copy of your coat check slip? There are now 2 slips (pointers) that point to the same coat! In programming this situation is possible, and an easy mistake to make. Two people now lay claim to the same object, so what happens?

In our case, we maintain two copies of the post data: the current version that is being rendered on the screen, and the version we last saved to the server. This way when we want to save the current version, we can easily find the differences between what we saved previously, and what is new to save. Each data source is a list of individual paragraphs — two isolated and separate lists, or so we thought.
To keep these lists in sync, we use one to update the other. Once the save is successful, we update the “last saved” list with what we just sent to the server. When we do this we give a pointer to the second data source that points to a new copy of the paragraph we are updating. However, in a couple of cases we were accidentally handing it a pointer to the original paragraph. It would be like handing someone else a copy of your coat check slip — they now have direct access to your coat, just like this second data source had direct access to the first data source’s paragraph model.


So, after we’ve given the second data source direct access to the first data source’s paragraph, the user continues to type their post. The editor then tries to find differences that need to be saved, and asks for the two data sources to compare their paragraphs and give back deltas. When we get to the paragraph that both data sources share, it comes back saying “nope, nothing new here” because it’s literally the same paragraph. It would be like asking someone “what’s the difference between these two coats” while holding up a single coat.
The fix is extremely simple: just give the second data source its own copy of the paragraph. In fact, I only had to change 3 lines of code. And then I did this:

The moral of the story? Mutability is not your friend.
Technical note: unfortunately it isn’t really possible for our editor models to be completely immutable. We update the underlying data model as the user types, and regenerating the entire model on every keystroke could cause the editor to slow down with larger data sets.
Thanks to Eugenia, Michael, Madeline, and Marcin for helping me edit this post, Andrew for helping me track down this bug, and H. Nemesis Nyx and others for reporting it.