Welcome to Under the Radar, a show about independent iOS app development. I'm Marco Arment. And I'm David Smith. Under the Radar is usually not longer than 30 minutes, so let's get started. So this week we wanted to talk about managing some changes that we're doing that feel risky because they involve...
changing long established behaviors or UI characteristics or code even in our apps. And it can be tricky. When you're first starting out making something new, you have no users, you have no traffic, you have no data.
And so you can kind of change whatever you want all the time. I remember back in the Overcast 1.0 beta, there was so much change during that beta period and huge changes to the way the app worked, to how it looked, to even what the features were, how the features worked, like massive changes during that period because there was no legacy, there was no data, the user base was like 50 people. It was a very relatively small thing.
And so I was able to iterate quickly. As time has gone on, I mean, that was eight years ago, I have...
been less and less able to make those big changes. And there's been lots of reasons for that. Part of it has been just, you know, my, my workload and my life, you know, I've had less time to do like really big coding blocks. A bigger part is just that the app is, has gotten really well established. You know, I'm, I'm very fortunate that the app has a lot of users. I, I manage a lot of, you know, volume of, of usage and of user data, um,
And and I have, you know, if anything, I change. I hit a bunch of one star reviews and a bunch of five star reviews. So and a bunch of people saying, why don't you just make it a setting? So anyway, why don't you just that phrase? Nothing that's ever followed. Why don't you just has ever actually been constructive? I think.
Any comment that begins with, why don't you just instantly says, you don't understand the problem. Yes. Everything is more complicated than you imagine it to be. Exactly. Exactly. But anyway, so...
I've recently undertaken a few of these changes over the last couple of weeks, kind of unplanned, but that's kind of how I work. And you're tackling some big ones as well. So first, I want to hear, what is the large change management that you're doing recently? Yeah, so in this version 5 update of Pedometer++ that I've been working on for the last few months, which I think, hopefully, fingers crossed, I will be submitting early next week, which I am thrilled by. Nice. Yeah.
It's essentially rebuilding the entire app from almost the ground up, from maybe the second floor up. I'm leaving some old Objective-C data layer stuff, but everything else is a complete rebuild to essentially modernize the app, to add a bunch of new features, to do a lot of big... Essentially put myself in a position to actually move the app forward rather than feeling like I'm trapped in the past, which is how I've been feeling for a long time.
But it's like so much of this is this weird, this terrifying feeling of, so I have something in the app store right now that works. It's like battle tested. It's proven. It's been out there for, you know, almost a decade now in some form. And it is very stable. And I'm going to essentially rip it out and replace it, which is,
I think is the right call in this situation. Like, I think I can't maintain an Objective-C UIKit app going forward into the future. Like, it is just at a certain point, I'm going to have to migrate it. And I may as well do that now that, you know, the APIs are mature, but most people are running the latest operating systems, all the good things that are like reasons it's a good idea. But it's like, I'm fundamentally changing the thing that is so vital to my business. And so like, that change is just terrifying. Yeah.
I mean, I've gone through this a few times where like I've made changes like this. And I think like in the back of my mind, I'm always have that feeling of like, you don't want to kill the golden goose like that, like the nursery rhyme about that, where it's that sense of like,
There's this thing that's working. And if I make a change, like this thing could just go away. And that's really problematic that like, in theory, you hope that it's just like, oh, you know, you get, you know, 25 star reviews, 21 star reviews, it all balances out, and it kind of moves forward as though nothing has changed, or in the best case, maybe nothing.
You get sort of like a different ratio of those that it's like 25 star reviews and five one-star reviews. Most people like it. The few people don't like it, whatever. But no matter what, there's always that risk of it's like 50 one-star reviews and no positive reviews. All the work has sort of gone poorly and terribly and everything falls apart and your life is over or your professional life is over. That's the –
Like, that's the risk that's always in the back of my head when I'm going through these kind of big fundamental changes. And it never gets easier. Like, I've done this many times now. I've gone through many, you know, things. And it's much easier, I will say, back in the day when my apps had, you know, hundreds of users. It was much easier to manage than in a situation now where I think we're both in a position where our apps have large enough audiences that...
you make a change, even if 1% of those people don't like it, like that's a lot of people. That's a number of people who could both make your life difficult in terms of
reviews in the App Store or all kinds of other places or just in general. It's an absolute number. It's a lot of people that might get really annoyed and disappointed. I don't want to disappoint anybody. Someone who likes my app now, I want them to keep liking my app. I don't want that to change. It's a dangerous thing to do. I always feel really nervous and tricky about actually going and doing it.
Yeah, that's exactly what I've been facing. Even the numbers you threw out, like the 1%. So one of the big risks that I took recently is about a week ago,
I ended support, and I said I was going to do it on Mastodon, like I said ahead of time, but not everybody follows me on Mastodon, obviously. So I ended support for some really old versions of my sync API that runs on my servers. And this was mostly because I'm in the middle of this giant migration process
That's another huge change that I'm managing and that I'm very stressed about. So basically, as I've talked about many times before, I'll be brief because this is a brief show. I've been doing server migrations and server tweaks to basically scale the business better, to lighten the server load, to make sync faster and more reliable, and to lower my server costs because my costs just keep going up and up and they're getting really crazy. So anyway, it's been very, very stressful, but I'm actually making pretty good progress of...
Kind of moving everything into this new – a newly architected handful of classes and functions in the back end where I'm consolidating all of the access to these two massive high traffic tables, the subscriptions and the episode progress tables basically. Consolidating the access to those two tables all now through this one unified class and
that has the ability to read and write that in two different ways. One of them is the new way that I'm trying to migrate to where it's going to be way smaller, way easier to host, and give me more flexibility in how it's hosted in the future. The new class has the ability to do things like...
instead of hosting it on local mysql servers i could host it like on a managed database service you know where then i don't have to deal with a lot of this stuff and scaling becomes easier performance becomes better cost might be better in certain ways i don't have to deal with backups and replication and sync and everything else so so moving it all towards this new system so this has involved rewriting and refactoring tons of the server code because it's
It turns out when you run a podcast app, Sync Servers, a lot of code involves reading and writing to the tables that deal with what podcast you're subscribed to, what your settings are, and what your progress and status of every episode is. So anyway, massive changes here. And in the process of doing all of that, which again, that alone has been terrifying, but also very...
So far, it's been very fulfilling in that by doing this, I've been able to make some things more efficient already, even before I started using the new table structure. I've been making a lot of things more efficient with the old table structure and everything. But as part of doing this,
The way I've managed my API versioning over time is I basically just copy the controller, the API controller. I have API 1, API 2, API 3. Whenever I do a major API version change where the method by which the app syncs to the servers has to change in a big way, I just copy, I make a new API controller, and I make the whole protocol in that. And then I just have the old one around, just running untouched.
Now, as I'm making all these changes to the low-level data storage and how it's accessed and everything else, these are pretty large changes to the code. And so I had to make a difficult choice. I had to either just keep the most modern API files up to date and refactor those to use the new code, or go back and refactor all of the old ones also.
And I chose not to do that for a few reasons. The biggest of which is that the old ones are all... The ones I was hoping to not have to update were the ones that serve versions of iOS that the app hasn't worked on in about a year or more. And so this is basically everything before iOS 15. And when it comes to my usage...
It actually is, you know, it's not nothing. It's about 1%. It's kind of funny, actually. I have about 0.7% on iOS 14, about 0.3% on iOS 12.
and almost nobody on iOS 13. I have almost as many people on iOS 17 as I do on iOS 13. iOS 17 isn't out yet. So iOS 13, yes, it was a disaster. Apparently everyone else thought so as well. So it seems like
The people who are holding on to iOS 14, because 15 and 14 run on the same devices. I think, from what I've gathered, it's mostly jailbreaks, because jailbreaks, I think, are still on iOS 14 only. I don't really follow that world, but that's what I've been told. And also people who are just like...
who are just kind of scared to update because they have maybe an older device and they don't want their device to slow down. And they've had bad experiences in the past with that. Somebody wrote in the day with an iPhone 11, and that they considered too old to run iOS 15. They were holding it to 14. That's what happens. Your user base, people develop these habits with bad experiences in the past, and they refuse to update anyway.
And iOS 12 makes some sense because the devices that are on 12 can't go past that. Like they are not compatible with 13, 14 and 15. But that's all in total. This total is about 1% of my user base. And I made the decision to actually cut that off because it was just it was so much work on the server side. Basically, my workload during this migration would have approximately tripled.
And furthermore, I no longer maintain old devices that can test those old API versions. Because after a while of my app not working on an OS, I decommissioned those test devices. So I don't even think I have a device that's running iOS 14 anymore.
I have an old one that can run iOS 12. I can maybe test that, but the API that runs iOS 14, I can't even test that myself. And so I'm not going to rewrite this huge part of my API file for a very small percentage of the user base that I can't even test against, and then I have to maintain indefinitely into the future. So I decided it was a tough decision. The last time I did this, it was probably three or four years ago at least.
um but i i did cut off support and i've heard from those people i've heard because you know one percent of a big number is a big number and so i have heard from them and it's and i knew i would and i felt bad and i've been you know i've tried to explain them as nicely as i could you know here's the situation and you know for the most part most people were like well okay you know thanks anyway a few people were really mad but you know what can you do um and ultimately this is what i had to do to make the app better like i i can't
When I look at what I'm moving towards, the world I'm moving towards by doing all these things, not only am I going to save server costs, which is something that I care about, but my users don't.
But also, I'm making things less likely to break in the future, much more resilient. But the big thing that my users will actually care about is that there have been a number of really good features that I haven't been able to do because of the way my old sync system worked. And there have been a few really big, annoying sync bugs that I haven't been able to fix because of the way the old system worked.
And so soon, once I'm on this new system, I will be able to do some really nice features that have been like the number one requested features for a long time. I can finally start to do those because the sync system will support them. And that's something that the old system could never do. So it's this is I feel like, you know, the the decision you have to make when you're when you're managing these big changes and taking these big risks is like,
Yes, you are going to upset some people. That's inevitable when you change anything that people are using. But you have to look at the bigger picture. Like, well, overall, am I going to please more people than I upset? Am I going to... Is this going to allow me to get new customers and retain my existing customers better? And unfortunately...
Those things often require losing some other people. And I wish it didn't have to, but that's the reality. You can't please everyone all the time. And I think there's an element of being...
As long as you're being thoughtful about it, like don't just make change for change's sake and be like pulling the rug out from other people just because like it's slightly easier. But if there's a good reason, like it's just I think that is like an important thing that is very difficult to feel good about. But I think increasingly I'm in that mindset of.
Understanding that those small, like single digit percentage numbers of users, like trying to cater to them is just going to ultimately make the product worse, right?
And it's going to make the experience of the 98% of other people have a worse experience. And then that is way more important. That is such a disproportionately valuable thing. And there's no strategy that you can take that is going to eliminate issues with...
That kind of very long tail situation. Like I ran into this recently, this one, just the stat just like blew my mind where I was doing some, you know, the dark sky API is going away. And so I was doing some work with migrating away from it. And my dark sky, like API calls were higher, like after kind of working through the migration stuff, then I expected them to be. And I eventually worked this out that there were people, there was a bug in the very first version of widget Smith, like the very first one.
on like day one. I remember that. Yeah, that was making way more dark sky calls than it actually should. And it was like happening for everybody, whether or not you had a membership. So there's still 1% of my user base running that very first version of the app. Oh, wow. And it's like, they've never updated the app in two and a half years. And like, okay, like, weather for them is just going to stop working at some point, because dark sky is going to go away.
And it was just like one of those thoughts of like, yeah, those people, like I can't, there's no, I can't help them. Like there's nothing that I can do to that person who is in a situation where two and a half years later, they are still, they downloaded one version of the app and they have never updated it since like that's a choice they're making or a situation they're in, whatever that, whatever the reason for that, it's like, that is just,
That is not where I should put my effort. I should put my effort into the 98% of people who are running a version that I released in the last six months. And that is a cohort that I can work for, make their life better, make my life better, and put effort into in a way that is actually useful.
We are brought to you this episode by Indeed. When it comes to hiring, you need to trust your gut. But what if you could give your gut some help? When you want to find the top talent fast, you need Indeed. Indeed is the hiring platform. You can attract, interview, and hire all in one place. Don't spend hours on multiple job sites looking for candidates with the right skills when you can do it all with Indeed. Find top talent fast with Indeed's suite of powerful hiring tools like Indeed Instant Match, Assessments, and Virtual Interviews.
And if you hate waiting, Indeed's U.S. data shows over 80% of Indeed employers find quality candidates whose resume on Indeed matches their job description the moment they sponsor a job. Instant Match really is incredible. As soon as you sponsor a post, you'll get that short list of quality candidates. You can invite them to apply right away.
Join more than 3 million businesses worldwide using Indeed to hire great talent fast. Indeed knows that when you're doing everything for your company, you can't afford to overspend on hiring. Visit Indeed.com slash UnderTheRadar to start hiring now. That's Indeed spelled I-N-D-E-E-D dot com slash UnderTheRadar. Indeed.com slash UnderTheRadar.
Terms and conditions apply. Cost per application pricing not available for everyone. Need to hire? You need Indeed. Our thanks to Indeed for the support of this show and all of RelayFM.
Yeah, so I was, you know, speaking of like, you know, other changes that you can't please everybody. I also made a couple other changes recently in my UI. I issued a couple of app updates, trying to solve some longstanding problems and trying to give people a few like little new features while I work on some bigger behind the scenes stuff. You know, you mentioned you're doing this big pedometer rewrite, you know, you kind of started from like the second floor up. I'm doing, I'm trying to do the same thing with Overcast. And this is another reason why
I am doing all the sync engine work is that I'm, I'm modernizing the backend and adding all these, all this support for new features while at the same time, rewriting the entire data layer and overcast very slowly, um, as part of my grand rewrite plans that, that, you know, hopefully getting everything to Swift UI and Swift and, and async stuff and Blackbird and everything else. Um, and so, um,
I'm also rewriting the sync protocol that this new sync layer will sync to the servers with, trying to optimize, making things as lightweight as possible on the client. Because there are certain things right now, like if you're an Overcast user and you have a lot of podcast subscriptions, sync is a very heavy operation for you. Because every time it syncs, it collects...
all episodes of all podcasts you're subscribed to and kind of sends all their e-tags to the server to say, hey, have any of these changed? And that's on every single sync request. And so as your collection grows, that's a very heavy operation. So that's one of the things I'm migrating away from, trying to make things scale better for both the client and the servers. But anyway...
Um, in the meantime, while I'm doing all that, which is going to show basically nothing to my customers for months and months and months, I'm trying to do small features in the app that will like kind of give people, you know, just signs that I'm still alive and we're still working on this app while I do the bigger stuff behind the scenes. Um, and so I added some, you know, some highly requested like menu options for, for like, you know, playlist management and stuff. Um,
I also wanted to try to tackle some longstanding issues that people had with both the new interface and with the interface in general. And by the new interface, I mean the one I launched like two years ago. It's been a while. Something like that, yeah. But anyway, so one of the issues with the new interface is that I changed for the first time in the app's history, I changed what was shown on the home screen, like what podcasts are shown there. And it used to be just a stack of like
quote, unplayed podcasts and then everything else. And I found as a user of the app, it was hard to find stuff like, like, cause I would skim past one of the sections maybe and not realize that the podcast was in the other section or, you know, whatever. And that was a common thing. People complained about all the time. So in this, in the quote new interface, I, I changed it to be like these tabs. This was like, it was like unplayed active and archive. And yeah,
These tabs, ever since I launched it, I've gotten a bunch of feedback on them, some of which likes them, most of which was like, can I just please get a list that just has all of the podcasts in it?
And I eventually wanted that myself, too, because I was hitting the same problem with the new layout. I was like, well, if I don't know whether a podcast is unplayed or active or inactive, I don't know which one of these tabs to look for it in. And so I was often either using search to get around this limitation or I would check like all three lists and that feels terrible. And so I'm like, you know what? People want an all tab.
So I just added an alt tab and I got rid of the archive tab because it confused a lot of people. And everything that was in the archive tab would now be in the alt tab amongst everything else. So I thought, this is, you know what, this is what people actually have been really asking for, for the most part. Let me try this. And I tested it with the beta group for a little while and everyone was fine with it for the most part. So I was like, all right, that sounds good. And then,
I also... The concept of unplayed in Overcast has never made sense. Because what unplayed has always meant in Overcast is not yet deleted. Which is different from unplayed. You could have an episode that is played. You couldn't in 1.0. But fairly soon afterwards in the app's life cycle, I added the ability to have an episode that was played but that you didn't delete yet. And so...
There were all these labels in the app said unplayed, but then you could literally have a played episode right below that header. And so it made no sense. And I've, that's another thing I've heard about forever. And so I, I decided, you know what, let me, let me address this issue finally too. Now I will actually change the logic so that played episodes won't show up in the unplayed sections.
And I shipped that out as 2023.1. Everyone hated it. Well, if you're one of the people who didn't, I'm sorry. But in this case, you're like a 1% or like the previous discussion. So it's like everyone else hated it. I heard from so many people, I can't find my episodes now. Where did they go? You broke the way things work. And I realized the reality is,
Oh, and also everyone said they missed the archive section. That was fun, too. But everyone loved the new all section, but they still missed archive. And I broke that. So like I broke these two these two big things for people because they were used to the other way. And and I realized, OK, I was correct that something needed to change. I was hearing from people something needs to change in these two areas. Unplayed doesn't make sense. And I can't find where podcasts are. I want an all tab.
So I fairly quickly issued a 2023.2 update that addressed those problems in two different ways. So I realized the unplayed thing, people actually had gotten used to the way it worked, and I was used to the way it worked, and I couldn't find stuff after it was played either. And so I realized that it wasn't that the behavior was wrong. The terminology was just wrong.
So I just changed the word unplayed to current, which there is no good single word that I can find that describes the status of episodes. Current is not the best, but I couldn't think of a better one. If I can in the future, I'll change it again. And so I just change everything back. All right. It works the way the behavior will go back to the way it was before. I just just changing the label so that the label now matches the behavior rather than changing the behavior to match the label.
And that was the right move. Now everyone's fine. No one's mad. Well, now like two people are mad because they like the change I made, but everyone else was fine. Okay. And then with the all versus archive versus, you know, unplayed slash current slash active, I realized what I want, and I think I should trust what I want because I made this app for myself for the most part. And so people who have come to this app usually are people who want things the way I want them for the most part, because otherwise they wouldn't have stuck with my app.
And what I wanted, I wanted the all list. I also wanted an archive list and the old active list I never used. So I just changed that section to now just be current all and archive. That seems to seems to be satisfying more people on the whole.
And it's not perfect. And honestly, when I do the big Swift UI rewrite, my plan is to actually make that customizable so that you can have whatever sections you want there. And I'll give you three or four or five different possibilities, and you can slot them in wherever you want. Because that's much, much easier to do in Swift UI than it is in my current massive UI table view or UI collection view hack.
thing. Like it's just, there's, that's very difficult for me to do now. Um, but that will be very easy for me to do in the future. And so my plan is this will all become customizable, um, among other things in the app. But in the meantime, I've settled on something that I think works better for more people in the old system.
And these were all huge risks that, you know, changing longstanding things in the app. But I'm happy that I got here. And I did have to anger some people and lose some people and get some one-star reviews along the way, as you do with any change. But overall, I think this is better for more people. Yeah. And I think there's definitely some couple lessons in that story you just told, where it's that sense of, I think...
It's important to realize that changes you're making are not typically irreversible.
that if you make a change, like that fear that I was talking about at the beginning of the episode of like that, this overwhelming sense of dread that I'm going to just like destroy everything. And it's all going to like, just go down in flames. It's like possible, pretty unlikely. Most changes, if it turns out you made you, you sort of, you picked, you got it the wrong way around that like, which side was the 1% you think if you got that wrong, like you can undo it. It's fine. You can,
Go in and undo what you change, just roll back, whatever that's going to look like for you. Usually that's possible. So don't be so precious with these changes and think that, oh, no, if I do this, I can never go back. I mean, I ran into a funny one with Pedometer recently where when you reach your goal in Pedometer, there's confetti that comes down from the top of the screen. So it's been in there for a long time. People love it. I love it. It's delightful. Everyone loves it.
I started getting beta feedback from people who were saying that the new confetti, which I'd written in SwiftUI, wasn't as fun as the old confetti. And at first, the first person who said that, it's like, oh, whatever. And then it's like, I started getting many people who were in the beta who were saying this to me. And I was like, oh, no. Like, I'm not going to mess with people's confetti. I'm going to undo this change. And so, like, now it is just the old CAE emitter view, like, UIKit emitter.
just like, you know, in a UI view representable inside of Swift UI. It's like, it is, I am not messing with that. Like, I don't want people to be like, oh, the new confetti is sad. It's like, no, we do not want to be on the wrong side of that change. So I just undid it. Like, it's fine. And,
I think there's a wisdom in that of being flexible, like pushing your app forward, making sure that you find ways to make it better. And if it turns out you pushed slightly too far, it's like ease back, back it up, change it. Ideally, you find this out when you're doing some kind of like beta test rather than when the 99% of people who are grumpy is 99% of like a few hundred people.
It's much easier to manage than if it's 99% of the entire user base. But still, like, when that happens, like, just ease up, back up, change it, and you'll be fine. Yeah. Yeah.
Yeah.
And in most cases, you're better off not rolling it back and just kind of, you know, making tweaks to be more successful rather than like, you know, undoing months of work. But, you know, the fast stuff you can undo pretty quickly and it's not that big of a deal and it's worth experimenting with.
Yeah. And I think just moreover, it's just being open to that is I think the most like what that looks like technically, like is going to widely vary and like rolling back isn't necessarily the right thing always. Sometimes it's going to be finding a new middle ground, listening to what, you know, sometimes when people are upset about a change, it isn't that it changed. It's that you're exposing some assumption they were making about the app or some workflow that you were completely oblivious to that is now being surfaced because of a change you made.
And it's like, listen is like step number one in all these things. It's like, listen, try and understand what it is, and then make the change and continue to move the app better. Don't get anchored in the past. And instead, just keep making it better, both for yourself and for your users. Yeah, and don't just make everything a selling. Yeah, that's right. Thanks for listening, everybody. And we will talk to you in two weeks. Bye.