Trivial features are killing your product

Shuky Kappon
6 min readMar 14, 2016

--

Dedicated to optimistic product owners and developers everywhere…

I’ve been meaning to play with Swift and XCode for a while now, and finally got down to business last week.
The experience did not only teach me about using XCode and Swift, but also made a greater impact on me, as it left me with a powerful example to an important lesson about shipping products.

I decided to share it with you, and even though I’ve included some technical explanation and code examples in the full post on my personal blog, you don’t really need to dive into it to get the complete picture of what happened and my take about product shipping from the experience.

Creating an iOS app in less than a day

It all started with my decision to take Udacity’s “Intro to iOS App Development with Swift” course for finally getting my hands dirty.

Throughout the course, you build a simple application with two views, that enables the user to create an audio recording and then play it back with changes to speed or pitch.

With the course’s help, I was able to produce the following app in less than a day:

The first blue view is responsible for recording the audio, while the second white one, with the four icons enables you to play back the recording using four different effects.

Trivial change doubled my development efforts

While building the user interface, I decided to take a slight detour from the course’s material and wanted to make a small adjustment it.

One of the course’s earliest exercises involved toggling between the microphone button and the stop button on the first view while recording, and a comment had also been made about user experience and how it is important to keep the interface simple and only show relevant actions to the user.
While this advice was included to the first view, it seemed to be forgotten while building the UI for the second view.

The second view had an identical stop button that was responsible for stopping playback, only this time it was always visible, even when no audio was playing.

My OCD kicked in, and I decided to fix this behavior by displaying the stop button to the user only while audio was playing.

I didn’t really gave it a lot of thought because my optimistic side was going:
“it will take only a couple of minutes”, but boy was my optimistic self wrong…

Hiding the stop button

First, I’ve set the stop button to be hidden by default, and only made it visible in the start of my play function, which got called while one of the effect buttons was clicked.

Then I needed to hide it again when playback finishes.
In that point in time I was playing a sound with an apple component named AVAudioPlayer, so I found out that you can pass a delegate with a function that the AVAudioPlayer calls when playback ends.

For those who understood even vaguely what I am talking about, I seriously recommend that you continue reading this post on my blog where I dive deeper with the technical explanations and share my code examples),
For those who didn’t, here is what matters:

Boom! Problem solved. It was easy and took less than ten minutes; I pet myself on the back and resume my training with the next video session.

Hiding the stop button… err… again…

So the next chapter goes through recording audio, and I even save some time because I already learned about delegates myself and I can skip that section — I’ve earned myself yet another pet on the back.
I then get to the chapter where we learn to change the pitch of the audio, and the smug smile is wiped clean off my face.

The requirements change, and we need to replace the current AVAudioPlayer component with a more advanced one named AVAudioEngine for playing sounds on iOS because this is the only one that supports changes to pitch.

This component doesn’t have a delegate like the last one, but when you call the function that plays a file, you can tell it to run another callback function when the audio playback has ended, so I rewrite the code and pass the function that hides the stop button…

it doesn't work !!!
(and that stop button is also really starting to annoy me)

I take a look again and realize that this happens because the callback function runs in parallel to the rest of the code.
Trying to change the user interface directly from a parallel call is not allowed in Swift.

This is a common paradigm about user interface development in many other programming languages, so I picked up on it rather quickly, but a junior developer could easily find herself wasting days on this. ¯\_(ツ)_/¯

I fix this problem too…
it still doesn’t work ! ! !

Reporting a bug to Apple

I do some testing and see that my function to hide the stop button is called immediately when starting to play the recording (and not on the end of the recording like apple’s documentation tells me).

apparently, this is a bug in a the iOS SDK, and I stumbled upon it on my first day developing for iOS. Weird… but it happens.

No way I’m backing down from hiding the stop button! this is the most important (and only) app for IOS I’ve ever built (I also grew an animosity forward that button by now).
I’m going to tackle this like good developers do; I’m going to find a workaround!

Finding a workaround

I’ve found a function which gets a buffer instead of a file, and works similar to the one with the bug.
I still needed to add some more code to convert the file into a buffer, but fun.

IT WORKS ! ! !
(and I also completed the course!)

… but the saga yet continues…

Staging environments work, production ones crash and burn

Feeling proud, I grabbed my wife’s iPhone 6 and deployed my new app on the first real device; I try the Darth Vader effect and it crashes immediately.

This took some more time, and some trial and error, but I finally realized this happened because when I tested the app on my computer it used only one audio channel, while the real iPhone was using two, and the buffer function I used as a workaround required some more changes in order to work on a real device.

IT WORKS ! ! !

The love-hate relationship I’ve been having with the stop button, makes me test a recording over and over again.

Fixing yet another bug

apparently when the audio got interrupted in the middle of playback, the stop button would continue to show. this was caused by two things happening at once, and it took me some more time to find out why and how to fix this as well. but this time I was finally done.

Aftermath

If you got this far (or just scrolled to the end because you just wanted to get to the point), you can probably argue that this was not a real world scenario, and
in-fact, I’m pretty sure that if this wasn’t a learning experience I would have prioritize my efforts differently.

but having said that, still, a tiny feature ended up costing me almost the same time it took to build the entire app, it resulted in more work when new requirements factored in, and it introduced code changes that took even more time and even caused a stability issue that would classify as a show-stopper in the real world.

Time constrains or not, I was positive that I have a solution in each step of the way, and I was just one guy developing a small app.

I can easily see how real teams of developers and a product owners can lose track of a real trivial feature with one or more similar side effects to the one I’ve experienced here, and with all of that in-mind, I leave you with my conclusion:

When shipping a product, Learn to say no.
Even to the smallest trivial features if they are not essential;
When essential, leave those for later as you never know how much time they will actually cost.

  • They are steering away your focus from the goal at hand.
  • They might take more time than planned.
  • They’ll probably have bugs in them you’ll later find out.
  • Requirement changes might break them.
  • They have the potential to kill your product in production.

P.S.
If you want to learn stuff, never surrender, and never stay on the path… that is, unless you’re thinking about hiding the stop button in the second view of Udacity’s “Intro to iOS App Development with Swift”, in that case — just don’t.

--

--