00:00:35 ◼ ► is I think the most high-stake bit of programming I think I have ever done in my entire career.
00:00:43 ◼ ► I don't think I've ever done any kind of sort of migration or change in any of my apps that is as high-stakes to me as this particular one.
00:00:50 ◼ ► And so it seemed like something that was definitely worth the topic, both, I think it's an interesting thing to talk about,
00:00:55 ◼ ► and then I am also very eager to essentially talk through my thought process with this,
00:01:00 ◼ ► and make sure that there's nothing that jumps out to you as someone who also has 20 years of programming experience,
00:01:06 ◼ ► to, you know, that I'm doing something horribly wrong, and then also do this before I roll it out,
00:01:36 ◼ ► But what I had no concept for at that time was that the thing that people would really, really want to do
00:01:44 ◼ ► So in the first version of Widgetsmith, all you could do was point Widgetsmith to an album of photos,
00:02:00 ◼ ► But what people actually wanted was to use it like a photo album, where they choose a photo,
00:02:16 ◼ ► I'd remember just sitting down and just like doing my best to build out that feature as quickly as I possibly could.
00:02:37 ◼ ► and I sort of down-sampled it to be the size of the widget that you were going to be showing.
00:03:15 ◼ ► that that approach and that solution that I had done in those first few days of the app being launched
00:03:27 ◼ ► And there are a lot of things that I wish I could do that I can't based on my original implementation.
00:03:31 ◼ ► And specifically something that I want to be able to do is give you kind of a full zoom and pan of the image,
00:03:46 ◼ ► Or if you pinch on the picture, you should be able to zoom it in and position it arbitrarily inside of the widget,
00:03:55 ◼ ► And so people have to either pre-crop their images, or there's all kinds of weird hacks or turn things,
00:04:01 ◼ ► And it's something that really, if this is the marquee feature that people use this app for, I can do better.
00:04:06 ◼ ► And similarly, it's something that I also thought of that I think a lot of people use this widget for,
00:04:12 ◼ ► And so I wanted to use the vision framework in iOS to automatically suggest a crop for you.
00:04:18 ◼ ► So if you have a picture that involves people, you can hit a button and it can kind of automatically zoom
00:04:28 ◼ ► and a kind of a fun way to experiment with the vision framework, which I hadn't used before.
00:04:39 ◼ ► so that if you want to change your zoom or change your pan, you can do that later without having to re-add the picture.
00:04:46 ◼ ► Because the problem I have now is, all I have is this very scaled-down version of your image.
00:05:01 ◼ ► and situations like this where my old downscaling version would be, means that I can't offer any kind of obvious
00:05:12 ◼ ► So this is where I am. And so what I need to do is then go ahead and build this new version.
00:05:27 ◼ ► where I could kind of just get the gesture and get the, you know, adding a picture and all that stuff built out.
00:05:32 ◼ ► And I actually built this for WatchSmith's last update, and used it in there for the complication of the,
00:05:38 ◼ ► with the photos complication I did there, which incidentally turned out to be really complicated,
00:05:46 ◼ ► And so I ended up having to do this weird hybrid approach where some of it is a wrapped UI view,
00:05:51 ◼ ► and some of it SwiftUI, and there was a lot of pain and suffering there, but it's built now, and it's fine.
00:05:57 ◼ ► But this is the point where it starts to get complicated, and this is the point that I think,
00:06:13 ◼ ► there's a certain compatibility between them, where the old one, I could treat the old, you know, photos widget
00:06:19 ◼ ► as a sort of, a simple case of the new one, where the new one has a big image, a zoom factor,
00:06:27 ◼ ► an offset factor within that, and I could kind of, as suits, just take the old ones and say,
00:06:32 ◼ ► "Oh, you know, they're just the size of the image is smaller, and you can't zoom it in very far as a result."
00:06:37 ◼ ► And I could adjust the offset factors between them, and I could do it that way, and just sort of treat it
00:06:47 ◼ ► And this is the place where the terror begins, and the, I think, when I had one of those moments where,
00:06:53 ◼ ► I don't know if you've ever had this, where you sit down, and you're like, "I wonder if there's a number,
00:06:57 ◼ ► or something you're kind of, you know you need to look up, and you need to kind of look at,
00:07:06 ◼ ► Like, I knew I wanted to know how many photo widgets are out in production, because it's just a helpful tool for me,
00:07:13 ◼ ► to just decide this. But I kind of didn't want to know the answer, because my initial, my vague sense
00:07:23 ◼ ► And so, but I was a little bit like, I didn't want to look for it. I didn't want to look,
00:07:34 ◼ ► You know, I collect very limited analytics inside Widgetsmith, but I collect enough that I could kind of
00:07:39 ◼ ► estimate what this number probably is. And it ends up that I think there's something on the order,
00:07:45 ◼ ► this is a conservative estimate, but I think on the order of about a hundred million photo widgets
00:07:54 ◼ ► Which, exactly, right? So like, you can either laugh, or you can cry if you're thinking of changing
00:08:03 ◼ ► the code that underlines the display of something on that many people's home screens. Like, on their
00:08:09 ◼ ► phone, with them doing nothing, they could just, an update could appear in the App Store that could somehow
00:08:14 ◼ ► horribly destroy something that's on their home screen, and it could do it tens of millions, if not
00:08:20 ◼ ► a hundred million times. So like... So wait, let me pause you here for a second. I'm very glad you looked up this number,
00:08:26 ◼ ► because that was a great moment for us. However, it blows my mind a little bit, but why did you need
00:08:32 ◼ ► to know the number? I think for me, it's getting the degree of difficulty for this, right?
00:08:40 ◼ ► And I mean, in some ways, maybe you're right. I can see where it doesn't actually matter. But in the sense of,
00:08:47 ◼ ► ultimately, I need to do what is best, and if it was, if, you know, I wouldn't want to go down a path that would be
00:08:53 ◼ ► problematic for ten thousand people, any more than it would for something that would be problematic for ten million people.
00:08:59 ◼ ► But yeah, I wanted to have a sense of it, I think honestly, in some ways, to keep me honest, and make sure that I don't
00:09:05 ◼ ► cut any corners or get, like, you know, sort of get a little too fast and loose with this, because in some ways,
00:09:12 ◼ ► that's what put me in this problem in the first place, by not being thoughtful, by not going through as much
00:09:16 ◼ ► thoughtfulness as I could on this, and to make sure that I had an appropriate amount of terror sitting behind me
00:09:24 ◼ ► while I'm working on this feature, to make sure that I do it right, and do it right the first time, because it is unlikely to be
00:09:30 ◼ ► something that I would be able to sort of, like, undo or fix in a good way, if I do something kind of horribly wrong.
00:09:38 ◼ ► So, already, I mean, that number scares the crap out of me, and I don't even have any skin in this game.
00:09:44 ◼ ► Already, I'm already thinking of the approach you should take, if it were me. And if, I can tell you right now,
00:09:52 ◼ ► what I would do in this kind of context is, I would not touch a single line of code about the old widgets,
00:09:58 ◼ ► and I would just make a new photo widget type, and I would hide the old one from being available when creating new widgets.
00:10:08 ◼ ► So let the old widget type persist, and for new people who create new photo widgets, that follows a different code path.
00:10:16 ◼ ► Which is not the most, like, in the most ideal case, people with the old widget should just, it should just work,
00:10:22 ◼ ► and it should just get better over time. But the risks are so high here, as you said, I think in that kind of context,
00:10:28 ◼ ► I would just make a whole new one that's a totally separate class, totally separate code paths, and if people write in asking,
00:10:36 ◼ ► "Hey, I'd love to be able to zoom my photos or whatever," you can say, "Alright, here, just recreate the photo widget, and it'll have that ability now."
00:10:42 ◼ ► And I think that's, it's not like the most elegant solution in the world, but I think it's the most pragmatic,
00:10:48 ◼ ► because it protects you, like, nerve-wise and liability-wise from accidentally disrupting all those widgets out there,
00:10:56 ◼ ► and I think people would be fine with that. I think most people are not going to care about the lack of grace on that,
00:11:01 ◼ ► and they're, you know, this is something like, they would care quite a bit if you broke their photo widget,
00:11:07 ◼ ► but if they want some new features and have to recreate it, I don't think they're going to care that much about that.
00:11:18 ◼ ► That is a strong encouragement to me that I did the right thing in this, and especially for you to say that,
00:11:23 ◼ ► because I feel like you have a bit more, like, I think you have less of a comfort, often with sort of like the hacks,
00:11:32 ◼ ► or doing things halfway, or feeling like that, like you tend to be a very complete developer,
00:11:38 ◼ ► whereas I think my tendency is to be a bit more fast and loose, and so it's encouraging to me that you took the same approach,
00:11:44 ◼ ► because basically, yes, I had the same, my thought was, I first, like, I built out a little bit of the version
00:11:49 ◼ ► where I would take the old photo data and kind of move it into the new system, and then, and then it sort of, it worked,
00:11:56 ◼ ► but then the more I thought about it, I'm like, something is going to go wrong here, and so I just took the,
00:12:00 ◼ ► instead took the approach of, there's, you know, if the widget has the old data, it uses the old system,
00:12:06 ◼ ► and, but as soon as you, if you add a new widget, or you add a photo to a one that had a photo before,
00:12:14 ◼ ► you know, so if you have, if you, you essentially have to go through the image picker again,
00:12:23 ◼ ► so if you go through that, then you switch over to the new system, but it's like they are totally separate inside the code,
00:12:29 ◼ ► and they do nothing with each other, and if you, you know, if you did the, if you did this upgrade,
00:12:39 ◼ ► like, the old code paths are the only thing that will ever be run, because all of the new stuff is all kind of hidden behind,
00:12:46 ◼ ► if you're using the new stuff, use this, if you're using the old stuff, use that, and, as I said, it's a bit inelegant,
00:12:52 ◼ ► and I think there's the issues of user communication, but obviously now, like, even a messaging for what features are available,
00:12:58 ◼ ► or like, help screens and things, and I think that's fine, and then it's also a little bit of a sad thought that's like,
00:13:04 ◼ ► that code for the old original photos is going to be with me forever, like, there's no way for me to ever realistically expect to retire that code,
00:13:14 ◼ ► and so I'm going to just need to support it indefinitely, which is fine, I think probably, and I think obviously,
00:13:21 ◼ ► and it's mostly a question of, in the future, my big concern was making sure that I preserve a way to sort of create those widgets,
00:13:32 ◼ ► at least in a debug mode, so that I can make sure I can, when I'm doing my testing and compatibility stuff,
00:13:38 ◼ ► and making sure that SwiftUI 3 doesn't break them, like, I could do it, I'm going to end up in a circumstance where there's user data that exists in the world,
00:13:46 ◼ ► that I can't easily recreate myself, but I'm delighted to hear that you took the, your mind went to the same place of like,
00:13:52 ◼ ► nope, we're just going to keep these systems totally separate and take that approach and, you know, as best as we can, minimize the risk there,
00:14:01 ◼ ► because the risk primarily is obviously that I somehow in the switch between the old and the new, I get something wrong,
00:14:06 ◼ ► but that feels like a much lower surface area switch, because it is very clear when you're in one case or when you're in the other,
00:14:14 ◼ ► rather than if I tried to kind of have a hybrid approach or do a data migration or do something that is much more sort of high risk in that way.
00:14:24 ◼ ► And I think also I love the thought that I'm doing nothing, I'm in no way changing the data of the old method, you know,
00:14:31 ◼ ► it uses a different file, like file structure when I'm saving things, and it's stored in a different place in the user's configuration.
00:14:38 ◼ ► So all of those, the old data should persist, and so if somehow it all goes horribly wrong and I need to roll it back,
00:14:44 ◼ ► I can roll back to the old widget system and it should be completely unchanged as a result.
00:14:49 ◼ ► Yeah, that's good, because like, the last thing you want in a migration like that is for, like if you were trying to migrate old stuff to new stuff,
00:14:57 ◼ ► like so much weird stuff can happen, like if the device is low on disk space, or any of that, like one of your file rights fails in the middle,
00:15:05 ◼ ► like then there's all these weird conditions that you'd have to accommodate for in order to do that safely at a large scale.
00:15:11 ◼ ► I would even say like, I do remember back when Apple was announcing APFS, there was some remark, I think maybe Federighi might have said on the talk show.
00:15:28 ◼ ► Yeah, about how like, for the previous few releases of the OS, one thing they would do as part of the upgrade process was basically test a migration to the new file system they were developing,
00:15:42 ◼ ► And so like, I feel like if you were going to do that kind of migration at this kind of scale for something so important,
00:15:48 ◼ ► you would have to do something like that, where like, you would test a migration to a new system without actually removing the old data.
00:15:57 ◼ ► And being super careful about it to first make sure that all the data migration stuff succeeded and everything,
00:16:04 ◼ ► and wrap the whole thing in a giant try catch block, and just do everything possible to like, okay, try to do this,
00:16:23 ◼ ► Anyway, we are brought to you, talk about figuring out when you have problems. We're brought to you by Pingdom from SolarWinds.
00:16:30 ◼ ► While you've been listening to this podcast, how would you know if your website had gone down?
00:16:39 ◼ ► You might stumble across the problem by luck, just by like hitting refresh on your site occasionally,
00:16:46 ◼ ► You need something to tell you everything is running smoothly on your site, and more importantly, you need to know when it's not.
00:17:04 ◼ ► Whether you're a one-person company or a Fortune 500, you need real-time alerts about critical website issues,
00:17:11 ◼ ► and customization of how you're alerted, whether it's SMS, email, your team's collaboration apps, push notifications, whatever it is,
00:17:20 ◼ ► They even track and analyze your website's load time so you can see what's affecting the user experience.
00:17:31 ◼ ► Go to Pingdom.com/RelayFM right now for a 30-day free trial with no credit card required.
00:17:48 ◼ ► So I think for the rest of the episode, I think what might be interesting to talk through is some of the approaches I'm taking now.
00:18:07 ◼ ► And it's sort of the strategies that I'm coming up with to minimize those and the things that I'm doing in the run-up to this release,
00:18:14 ◼ ► and the way I'll test it, and the way I'll beta test it, and so on, because I think that's--
00:18:27 ◼ ► A lot of times we as developers, we see some flaw in the way things are, and we think, I have to fix that.
00:18:47 ◼ ► But do we? Do we really have to fix it? I feel like so often we don't ask that question of ourselves enough,
00:18:55 ◼ ► but a lot of these things, like, if the current thing is working, we probably shouldn't go messing with it.
00:19:01 ◼ ► Or it's not worth the risk, or it's not worth the effort, or it's not worth having to rewrite something
00:19:15 ◼ ► So often we pour tons of effort into something and take lots of risks in order to clean something up
00:19:23 ◼ ► or modernize something that the reality is, just leaving it alone would probably have been more pragmatic,
00:19:29 ◼ ► and at the end of the day, you're spending a lot of effort modernizing something that your customers
00:19:35 ◼ ► And so I feel like it's very important to ask ourselves that kind of question much more than we do now.
00:19:42 ◼ ► Because I'm facing all sorts of possible migrations and rewrites and everything that are on my wish list/to-do list,
00:20:17 ◼ ► and the realization part where I was realizing the scope of the challenge that I was undertaking.
00:20:24 ◼ ► And I think it's a very good point and a good reminder, because I think it is definitely an area to be very thoughtful,
00:20:30 ◼ ► and make sure that if you're undertaking a dangerous migration or something that has a high-risk coding situation,
00:20:44 ◼ ► Isn't something that is going to make the app better for the end user, not better for you.
00:21:04 ◼ ► Because ultimately what I'm doing is I'm making software to make people's lives better, to make them happy.
00:21:09 ◼ ► And if I am something that's just like there's this one little thing in it that annoys me,
00:21:17 ◼ ► and in the end I ruin someone's day because they had their widgets all set up the way they loved them,
00:21:22 ◼ ► and I do something that messes with that, those are disproportional. Those are not good things.
00:21:35 ◼ ► And ultimately what I ended up on is I did it and then I've been using it myself on my own phone for a while,
00:21:45 ◼ ► And it makes them better in ways that I, now that I sort of, it was one of those features that now that I've done it,
00:21:52 ◼ ► Because I love that when I add a picture of my kids to my widget, I can zoom in nicely on them,
00:22:11 ◼ ► is if I have pictures of my family and friends and us on vacation or places we've been,
00:22:16 ◼ ► and being able to frame those pictures exactly the way that I would want has made the app better.
00:22:22 ◼ ► And so I don't think I could go back, but I think what you're saying is definitely a good important thing
00:22:27 ◼ ► and something that I think I'm glad to have gone through that thought process of who is this for
00:22:38 ◼ ► that it's ultimately a reason that I think will make people's, my users' lives better as well.
00:22:42 ◼ ► So yeah, and then I think in terms of the things that I'm doing now that I've like here is,
00:22:58 ◼ ► when you're in a process like this, but is I'm loading it onto lots of different devices
00:23:16 ◼ ► Thankfully with this, I don't have to deal too much with old OSes because everything requires iOS 14,
00:23:24 ◼ ► But it's like doing a lot of that and in some ways also I'm just sort of doing lots of data testing there,
00:23:56 ◼ ► But instead of gathering that user data and having those snapshots to be able to do testing with,
00:24:01 ◼ ► I think it's something that's always useful, so you have these baselines to install the new version on
00:24:21 ◼ ► That was just a feature I dreamed of and it doesn't actually exist, but hopefully it does.
00:24:44 ◼ ► I set it up so that it would run a lot of my vision stuff and the new pan and zoom and all that.
00:25:09 ◼ ► You can have these weird cases where the vision framework gives you back some weird value
00:25:16 ◼ ► It's important to test it on lots of different things because it's trying to find a face.
00:25:27 ◼ ► And so you get back a really weird-shaped face or something that when you try and zoom into,
00:25:33 ◼ ► if you don't handle it correctly, or if it's a face that's right on the edge of the frame,
00:25:37 ◼ ► you may accidentally try and over-offset and set the center to somewhere that doesn't exist.
00:25:47 ◼ ► a testing tool that lets me do that and lets me do it in a manual way that is high velocity.
00:25:55 ◼ ► And the reason I'm not using something like unit testing there is the nature of what I'm doing.
00:25:59 ◼ ► It isn't like there's a precondition and a postcondition that I can easily just put in code
00:26:08 ◼ ► It's like when I hit the zoom to faces option, does it zoom to a pleasing crop of this person?
00:26:28 ◼ ► which is very aesthetic and not something that I need to write another machine learning model,
00:26:39 ◼ ► And then I think the next thing I'm going to do is I think this is called chaos monkey testing,
00:26:46 ◼ ► But essentially I just always do this kind of round of testing before a feature, especially a UI feature,
00:27:21 ◼ ► And especially this is great to have someone who doesn't know the app or like your kids or someone be like,
00:27:29 ◼ ► because that's a good way to find these weird edge cases that you may not find otherwise.
00:27:45 ◼ ► and install it, add photos, make sure their old photo widgets don't get broken in the process.
00:28:21 ◼ ► And then my last little mitigation strategy is I'm certainly going to use the App Store Connect
00:28:36 ◼ ► and it slow rolls out so that you can expose this to a relatively small percentage of people initially,
00:28:43 ◼ ► and then watch like a hawk the customer support and all the places that people might talk about it
00:28:54 ◼ ► which is something I've also done in the past where I can send it out to half a percent of people
00:29:11 ◼ ► and mitigate the fact that this is, I think, I don't think it's an exaggeration to say,