mergeconflict238 James: [00:00:00] Absolutely terrified. Frank: [00:00:13] Ooh, I liked this intro. This is provocative. Um, I don't even know where to go with this. I don't think anything actually scares you. Uh, where are you terrified? James: [00:00:24] Well, Frank, not only because it is the night before the inauguration, that's actually not one terrifying. Surprisingly, I thought, I thought you were going to guess that, to be honest with you, are you terrified of COVID? Are you terrified of protesters? Are you terrified of overthrowing Congress? Frank: [00:00:42] I am so blocking everything out right now that like, I'm like, I'll deal with Wednesday when Wednesday comments. I am not going to think about it until then. James: [00:00:51] That was the way to do it. Well, by the time this podcast comes out, we will see what happens. I think everything will be fine. That's what, that's what I'm really hoping for. But that's not why I'm terrified. I am terrified because I finally. Did it, Frank, I finally have finished the conversion of Island tracker to a completely freemium app. This is the final chapter in our saga. It's happened, Frank: [00:01:19] uh, part 22 of the 25 parts. I got, I, I doubt this is the end. How exciting. Um, should we do a quick recap because I'm trying honestly, to remember what your last pricing model was. If I can remember it was. I thought it was freemium. And then you pay so that you could have more friends and now you've decided that wasn't good enough. And you changed your pricing model yet again, James: [00:01:45] that's what you thought I would have done by now. Cause it's almost been a year, but Frank, no, I am still a completely paid application, which you know, as we've talked about on the podcast is the best form because you have to do absolutely zero work to do anything into your app. And if they buy the app, they get the app and it's great. Frank: [00:02:04] Yeah. You know, I've been having this debate, you and I have discussions after the podcast sometimes where I just keep on complaining about pricing models and all that stuff. And we've even talked about my own apps and how to switch to the freemium model, different ways we could break it up. Um, you know, basically it's, you know, time trials are, you remove features or you add features, you know, it's those kinds of decisions. But in the end, James, I'm just like, Gosh, I really like pay up front apps because, well, obviously, mostly from the developer's perspective, it's so much simpler, but I also think, I don't know, man, I'm, I'm a little tired of subscriptions personally, myself. And so I'm projecting that onto all of my customers also. Um, so I'm, I'm kind of in the upfront pricing mode still mentally. So this is fascinating. You decided to continue on though with the modern. 2015 freemium model. Yeah, James: [00:02:59] I continued on with just keeping it a bulk set price and just going ahead and making it as it is. And now when we started the application, I think I put it off for two 99. I believe that's what it was. And. I'm pretty sure that's what it was. And that's Frank: [00:03:17] a price model that I don't love. Remember everyone, I always say charge a lot James: [00:03:24] and you're correct. I think that is the right way of going about it. And I have had the application out for a while. I've done a lot of presentations on it. I've learned a lot. I've been updating the application with new versions, like doing testing on it. I've been, you know, just sort of, you know, using it as a case study because it is one of the first applications that has this backend has this front end. You know, it's doing quite a lot of synchronization back and forth and it's been pretty good and it's been steady. Like I get some sales here and there, but I never really got that, that like, Oh, I'm selling, you know, even one a day or two a day. I think I would be happy because now I'm like, okay, this is great. And. At some point, the people will stop playing the game and, and that's fine, but people are continuously buying this game. It's in the top 10 sellers every month for Nintendo switch. And I'm like, you know what? I want to get more people onto it. And I don't think I'm going to get it by charging and I've, and I've adjusted the prices I've gone down to 99 cents some weeks up to dollar 99, up to three 99, just kind of moving it around and saying, well, Does it does the price matter? And to be honest with you, it seems as though the price doesn't matter at all, based on what I have. So had it been four 99, I think it would have been the same amount of sales, to be honest with you. Frank: [00:04:46] Yeah, it's a little unfortunate, but, um, without like very strong marketing or other external forces apps are generally hit or miss, um, a lot of people are going to be interested in it and they're going to find it one way or another through, you know, whatever terrible advertising I personally would do or your amazing advertising James. Um, they would find it that way. Uh, but the fact of the matter is you kind of know that first week, how good an app is going to do, um, You get your most sales in the very beginning. And then the rest is a long tail. So you see where that settles out and all that stuff. The. Trick is always, um, some markets like free apps, some markets don't. So that's the great test you're doing here. So if you go from two sales a day to a hundred or 200 downloads a day, that's something that means there is a really interesting market here. You're not capturing it because you're a free app, but at least there's some people there. The scary part is if you go to free and only get two downloads a day, Because that means you haven't found a market or the market hasn't found your app. You know, one of those directions is wrong and, uh, you got some work ahead of you. James: [00:06:02] I agree with you. That is one of my. Biggest scares, I would say. And how I look at it right now is I put up my cadence, the application I did over the holidays and that application, which is while it's free has, um, probably in two, I guess it's been like three weeks or so three and a half weeks, four weeks a month has already more than doubled the amount of like total installs and almost a year of Island tracker. Yeah. Frank: [00:06:32] Yeah. See that that's a hit, you can tell when an app is doing well. Um, because. I hate to say it. And I get made fun of by a lot of very smart people out there. Hey Joseph. Um, if the app is good you'll know, right away with minimal marketing and then good marketing, we'll just push that higher and higher and higher. And so you can maximize that. Um, but. Have you, do you want to talk about what has happened since the switch or you haven't actually made the switch James: [00:07:03] yet? That's a great question. So I'm this entire weekend. Um, I had a three-day we had a three-day weekend here, um, and I, you know, Sat down. And I said, you know, I think it's the time. The time is now. And I, I was actually writing the code and testing the updates and doing everything in my house. My heart was kind of pounding. I think I've never been more scared about a potential change in my application. Cause you have to remember that this application, you know, it hits a backend service. You know, the idea is that. For the, if you have a pro version or a paid version that you get everything unlocked and what you, what that means is that you get unlimited friends in the application. You can also submit different types of data in the application to your backend to share more data with your friends. Um, and you know, that part of the functionality has always just been unlocked, um, in the application. Now moving over to a freemium model means two things. The first thing means that I have to implement freemium in my application. I think 2021 is a good in-app purchase year. I feel strong about. Now that people are going to buy the in-app purchase by the way. But that, that, that like that the testing has improved and the API APIs have improved. And I feel good about my library. I feel good about the Google stuff that they've recently put out. Like all the libraries are updated. It's very minimal code to do. Non-consumable. In-app purchases, which are in-app purchases in which the user buys them once. And they own them forever. That there's the difference between consumable and non-consumable consumable would be like, I buy coins and I can continuously buy coins and, um, you know, March my army people across the screen and do things. And those are, those are evil, but the, the upgrade to pro scenario is not subscription. Right. It's the easiest form of in-app purchase monetization beyond this is a paid application paid application. You do nothing. And then non-consumable I think is the next easiest, because they've either purchased it or they haven't purchased it. And there's really no two, two ways around it, to be Frank: [00:09:20] honest with you. Yeah. Yeah. You've basically built a trial model and it's annoying because we all want Apple to do a trial model, but they won't give us a trial model. So they say. Do this, they say, do exactly what you're doing. And that's good news. What you're saying is the technical side of it has gotten pretty reasonable. So you have all your test accounts working and you have all that stuff working. Um, yeah, it, it always comes down to that hard problem of, okay, so now what's in the free version. Um, and how do I promote the pro version? I think in your last app, you put a star to promote the pro version, but it gets into trickier bits too. Like how do you word that in the app description? Because, um, you have to be very clear about which parts you're actually getting with the free version of the app, and then which parts are going to have to pay extra for, to get all of that. So while I've. It's good that the technical side is reasonable. It's still a lot more design work, like more meta app development work that you have to think about. But I like it. I like your trick. It's it's mostly just a binary. Yes, no, um, free or pro, but you still have some flexibility in the pricing for that we've talked about. Um, you could do like a one week consumable, so, um, 10 cents for a one week trial, um, $5 for a year, maybe $15 forever, you know, but that would be a more subscription model. So what you're saying is you don't really want to do the subscription model. You're just saying, um, I'm basically doing a pay for app, but I'm giving you this free version first. James: [00:11:04] Yeah. I give full, pretty much full functionality. But what I've done in the UI at least is I have a little button on the home screen that says Island tracker pro. And when you click on that, you can check your status to see if you already have it, check what features are in it. And then optionally, that is where you would buy Island tracker pro. You could, um, you always have to have a button that says, you know, restore purchase because it's super important because Apple rejecter app, if you don't do that, and you want your users to restore their purchase, even though. When you, if you were to buy in in-app that purchase and then the user buys it again, it won't let them buy the same thing twice. It's like against the rules. So it will just return the thing, but you want to, do you want to do the right thing anyways? Um, on there. So I've, I've added that in there and promoted there. Now I will say this. The biggest differentiator is that with the free version, which is every version you can have only two friend connections. Okay. Okay. And in the paid version, you get 15. Um, th that, that opens the door for ultra prohibition, which Frank: [00:12:14] could be more, you led me into that and I'd even see it coming. Good job. Good job. Um, it sounded like a little earlier though, you were a little bit worried about your servers. Stuff, you had kind of a cool server setup. If I remember correctly, you were using like a lot of Azure functions to the point where you're only basically paying for usage, which is fantastic. So no overhead. And then you had, I forgot what you were using for the data store, but again, you were paying for it on a usage basis. Is that all still true? James: [00:12:46] All still true. I have, um, table storage and Azure functions. All in there. Those that's my jam at this point, I would say it's like, it's definitely doing well. And here's where things get tricky because I have all of the data in there already. I have all the user data they've been using it back and forth, blah, blah, blah, people that have purchased it. And I always here's the fearful part. Right? The fearful part is not actually adding the in-app purchase or changing the in-app purchase. The, the. Problem. And the fear is how do I migrate all of my existing users that have already paid the app to pro and then how do I update my, I always get fearful when I publish a new backend. I just feel like it's going to break everything. And like, I'm just so fearful when I publish a new backend, even though. Azure functions and is amazing. And I'll tell you how I tested it. It's a magical and Azure functions is amazing, but like I'm always just fearful. It's like, well, I need to literally publish this thing and change some things and make it happen. Like what's going to happen. Um, and those are my fears is like I have to deploy a new backend and I need to update. I need to update all of my existing users and roll out the app update. At the same time on both platforms and toggle the backend switches on my application. So they're free. And then the server knows, like not to do that, but then my server also needs to be upgraded against. You know today, even though I might publish the updates next week, and then anyone that buys the app today, they need to automatically upgrade to pro like you need to con you know, consider these things in your application. It's like, I'm very fearful. That's scary. Frank: [00:14:44] Yeah, absolutely. And this is the reason I never changed pricing models in my abs. It's because of this, you would think it would be easy to tell that someone installed a paid for version versus a free version, but it's not a, you have to set up receipt validation, which means you have to have a server out there, which means the user has to be connected to the internet. You have to query their receipt. And I don't even know like. Is there a number in the receipt validation? Can you even find out if it was the free version or the paid version? Well, what you can at least find out is the date. And then from the date, you can make the right decision. But absolutely that is a, that's like real software engineering, man. You're supposed to have a dev ops team and that's something ops team and another ops team to help you handle all this switch over. It's supposed to have hot switching. Yeah. Um, but, uh, okay. So what are the practical solutions to this? Um, are you sending a different query string? At least when people are registering account to let, uh, the server know if they're using the free version or the paid version. James: [00:15:52] Okay. So. Here's the secret. Here's the secret sauce, Frank. So I sort of had thought about creating a pro version a year ago or so like, w like right when I released the app, I was like, well, what if I release a pro version of the application? Well, what's going to happen then? And we talked about this, cause we've been sure I have, I literally have a branch called freemium and it's been sitting there for like eight months and I just finished it. Right. And. So here's what I did. There's two parts. There's the app. And then there's the backend. Okay. Now the backend is the end all be all because it is a, it is the one that is going to either allow people to make more friend or question more friend requests or deny or deny them. Right? Like here's the thing is if you put that logic in your application, then. Not everyone may have the same version of your app, but if your backend code is saying, Hey, okay, I'm getting information about this user. Let me look up to their status. Oh, they're pro they're allowed to have 15 friends. Oh, they have 10 friends. Okay. Allow them to accept or, you know, accept this friend. Um, But if you put it in your application, then you know, you gotta make sure that logic is correct. Like, how is that validated? You know, not that it can be spoofed, but it just feels not as clean. Whereas normally obviously enabling and disabling user interface is something that you would do inside your application. So if there's an area that allows the user to see more information or update more information, well then of course, if, if they've purchased the thing, show it to them else, don't show it to them. So let's start with the application. Now, the application itself. How I always do a mode or this is how I did it in my cadence. And as I did in other applications, as I literally have a bully and Frank and I have a Boolean inside of my preferences that says is pro and that, and that is either true or false. I mean, yeah. I don't know how else you're going to store it, but it's literally a bullying, right? Frank: [00:18:00] Yeah. Um, the only extension to that I've ever done myself as I have, and. Um, an integer that I'm putting a bit pattern in if I want to store some things, but I want to make sure they always get stored together. It's almost transactional, you know, instead of having 20 bullions, I would pack them all into an integer, but a hundred percent that that's the way to go in the simple case, because you can do a Boolean because you're pro or not pro that's the trick. Um, But that's it sounding like that's just a mirror of what the server says though. So at some point you're going to have to query the server and say like, Hey buddy, or you're going to have to run the Apple or Google thing of, um, restoring James: [00:18:41] purchases. Correct? Yeah. So that would be the other thing is, is imagine someone uninstalls your application. This is the other scenario, right? Is, is that the user uninstalls, your application, reinstalls it, or gets a new phone? Then they get your app, the purchase isn't there. They need to restore the purchase. How are you going to validate that when the user never purchased the in-app purchase, but paid for the application? Right? That's the, that's the conundrum. So here's what I did is is this when the user registers for an account that creates a, an entry in the backend table storage, right? That's just my user account. And you can assume that right now, every single user. That is in the user account is a paid user because there's no free version of my application. So that is a known statistic. Is everybody that's. There is a paid user and is pro already now inside the application though, Frank, when I rolled out the last update, which was like eight months ago, which was a long time ago that I did this, I actually put in. So the application in is pro setting and on startup of the application, I set it to true. Frank: [00:19:59] Yeah. Yeah. Yeah. I can see where you're going with this. I mean, it's very hackable, but okay. Continue. James: [00:20:06] Okay. So I set it to true right away, right. Because the Boolean is hackable by, by default. Right. It's just it's there. Yeah. Or, Frank: [00:20:14] or is paid, whatever you want to call this thing, paid, paid, whatever you paid for it. Yeah. James: [00:20:19] Yeah. Is paid the, they paid for the app. Right. And in this app update, I actually updated my backend. To allow to allow this bullion to be like sent to the server for validation. Like when I'm doing certain HTTP calls, I send this additional information to the backend, just for knowledge. I don't do anything with it, but I'm just like, I'm just going to have it here just in case. Okay. Now here's the advantage of you. Can't just create the bullying and like have the default true. You actually need to set, you need to set it because it needs to be set. And this is helpful because. As soon as I make the version free in the app store, the user is going to open the app and they're already going to have the pro account, right. It's just going to be pro because they already had pro and everything will work, but this doesn't work. If the user, for instance, on installs the app or gets a new device, they, that will not be true by default. That will be false. Right. So it's no good. So here's what I did is. I've created a way that in my user database. Okay. There is a another table that is my pro user table. Okay. And this pro user table is just the identifiers of the user. So when someone buys the application today, they buy the application, they get the, I get the receipt from Apple or Google. I pull it down. I validate that receipt. I can, then I validate the receipt on my backend. I can then put that information into that table. So now I have like this mini store, this database that says all of these users purchase this application and here is their, their receipt. You know of that, they purchased it and that's valid. So now if a user buys the in app purchase inside the application buys pro on installs the app. Pulls it back. They just, they don't even have to hit restore. They can just say, Hey, go to the server and grab it. Right. Doesn't matter if you already purchased it. If you bought the in-app purchase, it was free. If it was paid, just go check the server to say, Hey, did this user do this? And this is actually really cool because I just built in this instance, a cross-platform mechanism of enabling someone to buy it on iOS and then going to Android. Signing into their account and then saying, Hey, restore my purchase. And now you have the paid version of both. This is how every single service works, right? If you were to buy a pro version and it's tied to your account like of Netflix, you don't have to buy Netflix on two different operate, two different OSS. You buy it once and it's logged into your account. So now I have this account store that also has this data associated with it. Frank: [00:23:16] Oh, aren't user tables. Wonderful. They solve so many problems. Yeah. Um, this is that great. But, um, if you are in control of your backend, you can solve a lot of problems by putting that data on the server and using that as the source of truth, then you don't have to worry about any of that security stuff. So like you said, it can either. Query Apple servers, or I can query yours. I like that solution. In fact, I was getting a little bit nervous while you were talking, because it was sounding very complex there for a little while, but to make it clear, you just have a flag in your user table that just says, is this the pro version? And the app is able to query that and that's absolutely the way to go. If you have user accounts and that's certainly going to help you here, the flag. Isn't perfect. So, um, you're still gonna have this timing issue of when do people actually download the new app and all that kind of stuff. So you're gonna want to really force out this update. That's actually querying the server for that information to otherwise the timing stuff is going to get nasty. James: [00:24:20] Well, Frank, let me tell you a little secret of what I did, which makes us even more scarier is that inside of table storage, there is a magical button that says export. Frank: [00:24:32] Okay. Uh, okay. James: [00:24:34] And what this does, is it exports the entire table? Here's all of your users and here's all of your data. So fence Frank: [00:24:42] now, if you're not storing passwords and clear texts James: [00:24:45] now, no, everything is, it's all just squids and whatever things. And there's even some information like Nintendo switch codes, but they're all encrypted. So I can't even see them because they're in the data. Um, I could probably. I don't, I don't even, I, it would take me so long to figure out how to reverse my own algorithm, that I wouldn't, it wouldn't be worth it for a number that doesn't do anything. And it's, you know, for me to send someone, a friend request that I have yet they are, but I created a different table. Okay. So instead of just adding a bullion or a, a number of string, true or false to my existing user table, I created another table. So here's what I did is I exported my entire user table. I deleted a bunch of data. So now all I have is my partition key Roky, and I have a receipt field, which is a string and I just filled it in with pre-pro like, Hey, yeah, pre-pro means that they purchased it before the pro version came down. And then I imported that Excel spreadsheet, the CSV file into the pro status table. So I just pre-populated every single, so every single purchase of the paid version is now in my database. Frank, it's all there. Frank: [00:25:57] I love the cloud, so yeah, that's good. First normal form database thing you just did there. Good job. Yeah. I always say add a column, but in database theory, Uh, creating a table referencing another table is the same as adding a column. So whatevs, however you want to talk about it, whatever makes you happy. Uh, the trick. Yeah, no, um, Uh, as long as that user table, isn't moving very fast. You can do something like that import export, but if people are registering every minute on your app, I think the problem gets a lot harder. So I think you're just a little bit lucky that you can probably pick a time of the night where you can do that. I don't know though. I'm still a little bit confused. What if they still have the free version? Huh? Um, so Frank what's scenario, I'm trying to think of here. James: [00:26:48] Let me tell you about a feature flag, because I thought about this. So one beautiful part of Azure functions that is just sitting on top of app service, which means that you can use app configuration. Okay. Oh boy. So you liking it. So I have a feature flag that says auto pro. I have two features. One is auto pro and one is enabled pro. Now auto pro is genius. What auto pro does is it says whenever anyone installs the application and creates their account, Automatically add an entry into the pro status table automatically. Okay. And then I have another feature flag that basically is like Inforce pro. So look to see, you know, two versus 15, right? Like take a look at that data. Um, and in that case, um, right now auto pro is turned to true and enable. Pro is set to false. So like right now, everyone who was buying the application, when they create their account, they just automatically get added to the pro table. It's good to go. And everything is gravy. Now Frank: [00:28:01] let's just be clear though. Uh, where are these feature flags? Are these on the server or in the app or are they mirrored with each other? James: [00:28:08] These are inside of Azure portal inside of the Azure function configuration. In your Frank: [00:28:14] function. So in your API access. Okay. That is James: [00:28:17] correct because the server I've made the server in charge of enforcing those friend requests. Okay. And the server is the one that will validate their pro status or not. Okay. So the app doesn't do that because then it gets really fishy. Now I have one source of truth, which is my table sores. It says these are my pro users because I add an entry whenever people purchase the inept purchase or auto right now, automatically doing it. Um, um, and then I have a user table, right. And, and friend requests. So right now, whenever people are doing friend requests will. I'm not enforcing any, any limit. There is a limit there's 10 right now, actually there's a limit. So I'm actually increasing the limit of friends to 15. Um, but you know, I am for all intents and purposes, making the server the end all be all. So when I say publish to my iOS and Android app, I will then make it free and then turn on pro mode in my backend server. And that takes about five seconds to redeploy, to my backend. Yes. No, maybe. So did I lose Frank: [00:29:37] you? Nope, I think I got it. I think I'm still just a worried about store propagation, but it's probably not an issue. I was just reviewing in my head, like how fast is store propagation these days? It's like an hour or two. So I think it's fine. Yeah. That'll probably work. James: [00:29:53] Here's the, um, is that what I would have the problem? So a few things, by the way, is one, I can manually add promo codes because if someone contacted me and said, Hey, I just bought this, but you made it, blah, blah, blah, or it's, you know, blah, blah, blah, free, whatever. I can just go add a row. Like I have an email button that literally sends me the information that I could then manually update there. And this would be a problem is let's say I was selling. My application, like a few an hour, right. And even one an hour, like that would be problematic in this case, but I haven't sold any in like five days, Frank, so it's totally fine. Right Frank: [00:30:27] free. Now it's free. There's going to be millions of people. Everyone go download it tomorrow and can happy. James: [00:30:33] But soon as I hit that free button and I say release, right? So what I've done is on both Google play and the app store, I'm doing a manual release. And when you do a manual release it propagates near instantaneously. Frank: [00:30:47] Oh, good trick. Yeah. Life hack. I like that. Is that true on Apple? Yeah, James: [00:30:52] it is. Yeah. I pretty much validated it. Like, if you want your application to like, be faster, I mean, it's not instantaneous, but it's a lot faster as I've realized that. When you do a manual, it like, has everything prepped basically. And same with Google. There's now a manual release mode. It's called something different. It's on a per app basis and it is called. Um, when you go into an app, it is called publishing overview, and then it is called managed publishing. And it's actually a very different, it's actually not great. Great. Because what it does is manage publishing is for anything that you do in your application. So that means beta testing. It means changes to your store listing. It means anything like adding a new photo, so it's good and bad, but you almost have to plan it. So like right now, I'm like doing all of my beta stuff, getting all my in-app purchase stuff ready, and then I will queue up a manual publishing. And then at the same time, make it free, publish the button, do it on iOS, toggle my backend and hope everything works out. Okay. That's Frank: [00:31:59] going to be an exciting night. Are you going to live stream that you better live stream? James: [00:32:02] That I'm terrified and that's what I'm terrified about, but you know, I was more tired about testing this, right? Because I've had a backend. That is literally sitting there in Azure for eight months working fine. Frank: [00:32:18] And then. Sorry, I'm just going to interrupt. I've had table storage on Azure since like 2002 and it just runs, I don't know. I don't know who's running it. I'm not running it, but it runs, so I have no doubt, like your code's going to be fine. Like that that's something I just would not worry about. Um, but I guess it is, it's a lot of permutations to test, I guess, like, is, is your problem because you have the feature flags and you have the two scenarios before and after the feature flags. So like four-ish things to test. James: [00:32:52] Yeah. And I have some new functions that I'm publishing. Like I have one to like create a pro status, get a pro status, um, back and forth from the application. And, um, that my app is going to call and do some things on. And, um, I didn't want to like deploy the backend live beforehand. And then, you know, each of my Azure functions has an API key and I'm like, what if I publish an update? And the API key changes, which by the way, it doesn't. You know, like it doesn't at all. Frank: [00:33:23] Okay. So I might've been missing a part of this story that you're going to stage the function update also. James: [00:33:29] So the function Frank, I published a yesterday and one of my, let it bake a little it's baking. It is getting Frank so hard right now and I needed to test it real. I needed to test it alive and, um, It's live it's happening. I'm looking at the data right now. I'm looking at the functions in the backend. The new ones are being called. I just literally tested it from my phone because I need those functions to be ready. So that's why I have that auto pro mode because I'm like, well, what if I ch you know, who knows when Apple approves it? You know? Cause I need it. That's the thing is I need the function keys to put in the application. And I can only get the function keys if I publish the backend, it's a chicken and egg situation, right? Yeah. So, um, I don't want to make them anonymous. I want to have some additional layer of security on it in some weird way. I'm just in my head. So I needed to do this. I needed to get it out there. And I, I was also like, well, I want to test all of these different backend services and yes and no and back and forth. So what I did is I created. A new Azure function for internal testing. Like I just created another one that I could test in my emulators and simulators locally and change the feature flag. So what I did is I put like zero max friends. Right. So I'm like just, you don't have it. You're not allowed to have any max friends and make sure like the server return stuff. So I just deployed a whole new backend. Just only for me, only for me, for me to get them my magical little world and it's glorious and I just set everything to anonymous. Like just whatever it's. Cause it's just for me. And. Frank: [00:35:13] This, this is correct. Like you're, you're supposed to have a dev production environment. So like the same setup as production, but for dev. Yeah. So like that's fine. Like everything you're saying, I think you're doing a little haphazardly and be like, you know, that's why like, um, Docker, because he can just have. Take your, uh, or I should be saying like Docker compose. So you can just say here's the list of my services, duplicate it for dev, but whatever you're doing it, the manual way. It's cool. But I think it's proper, like you should be doing that. James: [00:35:44] I think so. Yeah. Um, Frank: [00:35:47] I would think so. What's your Mark hashtag. Yeah. I'm excited for you because I've never had the guts to make this change specific. Well, You, you also had a little easy, cause you have user accounts and your, you did the right thing of making the server, the source of truth and Apple servers, obviously. Um, for other apps, I still think it's a bit hard because they still have all the same timing issues. And I think you still come down to receipt validation of some sort, otherwise you're never going to get your bullion flags and your dates. Correct. I think at some point you just have to ask Apple servers what the heck is going on with this purchase. But, um, I honestly think it's going to go perfectly. It sounds like you've been a good engineer here. James: [00:36:31] I've been testing it back and forth and test flighting and upgrading and, you know, testing all this additional backend. And I think I got really lucky because how I sort of tune this in and set that Boolean flag for existing users. And sort of planned it. I planned it eight months ago, Frank and I sort of just thought about it. It was sending data back and forth. Now, if I was to go back in time, I would have done all of this pro data table all ahead of time. Right. Because it would have been ready to go. It would have just been feature flags and it would have been a beautiful thing where I just like set the app free. I turn on a backend feature flag and then boom done, you know, might update the application. So like you can buy the in-app purchase, but literally it'd be that. I think the scariest part is that I. I wish I had just put in a few more feature flags, added a few more API APIs that I, you know, were there. Here's the cool part about Azure functions by the way, is in my, in my test environment. And I know that the API keys didn't change because I've updated the backend many a times and it doesn't change, but there's always that thing in the back of your head, that's like, But what if, like, what if the API has changed, right? Like they're not going to, but what if they do so now? No, Frank: [00:37:47] no, not what if I, what I released in a purchase for Coca, I totally screwed up my API access keys, and no one could buy the in-app purchase every time they did, the app would crash. Oh no. Wow. What a good programmer Frank is? Huh? There you go. That you ha you should be afraid, James. That's all I'm saying. James: [00:38:08] Okay, good. I should be athletes and I am absolutely terrified. So one thing I did in my Azure function tasks, this was cool as I, I, I first. Created my new, like internal testing, my dev environment, and it's completely separate URL, completely separate table storage, everything I deployed my main branch. I just deployed that one, which is the current live one in general. Then what I did is I deployed my freemium brand, which would be my new backend with the new functions and code changes. Frank: [00:38:43] That's smart. You want to test the migration? Make sure I can start from the one and get to the other. Yeah, James: [00:38:48] exactly. And I had one application, like I had one app that was the existing application, like on an Android emulator. I had another Android emulator, which was like the new version. So I guess that's the old version with the new server backend and the new version with the new version and also test upgrading the old app to the new version. Like I did the whole test matrix. Right. Um, And, but then what I did is I said, okay, now I've updated to the new path. The keys are the same. I'm like, what if something does go wrong? Like what in a, in a world, what, what would happen here? So what I did is I just changed a function to anonymous. Okay. And that gets rid of the API keys cause anybody can call it and. I did that. And in my application could continue to call that method. So it continued to work, which was really great actually, is it was just like, boom, you know? Okay. This works. So then what I did is I turned, I went from. Function auth no auth anonymous and then back to function off. And the key was the same before and after toggling anonymous, like it knows to keep that same exact key before and after. Like there's a, literally a button that you have to press in the portal. It says like generate new key. Frank: [00:40:07] Yeah, I think Azure has been pretty good about that. And it's usually great out and it's like, are you sure? And all that stuff. Yeah. That's good to know. It's a little accident proof and all that sounds like you're really confident, James: [00:40:24] terrified. I, I, I did, I did literally tweet that says, um, what was my tweet? Uh, it was like the other, it was kind of got a bunch of retweets. Sorry, go ahead. I said, as I'm doing a full migration from a paid app to freemium map is one of the scariest things I've ever done. I did just upload my new backend table storage updates, feature flags. I'm feeling good. The app is approved Frank. So I'm feeling pretty good. Receipts are being validated. I, I got a little scared because I did my first receipt and that. That string, that Apple gives you is quite large by the way. Oh Frank: [00:41:01] yeah. Um, couple kilobytes. I would guess James: [00:41:05] it is the command on, I think it's almost nine or 10 kilobytes. Wow. Frank: [00:41:10] Yeah. And it's all encrypted or is it plain text? James: [00:41:14] It is a it's. It's. It, it's all Sinead, it's a random numbers and you'd have to send that thing to you. Send that thing to Apple it's 64 bidding coded or whatever, but that string was quite large as well. That's a large. And I was like, well, what happens if I get huge receipts is my backend going to blow up because they can only be so big, but luckily table storage, a string can be 64 kilobytes. And that is quite a large string. Frank: [00:41:43] Uh, yeah, I've, I've actually run into that limit before I was like, gosh, darn it. Wasn't uh, uh, bill Gates it's six 40 K would always be enough. Well, 64 K a good I've actually never done Apple receipt validation. That's why I was just guessing there. Um, I've just avoided it because I hate running my own servers, but it's good to know that Azure functions can do it because then you're not maintaining a server. You just have a little chunk of code out there. Yeah, James: [00:42:12] I guess. Yeah. Jonathan peppers wrote a LinkedIn, my in-app purchase, um, repository. Um, he wrote the Azure functions to do the receipt validation for Boda, both Apple and Oh, Frank: [00:42:26] nice. So you don't even have to write it. Okay. Thank you. We said that to the podcast so I can get to that. James: [00:42:34] It's pretty cool in that regard because it's. Just Dan, here it is, you know, and done. So Frank: [00:42:40] yeah. Well, I went the other direction with Kalka. I went from freemium to pay and that was so much easier because you don't have to worry about anyone if they were lucky enough to get up before now, if I was being cruel, I could like, uh, disable a bunch of features when you're going from freemium to pro, but I decided to. Just being nice. Um, past sales are past sales. So ignore that if someone had bought the free version, all of a sudden they got the pro version, that was just honestly, me being a little bit lazy, but also, um, uh, someone who got a free app, isn't going to take kindly to you all of a sudden requiring that they pay for it. I hate it when apps changed their pricing model in that direction. So I wasn't going to do that to any customers and it was much easier. Your direction of going from the pay to the free it's a, it's a much more perilous slope than the other one. So I'm excited and I really do hope you'll Twitch stream it because it's fun to see that kind of server coordination happen. James: [00:43:40] Yeah. I'm, uh, I'm a little worried, but it will be okay. Frank: [00:43:45] Also. Don't, don't worry. I, I, I'm not kidding. When I said people who paid for my app made the app crash. Like people got over it, you know, it was, it was pathetic and sad, but it honestly didn't affect sales at all. It was just more of a professional embarrassment. So as long as you can swallow your pride, just, just smile and go for it. Yeah. James: [00:44:09] I feel as though I'm waiting for the weekend, like I want to, um, I want to basically wait until. I don't know if it's Saturday morning or Friday night, I don't want to do a midweek and then be like, Oh my goodness. Right? I, I might do it next. I don't, I don't know what I'm going to do it yet. I need to just do it and get it over with, but I don't know. Frank: [00:44:31] Um, some candles say some prayers. Yeah, you gotta, you gotta do all the right incantations to make sure this deployment goes correctly. James: [00:44:40] Yeah. So that's it. That's why I'm terrified, Frank, and I hope that it all goes okay. But I have done, I've done so much testing. I feel so good about it. Um, You're not trying to Frank: [00:44:51] be positive. See, that's what I'm, I'm just encouraging you. I'm saying go for it, James. I think we're all encouraging. You. You've been talking about it for eight months, James: [00:45:01] your honor. In your honor, I, I increase the price of the in-app purchase, so, Oh, Frank: [00:45:06] did you actually say the prices James: [00:45:08] during the show? It was two 99, but right now I just increase it to three 99 for wow. Frank: [00:45:13] Oh, big bucks, big bucks. James: [00:45:16] I know. Frank: [00:45:17] This is a pretty specialized market. I'll be interested to see what your free sales are. James: [00:45:23] Me too. I'm I'm quite fascinated. So anyways, I'm, I'm, I'm worried and excited and hopefully my backend is, does things, I don't know, it's, it's all happening, Frank. And I'm just very excited because I've been talking about this app for so long. And, you know, I think when you release an app and you, you. You think there's a lot of potential in it and then you don't get the momentum behind it. You're trying to figure out why aren't you getting momentum behind it. That was trying to figure out. And I don't know. Right. I think that people have used and people are using, I see the backend update. People do use it often. So the question became like, Hey, is this the barrier of entry? And we will find out if this is, and maybe I'll come back in two or three or four or six months and say, franca did all that work and worried so much. And. I literally shouldn't have all and I should have just kept it as a paid version or maybe I'll I'm rich and I've retired. Who knows, Frank: [00:46:20] or maybe you can send out user surveys and pay marketing. Salespeople, you need a Salesforce, you need people cold calling people and getting them on the higher end tracking market. James: [00:46:32] Okay, Frank? Sure, sure. Frank: [00:46:36] Can you see why I'm such a bad marketer? James: [00:46:39] And on that note, let's end this emerge conflict recording. Um, so I think that's gonna do it for this week. So until next week, I'm James wants a Magnum Frank: [00:46:48] and I'm Frank Krueger. Thanks for listening.