<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Unrushed Apps Blog</title><description>Insights and project updates from Unrushed Apps.</description><link>https://unrushedapps.com/</link><item><title>Hello World from UnrushedApps</title><link>https://unrushedapps.com/blog/hello-world-original/</link><guid isPermaLink="true">https://unrushedapps.com/blog/hello-world-original/</guid><description>Welcome to the original Unrushed Apps blog! An introduction to Samiul, a solo developer building apps with intention.</description><pubDate>Sat, 28 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hi 👋 I am Samiul – a solo developer from the UK. Welcome to my blog 🎉&lt;/p&gt;
&lt;p&gt;I have always wanted to make mobile apps, and recently I decided to just go for it. I quickly created an MVP of an &lt;a href=&quot;/app-pause&quot;&gt;app&lt;/a&gt; idea I had in mind and published on Google Play Store. But turns out Play Store shows the developers name and full address on the app details page, which seemed quite odd from a privacy perspective. The only way to avoid it was to publish as a company. And thus, Unrushed Apps Ltd was born 😀&lt;/p&gt;
&lt;p&gt;While created as a mean to privacy, I am totally enjoying the feeling of entrepreneurship. It has been a while since I felt such strong sense of creativity. Not only I am building an app, I am now also building my own solo-startup. It comes with lots of responsibilities, but somehow it doesn’t feel like a chore. It feels fun!&lt;/p&gt;
&lt;p&gt;For the last 2 months, I have been spending my 5-9 to build my app and grow my presence as UnrushedApps. It has been a fun journey. My 9-5 feeds my stomach, but the 5-9 feeds my heart ❤️.&lt;/p&gt;
&lt;p&gt;I hope to document all my learning and adventures on this blog, cause it would be shame to forget the exciting times.&lt;/p&gt;
</content:encoded></item><item><title>App Pause – 3 Months Progress Report</title><link>https://unrushedapps.com/blog/progress-report-app-pause-3-months/</link><guid isPermaLink="true">https://unrushedapps.com/blog/progress-report-app-pause-3-months/</guid><description>A reflection on the first 3 months of building and growing App Pause as a solo developer. Covering MVP release, user feedback, and lessons learned.</description><pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I hit 500 total downloads yesterday, just a week shy of &lt;a href=&quot;/app-pause&quot;&gt;App Pause&lt;/a&gt;‘s 3 months Birthday. I know it’s not a huge number and compared to other apps that go viral, the growth has been very modest, but it still feels like a big deal to me. I have come far considering I started from scratch as an App Developer.&lt;/p&gt;
&lt;p&gt;When I started 3 months ago, all I had an idea.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No MVP.&lt;/li&gt;
&lt;li&gt;No app.&lt;/li&gt;
&lt;li&gt;No website.&lt;/li&gt;
&lt;li&gt;No company.&lt;/li&gt;
&lt;li&gt;No social media presence.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Today, I have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An app with 500 downloads, 180 MAU and 40 DAU.&lt;/li&gt;
&lt;li&gt;I am now director of a Private Ltd Company called “Unrushed Apps Ltd”&lt;/li&gt;
&lt;li&gt;I have my own website, &lt;a href=&quot;/app-pause&quot;&gt;landing page&lt;/a&gt; and a blog with 0 subscribers.&lt;/li&gt;
&lt;li&gt;A threads &lt;a href=&quot;https://www.threads.com/@unrushedapps&quot;&gt;account&lt;/a&gt; with ~300 followers.&lt;/li&gt;
&lt;li&gt;More experience. I have made quite a few mistakes, so I feel wiser now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But how did I reach here? What went well and what went bad? This is the reflection on my journey.&lt;/p&gt;
&lt;h2&gt;Things that went well&lt;/h2&gt;
&lt;p&gt;Luck is always a factor and I would be lying if I denied the random fortunate events. Few places where I got lucky:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick MVP creation and its release&lt;/strong&gt;: My first git commit was on April 23 and I published my app on May 20. So around a month to complete my MVP AND publish it live on Google PlayStore. That was quick. The journey wasn’t smooth and there were multiple challenges, but it could have taken much longer easily and had it done so, my motivation would have drained quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bypassed PlayStore Internal Testing Policy&lt;/strong&gt;: I &lt;a href=&quot;/blog/publish-on-play-store-uk-company&quot;&gt;created a private ltd company&lt;/a&gt; to mostly hide my personal information on Google PlayStore page (didn’t feel comfortable showing my name and full address to millions of people). Turns out, if you have a corporate account with Google PlayStore, you are not required to do fullfill Google Play’s policy of getting your app tested by 12 internal testers for 14 days. That saved a lot of time!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Got Early Genuine Feedback&lt;/strong&gt;: Right after publishing my app, I &lt;a href=&quot;https://www.reddit.com/r/androiddev/comments/1ksv4xx/my_first_android_app_app_pause_is_live_surprises/&quot;&gt;posted&lt;/a&gt; on Reddit to promote it. It didn’t get much traction – just a single comment from one user. But this single person gave my app a try and sent very detailed feedback by email. Next day, another user emailed me with detailed feedback. So right off the bat, I got two kind users who gave me detailed feedback for improving the app. That helped shape my road map as continued adding more features and polishing the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Early Positive Feedback&lt;/strong&gt;: I got 8 five-star reviews for my app very quickly (within a month). That was motivating. I haven’t been getting a lot of reviews since then though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Building in Public&lt;/strong&gt;: Right before publishing, I opened a threads account to promote my app. After few posts, the algorithm started showing me accounts that were “building in public”. I got inspired by them. These folks were friendly, so I asked them questions on comments and they answered. Learned a ton. I started doing the same with my app and quickly built up a following base (as of today, 293 followers).&lt;/p&gt;
&lt;h2&gt;Things that didn’t go well&lt;/h2&gt;
&lt;p&gt;Let’s take a look at all the mistakes I made.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AdSense Account Suspension for Silly Reason&lt;/strong&gt;: Got my AdSense account blocked. I wasn’t even trying to show ads 🤦‍♂️. I just needed to use Admob’s UMP SDK for consent management to handle GDPR and they suspended my account (for life) due to “suspicious activity”. Bruh. You can appeal it once and I already wasted my chance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I have since then switched to a different CMP SDK and my advice to new app devs would be to stay away from Admob UNTIL you have significant userbase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;User Data Corruption During Update&lt;/strong&gt;: Botched up a database migration during v0.11.0 release and impacted 26 users. You can read more about it &lt;a href=&quot;/blog/post-mortem-database-migration-error&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Didn’t Market with Trackable Link&lt;/strong&gt;: At one point, I suddenly got a surge of new users, but I didn’t have a clue about the source. Learned the hard way about using UTM sources for creating trackable links.&lt;/p&gt;
&lt;h2&gt;Things where I need to improve&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Improve the landing page&lt;/strong&gt;: Even though I don’t get a lot of traffic to my website at all. Is there any point? I guess it’s better to stay prepared in case I start getting more visitors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track Referrals Better&lt;/strong&gt;: I still have no clue where I am getting all my users from, other than organic PlayStore traffic. I have been using UTM sources with my link, but the traffic has been so little that PlayStore reports them as “Others” on the dashboard. Perhaps I can improve the accuracy somehow? Maybe integrating with Play Referral API would do the trick?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Add more features&lt;/strong&gt;: Spend more time in development instead of worrying about marketing. Marketing really hurts the pace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;More Marketing&lt;/strong&gt;: Launch it on more sites. I have been feeling shy about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monetisation&lt;/strong&gt;: Add a tip jar such that Google gets a cut. I hear they tend to promote apps that earn them money.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep on Writing on my Blog&lt;/strong&gt;: Write about my lessons on the blog. Try to build up a newsletter on the side (why not?). I currently have 0 subscribers. If you are reading this, maybe consider signing up? 🙏&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep on building in public&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Publish on Play Store as UK Company</title><link>https://unrushedapps.com/blog/publish-on-play-store-uk-company/</link><guid isPermaLink="true">https://unrushedapps.com/blog/publish-on-play-store-uk-company/</guid><description>My journey of creating a UK Private Limited Company to publish App Pause on the Google Play Store while maintaining personal privacy.</description><pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When it was time to publish my MVP for App Pause on Google Play Store, I had to make a decision: publish as an individual or a company. Publishing as an individual meant my full name and address would be plastered across PlayStore for everyone to see. This made me feel a bit uneasy, so I explored the alternative – formation of a company.&lt;/p&gt;
&lt;p&gt;Turns out, creating a company in UK is relatively simple. The bare minimum requires simply filing up a form online and paying just 1 GBP! Now, I am not going to go into too much details on exactly what is needed in this post. There are plenty such guides online. I am just going to provide a brief summary of my journey in creating a Company.&lt;/p&gt;
&lt;p&gt;If you create a company, then PlayStore will show the company name instead of your actual name. However, there is still a challenge that remain unsolved – PlayStore will also show your business address. If you use your home address as your business address when creating the company, then that will be shown here.&lt;/p&gt;
&lt;p&gt;In order to hide your home address, you will thus need to use some alternative address when forming the company. Unfortunately, it’s not legal to use PO boxes for this address. The registered office address must have physical location where documents can be delivered and acknowledged. It’s going to be expensive to rent an actual office, but thankfully, there are cheaper options.&lt;/p&gt;
&lt;h2&gt;3rd Party Service for Buying Address&lt;/h2&gt;
&lt;p&gt;There are 3rd party services where you can buy registered office address. These locations are fully stuffed with people to accept any letters that arrive there and then they email the scan copies to you.&lt;/p&gt;
&lt;p&gt;I used 1stformations to buy their &lt;a href=&quot;https://www.1stformations.co.uk/package/privacy/&quot;&gt;Privacy Service&lt;/a&gt;. This package comes with a registered office address and a service address – the address connected to the director of the company. Without a service address, I, the director, would have needed to provide my home address for a place to contact with me 🤦‍♂️. So I had to buy both a registered office address and a service address.&lt;/p&gt;
&lt;p&gt;All in all, I had to spend around 70 GBP to buy the addresses. It’s going to be an annual cost. Sigh.&lt;/p&gt;
&lt;p&gt;Note, these address will only forward legal letters to you, so letters from Company House and HMRC. Any other kind of letters would NOT be forwarded to you, unless you buy another of their service. It’s all on their website. Have a look.&lt;/p&gt;
&lt;h2&gt;Creating is easy – Maintaining is a bit harder&lt;/h2&gt;
&lt;p&gt;Well, now that I have a company, what are my responsibilities? I was worried about this, but turns out this is also simple.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create account on Company House and enable all online safety mechanisms.&lt;/li&gt;
&lt;li&gt;Create a Corporation Tax account (you must do this within 3 months of company creation)&lt;/li&gt;
&lt;li&gt;If you collect any kind of user information, you will also have to pay money to &lt;a href=&quot;https://ico.org.uk/&quot;&gt;https://ico.org.uk/&lt;/a&gt; annually.&lt;/li&gt;
&lt;li&gt;Keep track of key events for the company decisions. I am just tracking them on Google doc.&lt;/li&gt;
&lt;li&gt;Keep track of all money in and out. I don’t have a bank account yet cause I am not dealing with any transactions just yet (only spending money occasionally). Once I do, I should also start using some kind of accounting software. Everything is manual for now.&lt;/li&gt;
&lt;li&gt;File paperwork annually: Confirmation Statement. I haven’t done them yet, but I don’t think they will be complicated either.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;AI to make things easier&lt;/h2&gt;
&lt;p&gt;Throughout my journey, I have been asking Gemini questions to unblock me. Got an HMRC form to fill and unsure about some question? Juts take a screenshot of the form and ask Gemini what to answer. Most of the time, it can explain the question and what’s needed accurately. Everything about this process is public so AI has plenty of information to help out with your questions.&lt;/p&gt;
&lt;h2&gt;Publishing on Google Play Store&lt;/h2&gt;
&lt;p&gt;To publish on Google Play Store, I just had to wait one day after creation of my Company. All UK company automatically get a DUNS number, which is what you need to create a company account on PlayStore. Then I just had to pay 25 USD and my account was good to go.&lt;/p&gt;
&lt;p&gt;Now I have complete privacy. On top of that, as a completely pleasant surprise, turns out you do not need 20 testers for 14 days if you have a company account on Play Console! This made releasing app much easier!&lt;/p&gt;
&lt;h2&gt;What’s Next&lt;/h2&gt;
&lt;p&gt;I haven’t faced any problems with my company yet. Then again, it only has been 2 months. I will wait a year before writing another update to this post.&lt;/p&gt;
&lt;p&gt;Interested in following my journey? Please Subscribe to my monthly newsletter or give me a follow on &lt;a href=&quot;https://www.threads.com/@unrushedapps&quot;&gt;Threads&lt;/a&gt; where I build in public. Have a question? Drop a comment 🙂&lt;/p&gt;
</content:encoded></item><item><title>App Pause – 6 Months Progress Report</title><link>https://unrushedapps.com/blog/progress-report-app-pause-6-months/</link><guid isPermaLink="true">https://unrushedapps.com/blog/progress-report-app-pause-6-months/</guid><description>A 6-month update on my solo dev journey with App Pause. Exploring high Day-0 churn, revenue validation, and device fragmentation challenges.</description><pubDate>Sun, 23 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;3 months ago, I posted my 3 months progress report &lt;a href=&quot;/blog/progress-report-app-pause-3-months&quot;&gt;here&lt;/a&gt;. I am back with a 6-month update. The second quarter was much better than the first, though I am still facing high Day-0 churn (~60% uninstall rate).&lt;/p&gt;
&lt;h2&gt;The Numbers (Month 3 vs Month 6)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Month 3&lt;/th&gt;
&lt;th&gt;Month 6&lt;/th&gt;
&lt;th&gt;Diff&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Downloads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;2,210&lt;/td&gt;
&lt;td&gt;+ 342%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MAU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;180&lt;/td&gt;
&lt;td&gt;487&lt;/td&gt;
&lt;td&gt;+ 170%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DAU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;140&lt;/td&gt;
&lt;td&gt;+ 250%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Revenue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;$111&lt;/td&gt;
&lt;td&gt;&amp;gt; 999%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Here is the breakdown of what happened:&lt;/p&gt;
&lt;h2&gt;What Went Well&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Focused More on Dev:&lt;/strong&gt; In Month 3, I realized organic ASO was vastly outperforming my manual social media efforts. So instead of spending time on social media promotion, I decided to try and reduce my high Day-0 uninstallation rate. This worked out just fine since my daily download has now tripled from 10/day to 30/day (unfortunately, I didn’t manage to drive down the uninstall rate).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discord Server:&lt;/strong&gt; I noticed some of the apps nowadays provide support on Discord. This felt much superior than email to me. Chatting is faster + if the community is large enough, sometimes users can just help each other. Obviously everyone doesn’t have discord, so I still provide email support. The Discord server is just an alternative feedback platform. It has grown to 12 users now and frankly, I am very happy with the engagement of the community. I am getting bug reports and user feedback more frequently now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PlayConsole Review Time:&lt;/strong&gt; I don’t know what happened, but Play Console now takes around 30 minutes to approve my releases. It used to take 24 hours before. Thanks to the fast review, I can now easily push hot-fix for critical bugs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Helpful Users:&lt;/strong&gt; I am happy to say that I have some power users of the app who don’t hesitate to contact me whenever they see any bugs. Really grateful to them since they manage to catch critical bugs. For example, after just 14 hours of a release, a user reported that my “Stop Service” no longer worked. I fixed it quickly. That day, I had the highest amount of uninstall (36) and if the user didn’t report the issue, it would have taken me multiple days to notice the problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Revenue Validation:&lt;/strong&gt; I know that my app is useful, but is it useful enough for people to pay? Ultimately, that’s the end goal for me: to make enough money so that I can say goodbye to my 9-5 job. My app is far from complete, so I decided to start with a tip jar first.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I integrated with RevenueCat and created a “Support Me” screen. Users could buy me coffee/lunch/dinner with various prices. I had 4 users who bought me coffee/lunch. This was a huge milestone. My app finally made some revenue.&lt;/li&gt;
&lt;li&gt;After a month, I finally added a premium feature: Multiple Profile support. I rebranded “Support Me” screen to “Pause+” and started a subscription model. I know that some people hate subscription so I also kept a “Lifetime Purchase” option.
&lt;ul&gt;
&lt;li&gt;Anyone who bought me cofffee/lunch/dinner, were upgraded to the “Lifetime” plan.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Happy to say that I now have: 2 monthly, 1 annual and 6 lifetime subscribers.&lt;/li&gt;
&lt;li&gt;I made a total of $111 so far.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;High Feature Velocity:&lt;/strong&gt; I managed to add quite a few features to my app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Major Features: Import/Export, Scheduling, Accessibility Service, Multiple Profile&lt;/li&gt;
&lt;li&gt;More features: Delay Pause Screen, Multiple Substitute Apps, Quick Switch, Ask Every Time, Breathing Exercise, Auto Close Pause Screen, App Usage Limits&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What didn’t go well&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Device Fragmentation:&lt;/strong&gt; I underestimated how differently OEMs handle background services.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I wasted weeks trying to debug user reports blindly because I didn’t have the hardware. I eventually had to buy a second-hand Samsung device just to reproduce and fix a specific &lt;code&gt;UsageStats&lt;/code&gt; lag bug.&lt;/li&gt;
&lt;li&gt;I don’t log to logcat in production, so it was hard to debug user issues. I solved this by implementing a local file-logging mechanism. Now, when users send feedback, they can tick a checkbox to “Attach Debug Log”. This context was the only way I managed to solve complex background service crashes.&lt;/li&gt;
&lt;li&gt;I implemented few more ideas to make the UsageStats based monitoring service work, but in the end, it didn’t work consistently on certain devices. I added “Accessibility Service” support as an optional alternative. This reduced uninstall rate a bit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Subscription Shock:&lt;/strong&gt; My uninstall rate was steady at 50% but spiked to &lt;strong&gt;60%&lt;/strong&gt; when I introduced the subscription screen. Users see a paywall and immediately get their guard up, even though most features are free. I need to fix this onboarding UX.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Complex UI – Poor UX:&lt;/strong&gt; I added “Multi-Profile” support, but it confused users (including existing DAU) so much that uninstalls spiked again. I had to build a specific “spotlight” tutorial just to explain the UI.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Focus on Development: Add more features. I am a developer, that’s my first instinct.
&lt;ul&gt;
&lt;li&gt;Website Blocking, In-App Component Blocking (Youtube Shorts, Instagram Reel, Weekly/Hourly usage cap, Strict Mode: Hide stop button, restrict changing pause settings, prevent app uninstallation – More UX improvements&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Focus on ASO: Improve PlayStore Listing by adding a video, better screen shots and machine translations for few other popular languages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole journey has been humbling. I have come far but I can still see a long road ahead of me. If I can continue to develop my app at this pace for another 18 months, I think it can turn into a great one.&lt;/p&gt;
</content:encoded></item><item><title>Initial Setup of Unrushed Blog</title><link>https://unrushedapps.com/blog/initial-setup-of-unrushed-blog/</link><guid isPermaLink="true">https://unrushedapps.com/blog/initial-setup-of-unrushed-blog/</guid><description>A look behind the scenes of setting up the original Unrushed Apps blog on WordPress, including domain, hosting, and plugin choices.</description><pubDate>Thu, 03 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Right after creating a blog and writing Hello World post for it, I like to write a post on the efforts behind setting up of the blog. It serves both as a guide for others and a documentation for myself that I can refer to months later, just to compare the before and after state.&lt;/p&gt;
&lt;p&gt;Here are the details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain:&lt;/strong&gt; Porkbun. I have been using them for a while and simply love them. Simple, reliable and cheap! They also have free email forwarding, which is what brought &lt;code&gt;contact@unrushedapps.com&lt;/code&gt; to you folks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosting:&lt;/strong&gt; Cloudways. A bit on the expensive side to have a dedicated server for wordpress blog. A shared hosting would have done the job. But I have another blog running on it already, so I am now just sharing the same hosting among my two blogs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme:&lt;/strong&gt; &lt;a href=&quot;https://themeforest.net/item/stratus-app-saas-product-showcase/13674236&quot;&gt;Stratus&lt;/a&gt;. I could have used a default free theme. I should have. But their demo page showcasing app looked beautiful so I ended up buying it. To be honest, I would probably go for something else next time. Something that’s lighter and faster. I am stuck with this for now since I don’t want to spend more money on the theme. Let’s focus on the content for a bit shall we?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugins:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Elementor:&lt;/strong&gt; Came with the theme.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sitekit:&lt;/strong&gt; Plugin by Google for Google Analytics. Pretty good.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rank Math:&lt;/strong&gt; for SEO.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complianz:&lt;/strong&gt; Cookie policy and Cookie popup for GDPR.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smush:&lt;/strong&gt; for optimising images.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wordfence:&lt;/strong&gt; for security.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;wpDiscuz:&lt;/strong&gt; for a better commenting system.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UpdraftPlus:&lt;/strong&gt; for backing up blog regularly on Google drive.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legal Pages:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cookie Policy:&lt;/strong&gt; Complianz plugin created on for me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Privacy Policy:&lt;/strong&gt; Created for free at &lt;a href=&quot;https://privacypolicy.cookieyes.com/&quot;&gt;CookieYes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Terms &amp;amp; Condition:&lt;/strong&gt; Complianz has another plugin for this. Used that.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s it. Took me roughly three weeks to get everything set up. I am not really happy with the speed and performance of the blog, but I don’t think I can do much to improve it with this theme. If I write consistently for a year on this blog, I will switch to a lighter theme next year.&lt;/p&gt;
</content:encoded></item><item><title>Measure Conversion Rate of On-Boarding Flow in Android</title><link>https://unrushedapps.com/blog/measure-conversion-rate-of-on-boarding-in-android/</link><guid isPermaLink="true">https://unrushedapps.com/blog/measure-conversion-rate-of-on-boarding-in-android/</guid><description>A guide on how to implement and measure an onboarding flow in Android using HorizontalPager, Firebase Analytics, BigQuery, and Looker Studio.</description><pubDate>Mon, 04 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/app-pause&quot;&gt;App Pause&lt;/a&gt; didn’t have an on-boarding flow until recently. It took me a while to implement since I wanted to measure conversion rate for it. The purpose of on-boarding is to make things easier for users, and if I don’t measure it, how would I know if on-boarding is actually helping or not?&lt;/p&gt;
&lt;p&gt;Well, at least that was the intent. To summarise, I implemented it in the following 4 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an on-boarding flow&lt;/li&gt;
&lt;li&gt;Fire custom events for each step&lt;/li&gt;
&lt;li&gt;Export data from Google Analytics to BigQuery&lt;/li&gt;
&lt;li&gt;Visualise data on Looker Studio.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is what the data looks on Looker Studio at the moment:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/src/assets/images/blog/onboarding-funnel.png&quot; alt=&quot;On-boarding funnel visualisation based on 3-days data&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I will now explain each of the step above in more detail.&lt;/p&gt;
&lt;h2&gt;1. Creating an On-boarding Flow&lt;/h2&gt;
&lt;p&gt;I wanted to use library to make things easier for me, and while I did find few, they didn’t match my exact vision, so I ended up implementing my own flow. Frankly, it wasn’t that difficult.&lt;/p&gt;
&lt;p&gt;On a high level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I created a screen on compos and put a &lt;a href=&quot;https://developer.android.com/develop/ui/compose/layouts/pager&quot;&gt;HorizontalPager&lt;/a&gt; in it.&lt;/li&gt;
&lt;li&gt;Based on the page number, I loaded different composables, where each step is one composable.&lt;/li&gt;
&lt;li&gt;Created next and previous button that updated the page number.&lt;/li&gt;
&lt;li&gt;Added some animation between page switch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And done. Rest of the logic is embedded in the composable like how you would create normal screens. I used a single ViewModel across all 5 steps.&lt;/p&gt;
&lt;h2&gt;2. Firing Custom Events from each step&lt;/h2&gt;
&lt;p&gt;If you are building an Android app, you are most likely using &lt;a href=&quot;https://firebase.google.com/docs/analytics/get-started?platform=android&quot;&gt;Firebase Google Analytics&lt;/a&gt; already. If not, maybe consider it? Firebase, by default, collects some generic events from your app, e.g. screen views.&lt;/p&gt;
&lt;p&gt;We just need to use that same framework to fire some custom events. Google analytics has &lt;a href=&quot;https://support.google.com/analytics/answer/9267744?sjid=9554177627591733017-EU&quot;&gt;limits&lt;/a&gt; to how many custom events you can fire, but the limits are pretty generous (500 per app user).&lt;/p&gt;
&lt;p&gt;I decided to create generic custom event called “onboarding” with specialised parameters to differentiate them:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Param Name&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;action&lt;/td&gt;
&lt;td&gt;start, step_viewed, completed, skip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;step_name&lt;/td&gt;
&lt;td&gt;welcome, how_it_works, select_app, permission, ready&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This way, I can cover multiple events (on-boarding start, complete and view of every step in between) with just one custom event. Here is the code snippet for the method I used.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fun fireOnboardingEvent(action: String, stepName: String?) {
    Firebase.analytics.logEvent(&quot;onboarding&quot;, Bundle().apply {
        putString(&quot;action&quot;, action)
        stepName?.let { putString(&quot;step_name&quot;, it) }
    })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Exporting data from Google Analytics to BigQuery&lt;/h2&gt;
&lt;p&gt;GA4 showed me count of every “onboarding” event, but what I needed to see was count of the params. To see breakdown of params for a event, you have to first “register” the param on the GA4 dashboard. It felt a lot of work tbh.&lt;/p&gt;
&lt;p&gt;Instead, I simply exported all the data to BigQuery where it’s much easier to query such a thing. Exporting was as simple as &lt;a href=&quot;https://support.google.com/analytics/answer/9823238?hl=en#zippy=%2Cin-this-article&quot;&gt;linking bigquery integration&lt;/a&gt; from the admin panel. If you are using Firebase, there should already be a Google Cloud project ready for you to use, so it’s quite simple.&lt;/p&gt;
&lt;p&gt;The advantage of BigQuery:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s free to store 10 GB of data each month. For small apps like mine, it’s very unlikely that I will hit such amount of data storage.&lt;/li&gt;
&lt;li&gt;On the stored data, you can run queries. 1 TB of data process is free each month. Again, unlikely that I will hit such a limit.
&lt;ul&gt;
&lt;li&gt;For my app with around 100 weekly active users, one of the custom query was taking around 200 KB.&lt;/li&gt;
&lt;li&gt;BigQuery actually provides you estimate of how much data a query might use, so that’s also useful. Always check that before running a query.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You can configure it such that when limit is hit, the query just fails, rather than incur cost.&lt;/li&gt;
&lt;li&gt;You can delete old data to make space for new ones.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you export the data, it takes around 24 hours to see the first table. Each day’s data is stored in a table named &lt;code&gt;event_YYMMDD&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;4. Visualization on Looker Studio&lt;/h2&gt;
&lt;p&gt;Once the table was created after 24 hours, I was able to then connect BigQuery with Looker Studio, which is free to use. Here is what I created for the on-boarding event flow.&lt;/p&gt;
&lt;p&gt;To create this, just do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Insert a table&lt;/li&gt;
&lt;li&gt;Filter events based on name “onboarding”&lt;/li&gt;
&lt;li&gt;Add dimension: “Event Param Name Value” so that a column gets created for each value of the event&lt;/li&gt;
&lt;li&gt;What should be the value for each row? That’s decided by metric and we can simply select record count.&lt;/li&gt;
&lt;li&gt;Finally, go to “Styles” of the table and convert it into a bar chart instead of table.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make things easier to digest, I also created three score card to highlight the three values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total users starting the onboarding&lt;/li&gt;
&lt;li&gt;Total users who completed onboarding&lt;/li&gt;
&lt;li&gt;Completion Rate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The instructions are a bit hand wavy, cause I don’t really want to write up a tutorial on “how to use Looker Studio”. There are plenty available for that. This article is for showing what’s possible and getting you interested in learning Looker Studio. It’s all drag and drop tbh and once you get used to it, pretty simple to visualise the data in your own way.&lt;/p&gt;
&lt;h2&gt;How I used the data&lt;/h2&gt;
&lt;p&gt;Originally, I didn’t allow skips in my onboarding flow. I noticed how users were dropping out from the first step and without skip, the only way to drop is to uninstall the app. Maybe users didn’t want to go through onboarding at all or maybe didn’t trust me enough to grant me permissions? Maybe the on-boarding flow was hurting the user rather than help them?&lt;/p&gt;
&lt;p&gt;Instead of pissing off users and getting my app uninstalled, I decided to add a skip button. A skipped onboarding is much better than uninstall.&lt;/p&gt;
&lt;p&gt;What I should be ideally doing is A/B testing here instead of just adding a skip button. I have never done A/B testing before, so for now, I just added the skip button to stop the leak. In future, I will start using A/B testing to fine tune the process.&lt;/p&gt;
&lt;p&gt;At least now I have some numbers that I can try to improve. I finally have a baseline.&lt;/p&gt;
</content:encoded></item><item><title>[Post-Mortem] Room Database Migration Gone Wrong during Android App Update</title><link>https://unrushedapps.com/blog/post-mortem-database-migration-error/</link><guid isPermaLink="true">https://unrushedapps.com/blog/post-mortem-database-migration-error/</guid><description>A detailed look into a database migration error that impacted users during an update of App Pause. Lessons learned on Room embedded fields and recovery strategies.</description><pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I started using Room database for &lt;a href=&quot;/app-pause&quot;&gt;App Pause&lt;/a&gt;, I knew it was just a matter of time before I messed up the migration one day. I hit the milestone this week after releasing v0.11.0.&lt;/p&gt;
&lt;p&gt;I am writing this post-mortem of the incident so that I can review this next year, along with all the other mess ups that I am sure will happen. We need to share our failures just as much as we share our wins.&lt;/p&gt;
&lt;h2&gt;Problem and its Discovery&lt;/h2&gt;
&lt;p&gt;My wife calls me to tell: “Hey! Your app isn’t working anymore.” I run to her to check what’s happening. She shows me the screen with a button: “Use for 0 minutes”.&lt;/p&gt;
&lt;p&gt;Yup, that was a bug 🐞 I went into the app and found that all values are either 0, false or null for per-app settings, even though she doesn’t use that feature. I immediately knew that my update had gone wrong. I must have made some error in the migration logic that corrupted her data.&lt;/p&gt;
&lt;p&gt;Got really heart broken. Just that morning I was celebrating hitting 100 WAU on Threads, and now this! I already have a high uninstall rate and this was just gonna make things worse.&lt;/p&gt;
&lt;h2&gt;Reducing the blast radius&lt;/h2&gt;
&lt;p&gt;After mopping for 5 minutes, I started to think if there was any way to stop the roll-out for the new version. Even though I released the new version 12 hours ago, not everyone received it yet.&lt;/p&gt;
&lt;p&gt;I remember reading about &lt;a href=&quot;https://support.google.com/googleplay/android-developer/answer/16285429?hl=en&quot;&gt;halting roll-out&lt;/a&gt; in one of Google Play’s recent updates, so I searched for it. I found the button on console and pressed it.&lt;/p&gt;
&lt;p&gt;Halting reduced the blast radius, but didn’t change the fact that there were damage. 26 devices had received the update already. While it was bad, at least rest of the 140+ devices were now safe.&lt;/p&gt;
&lt;h2&gt;Root Cause&lt;/h2&gt;
&lt;p&gt;I started digging into the code to figure out where I went wrong. So here is what I was doing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Entity(
    tableName = &quot;per_app_settings&quot;,
    ...
)
data class PerAppSettings(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    ...
    @Embedded
    val appConfig: AppConfiguration? = null
)

data class AppConfiguration(
    val waitingPeriodInSeconds: Int,
    val gracePeriodInMinutes: Int,
    val appUsage: AppUsageSection, &amp;lt;--- New
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the recent update, I added a field to the embedded object &lt;code&gt;appConfig&lt;/code&gt; called &lt;code&gt;appUsage&lt;/code&gt;. I first altered the table to add a column for this new field, and then populated it with a default value (“BARCHART”) since it’s meant to be non-null.&lt;/p&gt;
&lt;p&gt;The latter part was the bug and purely my fault for not understanding how embedded fields worked in Room database. I thought since &lt;code&gt;appUsage&lt;/code&gt; is non-null, I must put non-null value in it. Wrong!&lt;/p&gt;
&lt;p&gt;The database has no concept of &lt;code&gt;AppConfiguration&lt;/code&gt; object or &lt;code&gt;AppUsageSection&lt;/code&gt;. In it’s eye, &lt;code&gt;appUsage&lt;/code&gt; is just a string column which can be null. The Room database uses converters to convert the raw fields into the kotlin objects as required.&lt;/p&gt;
&lt;p&gt;Since I used &lt;code&gt;AppConfiguration&lt;/code&gt; as nullable embedded field, Room behaves as following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flatten all the fields from embedded class into the table.&lt;/li&gt;
&lt;li&gt;If EVERY field of embedded class is null, then treat &lt;code&gt;AppConfiguration&lt;/code&gt; as null and don’t convert it.&lt;/li&gt;
&lt;li&gt;If ANY of the field is non-null, treat it as non-null and convert it to &lt;code&gt;AppConfiguration&lt;/code&gt; object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By updating every &lt;code&gt;appUsage&lt;/code&gt; value to &lt;code&gt;BARCHART&lt;/code&gt;, I inadvertently ended up marking the embedded field non-null, and when Room tried to convert it to an object, it converted the null into their corresponding default values (0 for integer, false for boolean and null for string).&lt;/p&gt;
&lt;h2&gt;Fix and Recovery&lt;/h2&gt;
&lt;p&gt;Once I understood the root cause, the fix was simple.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t update &lt;code&gt;appUsage&lt;/code&gt; field for ALL rows.&lt;/li&gt;
&lt;li&gt;Just update it for rows that already have non-null &lt;code&gt;AppConfiguration&lt;/code&gt;. That’s detectable by querying for non-null &lt;code&gt;grace-period&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But this just fixes the migration for those who hasn’t migrated yet. What about the 26 people whose data got corrupted during migration? I needed a way to fix their broken state.&lt;/p&gt;
&lt;p&gt;For them, I ended up bumping my database version again to v6 and wrote a version-5-to-6 migration logic. As part of this, I queried all rows that have &lt;code&gt;grace-period&lt;/code&gt; column set to null, but &lt;code&gt;appUsage&lt;/code&gt; field set to non-null. These should never happen ideally and indicate a corrupted row. For these I set all fields to null again.&lt;/p&gt;
&lt;p&gt;Then I tested the flow few more times and shipped the update (after a 24 hours review period).&lt;/p&gt;
&lt;h2&gt;After thought&lt;/h2&gt;
&lt;p&gt;This brings me to the question: what can I do to avoid such a scenario in future?&lt;/p&gt;
&lt;p&gt;Few things I can improve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This bug impacted per-app configuration, even if user didn’t use the feature. Very few user uses this feature, yet everyone ended up suffering from the bug. This shouldn’t be the case. I have decided to make per-app configuration a toggle switch and for those who have it turned off, in future, they won’t be impacted by any bug in per-app configuration.&lt;/li&gt;
&lt;li&gt;Even as the author, it took me a while to realize that the pause screen was showing config from per-app configuration. The detection should be simpler. On pause screen, I should show some icon to differentiate configuration from per-app vs dashboard.&lt;/li&gt;
&lt;li&gt;It takes 3 clicks to reach per-app configuration. I should make that easier.&lt;/li&gt;
&lt;li&gt;I had no way to communicate to the impacted users that I am aware about the bug, fix is coming and there is a workaround. I am gonna explore if there is any way to push message to my app’s home screen from Firebase (to apps on a particular version).&lt;/li&gt;
&lt;li&gt;I learnt a lot from this mistake. That’s the only consolation I can give to myself at the moment. I wouldn’t be surprised if I lose some users due to this issue and that sucks. Took me a long time to hit 100 WAU.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oh well. Mistakes are part of the journey. Let’s just hope I don’t repeat the same mistake again.&lt;/p&gt;
</content:encoded></item></channel></rss>