﻿<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>Ankur Sheel</title>
		<link>https://www.ankursheel.com/</link>
		<description>I go down rabbit holes and blog about them</description>
		<copyright>2026</copyright>
		<pubDate>Tue, 11 Aug 2026 03:07:22 GMT</pubDate>
		<lastBuildDate>Tue, 11 Aug 2026 03:07:22 GMT</lastBuildDate>
		<item>
			<title>WordXplorer Update 14</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-14</link>
			<description>New Theme and Cleaner UI</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-14-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-14</guid>
			<pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="new-theme-animals-24-new-levels"&gt;New Theme: Animals - 24 New Levels&lt;/h2&gt;
&lt;p&gt;The biggest change is a brand-new theme with 24 new levels.&lt;/p&gt;
&lt;p&gt;I wanted the game to keep growing in a way that still feels approachable for kids, so this update adds a fresh set of words themed around animals.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Level Selection Screen&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Game Screen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/animal_level_selection_screen.png" width="200" alt="Animals Level Selection Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/animal_game_screen.png" width="200" alt="Animals Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="clearer-ui-and-friendlier-copy"&gt;Clearer UI and Friendlier Copy&lt;/h2&gt;
&lt;p&gt;I also updated some of the text and buttons in the game.&lt;/p&gt;
&lt;p&gt;The old text just felt way too verbose. The instructions, notifications, and hint copy should now be clearer and easier to understand. Since WordXplorer is aimed at younger players, I want the wording to be simple, helpful, and friendly.&lt;/p&gt;
&lt;p&gt;I also updated buttons like Submit and Delete so the main actions are easier to understand at a glance.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/old_instructions.png" width="200" alt="Old Instructions"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/new_instructions.png" width="200" alt="New Instructions"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/old_hint.png" width="200" alt="Old Hint"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/new_hint.png" width="200" alt="New Hint"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/old_gamescreen.png" width="200" alt="Old Game Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/new_gamescreen.png" width="200" alt="New Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/old_notification.png" width="200" alt="Old Notification"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-14/new_notification.png" width="200" alt="New Notification"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="cleaner-monobehaviours"&gt;Cleaner MonoBehaviours&lt;/h2&gt;
&lt;p&gt;Behind the scenes, I spent most of my time making the Unity MonoBehaviour classes leaner and moving the actual logic into plain C# classes.&lt;/p&gt;
&lt;p&gt;By decoupling the game's core rules from Unity's engine architecture, I was finally able to write proper unit tests for the logic.&lt;/p&gt;
&lt;p&gt;This was a calulated trade-off. I could have worked on some more features that would be more visible. But having tests gives me immense confidence when making future updates, knowing a tiny change won't accidentally break three other things in the process.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next&lt;/h2&gt;
&lt;p&gt;Next up is adding a menu screen to the game and possibly some more sound effects. If you have any suggestions for features or improvements that you would like to see immediately, please let me know.&lt;/p&gt;
&lt;h2 id="become-a-beta-tester"&gt;Become a Beta Tester&lt;/h2&gt;
&lt;p&gt;Want to help catch bugs before they reach the public? I’d love your help. Simply send me a screenshot of your app review, and I’ll add you to the beta team. As a thank you, I’ll also give you a &lt;strong&gt;free copy of the app&lt;/strong&gt; to share with a friend!&lt;/p&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store and Google Play Store.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Want to try before you buy? Check out the &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for being part of this journey. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Defining Good: Why Your Engineering Team Needs Testing Guidelines</title>
			<link>https://www.ankursheel.com/blog/defining-good-engineering-team-needs-testing-guidelines</link>
			<description>It’s not that engineers don’t know how to write tests. It’s that they’ve never actually agreed on what 'good' means for their team. Here are some guidelines I try to follow in every project.</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/defining-good-engineering-team-needs-testing-guidelines-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/defining-good-engineering-team-needs-testing-guidelines</guid>
			<pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Whenever I join a new team, unless there are written guidelines, every person has a different opinion on what good tests look like. The codebase ends up inconsistent and the same debates cycle endlessly till everyone gives up.&lt;/p&gt;
&lt;p&gt;And lets be honest, some engineers still write tests just to tick a checkbox and meet a requirement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s not that we don't know how to write tests. It's that we’ve never actually agreed on what &lt;strong&gt;good&lt;/strong&gt; means for our team.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While guidelines might sound like unnecessary bureaucracy, in practice they offer a lot of advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They reduce cognitive load for both authors and reviewers by providing a clear set of standards to lean on and reference.&lt;/li&gt;
&lt;li&gt;They ensure long-term readability, making it easy to pick up a test someone else wrote six months ago and actually understand what it's doing.&lt;/li&gt;
&lt;li&gt;They speed up writing by eliminating the endless micro-decisions that don’t actually add value.&lt;/li&gt;
&lt;li&gt;They make your codebase AI-friendly. Hand these guidelines over to an AI coding agent, and it will write tests exactly how you expect them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I highly recommend every team come up with their own. These are mine. While they have been sourced from the many companies I have worked at, I have also adapted them heavily for my personal projects. Feel free to use them as a starting point or adopt them wholesale.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: No mock objects or QA engineers were harmed in the making of these guidelines. If you are deeply emotionally attached to &lt;code&gt;HappyPath&lt;/code&gt; as a test name, please consult your local reviewer before reading further.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/defining-good-engineering-team-needs-testing-guidelines/testing_guidelines.png" alt="Chaos to Organised"&gt;&lt;/p&gt;
&lt;h2 id="core-testing-philosophies"&gt;Core Testing Philosophies&lt;/h2&gt;
&lt;h3 id="test-both-sides-of-your-feature-flags"&gt;Test Both Sides of Your Feature Flags&lt;/h3&gt;
&lt;p&gt;Always write tests for both the flag-off path and the flag-on path. If testing the flag off scenario is too difficult, document exactly why, note what is missing, and make sure to manually test the scenario.&lt;/p&gt;
&lt;h3 id="optimize-for-readability-over-dry"&gt;Optimize for Readability over DRY&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;A good test suite should read like a &lt;strong&gt;Lego instruction manual&lt;/strong&gt;. Every step is laid out sequentially, is self-contained, and is completely obvious.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When someone navigates to a test to understand how a piece of code is supposed to behave, they should be able to figure it out quickly. A little duplication in tests is a small price to pay  if it makes the intention obvious.&lt;/p&gt;
&lt;p&gt;Think of your test suite as executable documentation.&lt;/p&gt;
&lt;h3 id="test-names-should-convey-the-behaviour"&gt;Test names should convey the behaviour&lt;/h3&gt;
&lt;p&gt;A test name like &lt;code&gt;GetUser_HappyPath&lt;/code&gt; tells me nothing. What is the happy path? What does success look like? Unless I look at the test body and the underlying implementation, I have no idea what it is doing.&lt;/p&gt;
&lt;p&gt;Better names express the &lt;em&gt;state&lt;/em&gt; and the &lt;em&gt;expected outcome&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GetUser_HappyPath&lt;/code&gt; → &lt;code&gt;CanRetrieveUserById&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetUser_ReturnsError&lt;/code&gt; → &lt;code&gt;WhenUserNotFoundReturnsNotFoundError&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have written about this in more detail at &lt;a href="https://www.ankursheel.com/blog/better-naming-convention-unit-tests"&gt;A better naming convention for unit tests&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="keep-flag-logic-out-of-parameterized-tests"&gt;Keep Flag Logic Out of Parameterized Tests&lt;/h3&gt;
&lt;p&gt;It is incredibly difficult to convey what a test is actually doing when you rely on boolean parameters to toggle flags inside the test cases. Keep flag paths in separate, explicitly named tests instead.&lt;/p&gt;
&lt;p&gt;Since you will eventually delete the flag-off path, it is perfectly fine to add a &lt;code&gt;_FlagOff&lt;/code&gt; suffix to the test that will eventually be removed.&lt;/p&gt;
&lt;h3 id="structure-every-test-as-arrange-act-assert"&gt;Structure every test as Arrange → Act → Assert&lt;/h3&gt;
&lt;p&gt;Each test should have one setup phase, one action, and one set of assertions. If you find yourself doing Arrange → Act → Assert → Act → Assert, you have two tests trying to live inside one. Split them up.&lt;/p&gt;
&lt;h3 id="leave-private-methods-alone"&gt;Leave Private Methods Alone&lt;/h3&gt;
&lt;p&gt;Private methods are implementation details. You should test the public methods that call them.&lt;/p&gt;
&lt;p&gt;The rare exception to this rule is when testing a highly specific, complex internal behaviour. An example of this is when I wanted to test batching logic for a SQL query with pagination. Since the batch was a constant of 10,000, testing it via the public method would have been extremely painful.&lt;/p&gt;
&lt;h3 id="no-branching-in-tests"&gt;No branching in tests&lt;/h3&gt;
&lt;p&gt;An &lt;code&gt;if&lt;/code&gt; statement inside a test makes it harder to understand and significantly harder to debug when it fails. Instead, pull the shared code into a helper method and write two separate, clean tests.&lt;/p&gt;
&lt;h3 id="avoid-spies"&gt;Avoid spies&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Needing a spy is usually a design smell.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A spy wraps a real object and lets you verify which calls were made on it. Using one suggests your code is doing something internally that isn't represented by a clean interface.&lt;/p&gt;
&lt;p&gt;The right fix is almost always to extract that dependency and inject it as a mock or test double instead. However, if you are working in a legacy codebase and can't easily restructure the code, use the spy but leave a comment explaining why. This forces you to articulate why there was no better option and saves the next developer from wondering why it's there.&lt;/p&gt;
&lt;h3 id="use-test-doubles-in-integration-tests-not-mocks"&gt;Use test doubles in integration tests, not mocks&lt;/h3&gt;
&lt;p&gt;Integration tests verify that your components work together correctly. When you mock a dependency, you replace real behaviour with a scripted response. If your assumptions about how that dependency behaves are wrong, your tests will pass while production breaks.&lt;/p&gt;
&lt;p&gt;Fakes and stubs actually implement the behaviour, just in a simpler way. For example, a fake email service that stores sent emails in memory still behaves like an actual email service.&lt;/p&gt;
&lt;p&gt;I have written about the different options at &lt;a href="https://www.ankursheel.com/blog/difference-stubs-mocks-fakes-spies"&gt;Stubs vs Mocks vs Fakes vs Spy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;h3 id="keep-setup-code-relevant-to-the-assertion"&gt;Keep Setup Code Relevant to the Assertion&lt;/h3&gt;
&lt;p&gt;If setup code is unrelated to what you are asserting in a specific test, move it into a helper method. Parameterise your helper methods so it is immediately clear what is unique about the current test.&lt;/p&gt;
&lt;p&gt;However, if removing the setup from the test body altogether makes the test harder to follow, keep it inline.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var user = CreateValidUser(id: "invalidId");

userService.Validate(user);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="start-from-the-minimum-for-database-dependent-tests"&gt;Start from the minimum for database-dependent tests&lt;/h3&gt;
&lt;p&gt;For tests that rely on the database being in a specific state, start from the minimum setup required for the application to run, and build up the state directly inside the test.  This keeps dependencies explicit and prevents a change in shared test data from breaking tests.&lt;/p&gt;
&lt;h2 id="assertions"&gt;Assertions&lt;/h2&gt;
&lt;h3 id="assert-one-concept-per-test"&gt;Assert one concept per test&lt;/h3&gt;
&lt;p&gt;Asserting one concept does not mean you are restricted to a single assertion line. A test called &lt;code&gt;CanRetrieveUserById&lt;/code&gt; can comfortably assert on multiple returned fields:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var user = userService.GetUser();

Assert.Equal("id", user.Id);
Assert.Equal("name", user.Name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What it &lt;em&gt;shouldn't&lt;/em&gt; do is also verify that an underlying analytics service was called during the retrieval. That is a separate concept and should live in its own test.&lt;/p&gt;
&lt;h3 id="assert-on-properties-not-object-equality"&gt;Assert on properties, not object equality&lt;/h3&gt;
&lt;p&gt;When you assert on object equality, you are at the mercy of how &lt;code&gt;Equals&lt;/code&gt; is overridden on that class.. You're trusting that whoever wrote it checked every field you care about.&lt;/p&gt;
&lt;p&gt;Asserting on properties directly means your test is explicit about what it actually cares about. When it fails, you know exactly which field is wrong.&lt;/p&gt;
&lt;p&gt;Avoid this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var expected = new User
{
    Id = "id",
    Name = "name"
};

var user = userService.GetUser();

Assert.Equal(expected, response);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do this instead:&lt;/p&gt;
&lt;p&gt;csharp&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var user = userService.GetUser();

Assert.Equal("id", user.Id);
Assert.Equal("name", user.Name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Exception&lt;/strong&gt;&lt;/em&gt;: If you want to catch the case where someone adds a new field and forgets to handle it, create an explicit &lt;code&gt;AllFieldsAreSetCorrectly&lt;/code&gt; test and use object equality there intentionally.&lt;/p&gt;
&lt;h3 id="use-assert.multiple-for-multiple-assertions"&gt;Use &lt;code&gt;Assert.Multiple&lt;/code&gt; for multiple assertions&lt;/h3&gt;
&lt;p&gt;If a test has multiple assertions and fails, you want to see all the failures at once, not just the first one that blocked the run.&lt;/p&gt;
&lt;p&gt;xUnit 3 has &lt;code&gt;Assert.Multiple&lt;/code&gt; and &lt;code&gt;Assert.MultipleAsync&lt;/code&gt; built in for this. For older versions, I put together &lt;a href="https://github.com/AnkurSheel/xUnitHelpers"&gt;xUnitHelpers&lt;/a&gt;, a small NuGet package with helpers I  I found myself writing from scratch on almost every project.&lt;/p&gt;
&lt;h3 id="verify-what-matters-ignore-what-doesnt"&gt;Verify what matters, ignore what doesn't&lt;/h3&gt;
&lt;p&gt;If your code delegates an important, state-changing call to a mocked dependency, verify it explicitly. But avoid verifying every single interaction or assertion on unrelated dependencies. It only makes your tests brittle.&lt;/p&gt;
&lt;h1 id="a-starting-point-not-a-finish-line"&gt;A starting point, not a finish line&lt;/h1&gt;
&lt;p&gt;I'm not suggesting you adopt these exact guidelines wholesale. Every team has different constraints, languages, and codebases.&lt;/p&gt;
&lt;p&gt;But next time a code review devolves into a circular argument about test names or assertion styles, ask yourself:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is the real problem just that nobody ever wrote the expectations down?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pick a starting point, document it, and iterate. The goal isn’t a perfect document on day one. It’s simply having a shared standard to point to.&lt;/p&gt;
&lt;p&gt;Note that these are guidelines, not rigid rules. Treat your guidelines as a living document, and update them as your team learns and evolves.&lt;/p&gt;
&lt;p&gt;What is one rule on this list you disagree with, or one guideline your team swears by that I missed? Let's discuss in the comments below!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Startup versus Big Tech: The Good, the Bad, and the Ugly</title>
			<link>https://www.ankursheel.com/blog/startup-versus-big-tech-good-bad-ugly</link>
			<description>Startup or Big Tech? Which should you choose? Here’s my honest take on the good, the bad, and the ugly of both.</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/startup-versus-big-tech-good-bad-ugly-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/startup-versus-big-tech-good-bad-ugly</guid>
			<pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I’ve spent about two decades bouncing between startups, Big Tech, and everything in between. Recently, someone early in their engineering career asked me which path they should take.&lt;/p&gt;
&lt;p&gt;My answer was the same as it is for most engineering questions: &lt;strong&gt;it depends but this is what I would do&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Both environments have completely different dynamics. They reward different styles and need different skills to be successful.&lt;/p&gt;
&lt;p&gt;Here’s my honest take on the good, the bad, and the &amp;quot;ugly&amp;quot; of both.&lt;/p&gt;
&lt;p&gt;And if you just want to know what I’d personally do, you can scroll to the end. But I'd urge you to actually figure out what works for you.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The stage of business determines what game you are playing in the first place: what the job will be like, who will be successful in it, and the mix of risk and reward you are accepting. So start there.&lt;/p&gt;
&lt;p&gt;Dan Hock&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="startups-the-good"&gt;Startups: The Good&lt;/h2&gt;
&lt;h3 id="ready-or-not-you-learn-fast"&gt;Ready or Not: You learn fast&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no - I don't know how to do that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Startups are amazing places to learn a lot of different things quickly. Because the company is small, there usually aren't specialized teams to hand things off to.&lt;/p&gt;
&lt;p&gt;You jump in where needed, even when you don't have a clue what you're doing.&lt;/p&gt;
&lt;p&gt;At First AML, I touched everything: backend systems, developer tooling, processes, just whatever needed to be done. That kind of environment broadens your expertise incredibly fast.&lt;/p&gt;
&lt;h3 id="visible-impact"&gt;Visible Impact&lt;/h3&gt;
&lt;p&gt;Startups are flat. You can influence the direction of the product, push back on decisions, and see the result of your work in the hands of real users quickly.&lt;/p&gt;
&lt;h3 id="you-know-everyone"&gt;You know everyone.&lt;/h3&gt;
&lt;p&gt;Startups are tight-knit in a way that larger organisations can't replicate. It’s easier to feel a personal connection when you aren't just Employee #10,402.&lt;/p&gt;
&lt;h2 id="startups-the-bad"&gt;Startups: The Bad&lt;/h2&gt;
&lt;h3 id="learning-is-self-directed"&gt;Learning is Self Directed&lt;/h3&gt;
&lt;p&gt;The flip side of &amp;quot;you learn fast&amp;quot; is that there's often nobody to learn from. When you're stuck, you're mostly stuck alone. No senior engineer who's solved this exact problem before, no internal documentation, no established way of doing things. No mentors. You can pick up bad habits without realizing it.&lt;/p&gt;
&lt;h3 id="compensation-is.aspirational"&gt;Compensation is… aspirational&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Startup Equity is Monopoly Money&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Base salaries are lower. Bonuses are rare. Stock options sound exciting until you realise that you need an IPO to see any of it.&lt;/p&gt;
&lt;p&gt;Most exercise windows are short enough that leaving early means walking away from everything that you are &amp;quot;promised&amp;quot;. You have no control over dilution or what kind of exit the company pursues.&lt;/p&gt;
&lt;h2 id="startups-the-ugly"&gt;Startups: The Ugly&lt;/h2&gt;
&lt;h3 id="money-problems"&gt;Money Problems&lt;/h3&gt;
&lt;p&gt;Several startups I worked at had payroll problems. Salaries were delayed for months on end.&lt;/p&gt;
&lt;p&gt;Holidays were cancelled because of a deadline that had to be met or the company would fold. It wasn't pretty.&lt;/p&gt;
&lt;h3 id="most-of-them-fail"&gt;Most of them fail&lt;/h3&gt;
&lt;p&gt;This is not a hot take. Startups fail. The earlier they are in the lifecycle, the higher the risk. You can do everything right and still find yourself out of a job because the runway ran out.&lt;/p&gt;
&lt;p&gt;You might have little to show when the company folds.&lt;/p&gt;
&lt;h2 id="big-tech-the-good"&gt;Big Tech: The good&lt;/h2&gt;
&lt;h3 id="career-rocket-fuel"&gt;Career rocket fuel&lt;/h3&gt;
&lt;p&gt;Having a big name on your CV opens doors before a recruiter even looks at your skills. Your skills and expertise is assumed.&lt;/p&gt;
&lt;h3 id="you-learn-a-different-kind-of-discipline"&gt;You learn a different kind of discipline&lt;/h3&gt;
&lt;p&gt;Big Tech teaches a different kind of engineering: depth, rigor, and long‑term thinking.&lt;/p&gt;
&lt;p&gt;At Atlassian, the scale alone was an education. Established practices, specialized teams, engineers who’d solved hard problems before.&lt;/p&gt;
&lt;p&gt;I learnt how to build systems that survive millions of users and thousands of edge cases. You learn how to plan and how to operate inside a complex ecosystem where different teams have their own roadmaps and commitments.&lt;/p&gt;
&lt;h3 id="the-money"&gt;The money&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Money is not the limiting factor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Big Tech pays pays extremely well. Base salaries are high, bonuses are common, and equity is actually worth something. Perks feel unreal.&lt;/p&gt;
&lt;p&gt;If financial independence or having shiny things is a goal, this environment makes it a lot easier.&lt;/p&gt;
&lt;h3 id="big-tech-the-bad"&gt;Big Tech: The Bad&lt;/h3&gt;
&lt;h3 id="a-small-cog-in-a-big-machine"&gt;A small cog in a big machine&lt;/h3&gt;
&lt;p&gt;It can be genuinely hard to see how your work connects to anything that matters. You can ship something used by millions and still feel that you didn't move the needle.&lt;/p&gt;
&lt;h3 id="everything-takes-longer"&gt;Everything takes longer&lt;/h3&gt;
&lt;p&gt;A straightforward change can take days. Stakeholder approvals, code reviews, teams in different time zones who own different parts of the system all contribute. You will spend a non-trivial amount of your career waiting.&lt;/p&gt;
&lt;h3 id="growth-can-plateau"&gt;Growth can plateau&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Progress isn’t always tied to ability.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can be the best engineer on your team and still find that the path forward is unclear.&lt;/p&gt;
&lt;h3 id="big-tech-the-ugly"&gt;Big Tech: The Ugly&lt;/h3&gt;
&lt;h3 id="performance-reviews-create-strange-incentive"&gt;Performance reviews create strange incentive&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Doing work that results in a great performance review is not always the same as doing work that best helps the company.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Individual impact is what gets measured, which makes it genuinely difficult to do work that benefits the broader organisation without also making sure it's visible enough to count for you personally.&lt;/p&gt;
&lt;p&gt;Doing great work that benefits the company but doesn't &amp;quot;look&amp;quot; like a big individual win can actually hurt your career.&lt;/p&gt;
&lt;p&gt;In the most pathological companies, work is created primarily so that someone more senior can demonstrate impact and get promoted, not because it's the right thing to build.&lt;/p&gt;
&lt;h3 id="switching-directions"&gt;Switching Directions&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The pay cut might be hard, but the loss of structure is harder.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The common path is startup to Big Tech, not the other way around. Big Tech teaches you how to build at scale with lots of support. Startups need scrappiness, breadth, and comfort with chaos. Big tech skills don’t always transfer cleanly.&lt;/p&gt;
&lt;h2 id="what-would-i-do"&gt;What would I do?&lt;/h2&gt;
&lt;p&gt;If I were early in my career and had both options, I would take Big Tech.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can always leave for a startup later. You can't go back and add the name to your CV.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While it wasn't exactly &amp;quot;Big Tech&amp;quot;, this is why I joined Perot Systems after graduating. It signaled to future employers that I had the skills to be hired by a recognizable company. It was a stamp of approval.&lt;/p&gt;
&lt;p&gt;A year later, I left to follow my passion - making games. I took a massive pay cut and joined a games company. Not a startup in the purest sense, but it had all the same problems: a tiny engineering team and constant payroll issues.&lt;/p&gt;
&lt;p&gt;I could take that risk because I had Perot Systems on my CV. It provided me with a safety net and derisked that decision for me.&lt;/p&gt;
&lt;h2 id="so-which-should-you-choose"&gt;So which should you choose?&lt;/h2&gt;
&lt;p&gt;If you're early in your career and havent worked for a recognizable company yet, take the opportunity when it comes.  It makes every future job hunt easier.&lt;/p&gt;
&lt;p&gt;But don't stay so long that you forget how to hustle. They are called &lt;strong&gt;golden handcuffs&lt;/strong&gt; for a reason. I know people who stayed so long they now struggle to move anywhere else except for maybe another big company.&lt;/p&gt;
&lt;p&gt;Once you have some experience, optimize for learning over compensation.&lt;/p&gt;
&lt;p&gt;The environments where you're slightly out of your depth are usually the ones that move your career forward fastest.&lt;/p&gt;
&lt;p&gt;I grew more and learnt more things at First AML than anywhere else.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The sweet spot is somewhere between understanding the discipline of a large organisation and still being able to build something from scratch.&lt;/p&gt;
&lt;p&gt;Where are you in your journey, and which way are you leaning right now? Do you prefer the stability of a big company or the excitement of wearing ten different hats at a startup? Or are you just there for the free snacks?&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer Update 13: Updated Flip Animation</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-13</link>
			<description>Updated Flip Animation and some code cleanup.</description>
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-13</guid>
			<pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="flip-animation-overhaul"&gt;Flip Animation Overhaul&lt;/h2&gt;
&lt;p&gt;I updated the flip animation to make it a pop a bit more. You can see for yourself and tell me waht you think.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-13/old_flip_animation.gif" width="200" alt="Old Flip Animation"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-13/new_flip_animation.gif" width="200" alt="New Flip Animation"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="code-cleanup-and-unity-upgrade"&gt;Code Cleanup and Unity Upgrade&lt;/h2&gt;
&lt;p&gt;I upgraded to Unity 6.3 to keep the engine current and to get the free AI credits. I am not sure what I did wrong but I was unable to get the AI credits to work.&lt;/p&gt;
&lt;p&gt;I also removed some tech debt to make it easier to add tests and updated Github Actions to use macos-26 to stop a Apple warning related to Xcode.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next&lt;/h2&gt;
&lt;p&gt;I had some UX and copy improvements ready to go but ran out of free GitHub Actions minutes before I could ship them. They'll be in the April update.&lt;/p&gt;
&lt;h2 id="become-a-beta-tester"&gt;Become a Beta Tester&lt;/h2&gt;
&lt;p&gt;Want to help catch bugs before they reach the public? I’d love your help. Simply send me a screenshot of your app review, and I’ll add you to the beta team. As a thank you, I’ll also give you a &lt;strong&gt;free copy of the app&lt;/strong&gt; to share with a friend!&lt;/p&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store and Google Play Store.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Want to try before you buy? Check out the &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for being part of this journey. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Rolling Your Own Dependency Injection in Unity</title>
			<link>https://www.ankursheel.com/blog/rolling-dependency-injection-unity</link>
			<description>Unity has no DI story out of the box and the existing frameworks felt too heavy. So I rolled my own using Microsoft.Extensions.DependencyInjection.</description>
			<guid>https://www.ankursheel.com/blog/rolling-dependency-injection-unity</guid>
			<pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Unity codebases have a way of turning into spaghetti. Every system talks to every other system through singletons and magic strings, and it never gets better with time. When I came back to Unity to build WordXplorer, I knew what I wanted: clean dependency injection, testable classes, no hidden dependencies. The problem was that while I'd used DI in .NET Core apps, I'd never seen it in a Unity project.&lt;/p&gt;
&lt;p&gt;By the end of this, you'll have a setup where plain C# classes get proper constructor injection, MonoBehaviours register themselves automatically, and the whole thing initialises lazily without a bootstrapper. It ends up feeling a lot like any other .NET Core app which is exactly the point.&lt;/p&gt;
&lt;h2 id="the-alternatives-and-why-they-fall-short"&gt;The Alternatives (And Why They Fall Short)&lt;/h2&gt;
&lt;p&gt;Before getting into the solution, it’s worth looking at what most Unity developers do instead. Spoilers: it’s not great.&lt;/p&gt;
&lt;h3 id="drag-and-drop-in-the-editor"&gt;Drag-and-Drop in the Editor&lt;/h3&gt;
&lt;p&gt;The simplest way to connect objects is to expose a private serialised field and drag the reference in the Inspector. This is convenient, especially for artists and designers who can wire up behaviours without touching code. But it's a lot of manual effort and doesn't scale. And none of it is easily testable because you can't run a unit test against a scene.&lt;/p&gt;
&lt;h3 id="querying-the-scene"&gt;Querying the Scene&lt;/h3&gt;
&lt;p&gt;You can find objects at runtime in a few ways: by name, by tag, or by type.&lt;/p&gt;
&lt;p&gt;Finding by name or tag relies on magic strings. Rename something in the hierarchy and the code breaks. The worst part is you only find out at runtime.&lt;/p&gt;
&lt;p&gt;Finding by type is better because it's at least type-safe. But it's still a global scene query, it's slow, and it gives you no way to swap in a different implementation for testing.&lt;/p&gt;
&lt;h3 id="singletons"&gt;Singletons&lt;/h3&gt;
&lt;p&gt;Most Unity tutorials reach for singletons, and they are better than the two options above. But in practice, singletons cause three problems that compound as the project grows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Global state&lt;/strong&gt;: Every singleton is effectively a global variable. Any class can reach in and modify it at any time, from anywhere. Reasoning about what changed, and when, becomes increasingly difficult. You can manage this if you're diligent — but diligence has a way of slipping when you're in a rush and just need to ship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hidden dependencies&lt;/strong&gt;: When a class accesses a singleton directly, that dependency is invisible from the outside. You can't tell what a class needs just by looking at its constructor or its interface. The only way to find out is to read the implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(Un)testability&lt;/strong&gt;: Singeltons make writing automated tests hard. If a class depends on a concrete singleton, you can't swap it out for a fake in a test. You could test with the real thing, but only if it has no side effects and doesn't depend on other singletons. In practice, it usually does.&lt;/p&gt;
&lt;h3 id="unity-specific-di-frameworks"&gt;Unity-Specific DI Frameworks&lt;/h3&gt;
&lt;p&gt;There are frameworks built specifically for Unity DI like &lt;a href="https://github.com/modesttree/Zenject"&gt;Zenject&lt;/a&gt; and &lt;a href="https://vcontainer.hadashikick.jp/"&gt;VContainer&lt;/a&gt;. Both are heavy frameworks with their own learning curve. I find them too bloated for my purposes, but they might be a viable option.&lt;/p&gt;
&lt;p&gt;To be honest, I really didnt look too much into them. I might just be reinventing the wheel here.&lt;/p&gt;
&lt;h2 id="what-is-dependency-injection"&gt;What is Dependency Injection?&lt;/h2&gt;
&lt;p&gt;DI (Dependency Injection) is a technique in OOP where instead of a class creating its own dependencies, you pass them in from the outside.&lt;/p&gt;
&lt;p&gt;So instead of this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class GameManager
{
    private AudioManager _audio = new AudioManager();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class GameManager
{
    private IAudioManager _audio;

    public GameManager(IAudioManager audio)
    {
        _audio = audio;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;GameManager&lt;/code&gt; doesn’t care if it gets a real &lt;code&gt;AudioManager&lt;/code&gt; or a mock for testing. A DI container automates the wiring and  resolves them wherever they're needed.&lt;/p&gt;
&lt;h2 id="why-does-unity-make-di-awkward"&gt;Why Does Unity Make DI Awkward?&lt;/h2&gt;
&lt;p&gt;Two things get in the way in Unity.&lt;/p&gt;
&lt;p&gt;First, MonoBehaviours can't be newed up. Unity owns their lifecycle by calling &lt;code&gt;Awake&lt;/code&gt;, &lt;code&gt;Start&lt;/code&gt;, &lt;code&gt;Update&lt;/code&gt;, and so on. You can't pass dependencies through a constructor the normal way.&lt;/p&gt;
&lt;p&gt;Second, there's no single entry point. A .NET console app has &lt;code&gt;Main&lt;/code&gt;. An ASP.NET app has &lt;code&gt;Program.cs&lt;/code&gt;. But Unity has scenes, objects, and an initialization order you don't fully control. Scenes can also be loaded and unloaded, which means objects might not always be present when the container is first created, or might be destroyed after they've already been registered.&lt;/p&gt;
&lt;p&gt;You might think these two constraints would be a showstopper. They're not. But they do require a slightly different approach.&lt;/p&gt;
&lt;h2 id="the-solution"&gt;The Solution&lt;/h2&gt;
&lt;h3 id="step-1-add-the-nuget-packages"&gt;Step 1: Add the NuGet Packages&lt;/h3&gt;
&lt;p&gt;Download the following DLLs from &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection/"&gt;NuGet&lt;/a&gt; and drop them into your &lt;code&gt;Assets/Plugins&lt;/code&gt; folder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.DependencyInjection.dll&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.DependencyInjection.Abstractions.dll&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've written about &lt;a href="https://www.ankursheel.com/blog/installing-nuget-packages-unity-2021"&gt;how to install NuGet packages in Unity&lt;/a&gt; in more detail. If you use assembly definitions, add references to these DLLs in the relevant assembly definition file.&lt;/p&gt;
&lt;h3 id="step-2-the-dependencyhelper"&gt;Step 2: The DependencyHelper&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;DependencyHelper&lt;/code&gt; owns the DI container and acts as the resolution point for both plain C# services and MonoBehaviours.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public sealed class DependencyHelper
{
    private static readonly Dictionary&amp;lt;Type, object&amp;gt; _injectedMonoBehaviours = new();
    private static readonly Lazy&amp;lt;DependencyHelper&amp;gt; _lazy =
        new(() =&amp;gt; _instance ?? new DependencyHelper());
    private static DependencyHelper _instance;
    private static ServiceProvider _serviceProvider;

    private DependencyHelper()
    {
        var serviceCollection = new ServiceCollection();
        ServiceRegistry.RegisterServices(serviceCollection);
        _serviceProvider = serviceCollection.BuildServiceProvider();
        _instance = this;
    }

    public static void Register(object instance)
    {
        _injectedMonoBehaviours[instance.GetType()] = instance;
    }

    public static T GetInjectedMonoBehaviour&amp;lt;T&amp;gt;() where T : class
        =&amp;gt; _injectedMonoBehaviours[typeof(T)] as T;

    public static T GetRequiredService&amp;lt;T&amp;gt;() where T : class
    {
        _ = _lazy.Value; // ensures the container is built on first call
        return _serviceProvider.GetRequiredService&amp;lt;T&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Register&lt;/code&gt;: called automatically by &lt;code&gt;InjectableMonoBehaviour.Awake&lt;/code&gt; to store the live MonoBehaviour instance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetInjectedMonoBehaviour&lt;/code&gt;: used inside &lt;code&gt;ServiceRegistry&lt;/code&gt; to wire a MonoBehaviour into the container by fetching the stored instance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetRequiredService&lt;/code&gt;: how everything else resolves dependencies. Throws if the service isn't registered.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Lazy&lt;/code&gt; factory ensures the container is built exactly once, on the first call to &lt;code&gt;GetRequiredService&lt;/code&gt;. The &lt;code&gt;_instance&lt;/code&gt; check means there is only ever one container instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="step-3-inherit-from-injectablemonobehaviour"&gt;Step 3: Inherit from InjectableMonoBehaviour&lt;/h3&gt;
&lt;p&gt;Next, we need a way to add MonoBehaviours to the DI container&lt;/p&gt;
&lt;p&gt;&lt;code&gt;InjectableMonoBehaviour&lt;/code&gt; is a base class that MonoBehaviours inherit from, which registers them with the container when they wake up.&lt;/p&gt;
&lt;p&gt;Any MonoBehaviour that needs to be resolvable by the container should extend this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class InjectableMonoBehaviour : MonoBehaviour
{
    protected virtual void Awake()
    {
        DependencyHelper.Register(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When Unity calls &lt;code&gt;Awake&lt;/code&gt;, the MonoBehaviour registers itself by type. That's it. The container can now hand out a reference to this instance when something asks for it.&lt;/p&gt;
&lt;h3 id="step-4-the-service-registry"&gt;Step 4: The Service Registry&lt;/h3&gt;
&lt;p&gt;This is where you tell the container what goes where.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static class ServiceRegistry
{
    public static void RegisterServices(IServiceCollection serviceCollection)
    {
        // Plain C# service
        serviceCollection.AddTransient&amp;lt;ILevelSaveDataService, LevelSaveDataService&amp;gt;();

        // MonoBehaviour
        serviceCollection.AddTransient&amp;lt;IAudioManager&amp;gt;(
            _ =&amp;gt; DependencyHelper.GetInjectedMonoBehaviour&amp;lt;AudioManager&amp;gt;());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Plain C# services register normally. MonoBehaviours are different - they use a lambda to defer the lookup until the instance is actually needed, by which point Unity will have called &lt;code&gt;Awake&lt;/code&gt; on the MonoBehaviour and registered it.&lt;/p&gt;
&lt;h3 id="step-5-watch-the-lifecycle-order"&gt;Step 5: Watch the Lifecycle Order&lt;/h3&gt;
&lt;p&gt;This is the part that will bite you if you're not paying attention.&lt;/p&gt;
&lt;p&gt;Unity calls &lt;code&gt;Awake&lt;/code&gt; on scripts in an arbitrary order. If you try to resolve a dependency in &lt;code&gt;Awake&lt;/code&gt;, you might run before that dependency has even registered itself. If that happens, you will get a &lt;code&gt;KeyNotFoundException&lt;/code&gt; at runtime.&lt;/p&gt;
&lt;p&gt;We know that &lt;code&gt;Start&lt;/code&gt; is called after &lt;code&gt;Awake&lt;/code&gt;, so the rule is simple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Always resolve and use dependencies in &lt;code&gt;Start&lt;/code&gt;, not &lt;code&gt;Awake&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This also has the added benefit of resolving the dependency only once.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class LevelSaveDataManager : MonoBehaviour
{
    private ILevelSaveDataService _levelSaveDataService;

    private void Start()
    {
        _levelSaveDataService = DependencyHelper.GetRequiredService&amp;lt;ILevelSaveDataService&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same caveat applies across scenes. If a scene transition destroys and recreates MonoBehaviours, they re-register themselves in their new &lt;code&gt;Awake&lt;/code&gt;. The container's service registrations stay intact - those lambdas always pull the current live instance. But if you're holding a cached reference somewhere and the MonoBehaviour was destroyed, that reference is stale. Be deliberate about when you resolve.&lt;/p&gt;
&lt;h2 id="isnt-this-the-service-locator-anti-pattern"&gt;Isn't This the Service Locator Anti-Pattern?&lt;/h2&gt;
&lt;p&gt;Yes. I know. I'm not thrilled about it either.&lt;/p&gt;
&lt;p&gt;The criticism is legitimate: when a class calls &lt;code&gt;DependencyHelper.GetRequiredService&amp;lt;T&amp;gt;()&lt;/code&gt; directly, its dependencies are hidden. You can't tell from the constructor what it needs. Testing becomes harder because you have to know to configure the locator before running any test.&lt;/p&gt;
&lt;p&gt;But Unity’s lifecycle leaves us with very few &amp;quot;clean&amp;quot; options.&lt;/p&gt;
&lt;p&gt;Constructor injection requires that you control object creation.&lt;/p&gt;
&lt;p&gt;In Unity, you don't. MonoBehaviours are created by the engine, attached to GameObjects, and initialised through &lt;code&gt;Awake&lt;/code&gt; and &lt;code&gt;Start&lt;/code&gt;. There's no constructor call you can intercept to inject dependencies the clean way.&lt;/p&gt;
&lt;p&gt;I think the Service Locator is a small price to pay given all the other alternatives. We can minimize the damage by treating MonoBehaviours as a &amp;quot;thin&amp;quot; layer. They handle the Unity stuff (input, rendering), and delegate all the actual logic to injected plain C# classes.&lt;/p&gt;
&lt;p&gt;As you saw in Step 5, &lt;code&gt;LevelSaveDataManager&lt;/code&gt; is the thin MonoBehaviour — it knows &lt;em&gt;when&lt;/em&gt; to load. &lt;code&gt;ILevelSaveDataService&lt;/code&gt; is the plain C# class where all the loading logic lives, fully testable without a running scene.&lt;/p&gt;
&lt;h2 id="was-it-worth-it"&gt;Was It Worth It?&lt;/h2&gt;
&lt;p&gt;If you've worked on .NET Core apps, you already know most of what's here. Unity just requires a small amount of extra wiring to account for its lifecycle. Once that's in place, you can mostly forget it's there.&lt;/p&gt;
&lt;p&gt;What are your experiences with DI in Unity? Have you gone the Zenject route, or are you still living with singletons? I'm curious what you are doing? Let me know in the comments.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer Update 12: Demo Build &amp; Teacher Feedback</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-12</link>
			<description>Automated screenshots, addressing 'Teacher Approved' feedback, and expanding the demo build.</description>
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-12</guid>
			<pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="automated-screenshot-capture"&gt;Automated Screenshot Capture&lt;/h2&gt;
&lt;p&gt;Updating the WordXplorer store listings has been a tedious affair. Previously, I had to manually capture screenshots for every device size, which made it difficult to keep visuals up to date as the game improved.&lt;/p&gt;
&lt;p&gt;To solve this, I wrote a series of custom &lt;strong&gt;Unity Editor scripts&lt;/strong&gt; to automate the process. These scripts programmatically trigger high-resolution renders for every required form factor and automatically apply a branded banner to each shot.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-12/old_screenshot.png" width="200" alt="Old Screenshot"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-12/new_screenshot.png" width="200" alt="New Screenshot"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="teacher-approved-feedback"&gt;Teacher Approved Feedback&lt;/h2&gt;
&lt;p&gt;I’ve been working toward getting the "Teacher Approved" badge on the Google Play Store. While the app was previously approved, Google has identified two specific areas I need to address to maintain that status: &lt;strong&gt;Text Volume&lt;/strong&gt; and &lt;strong&gt;Replayability&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I'm currently brainstorming ideas to address these two concerns and ensure the game gets approved again.&lt;/p&gt;
&lt;h2 id="demo-build-updates"&gt;Demo Build Updates&lt;/h2&gt;
&lt;p&gt;I’ve updated the &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt; to include all themes and levels available in the paid version. While active play is still limited to the first six levels, users can now browse the full library of content.&lt;/p&gt;
&lt;p&gt;My goal is to ensure players see the full scope of the game before purchasing, so they know exactly how much content they are getting in the full version.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next?&lt;/h2&gt;
&lt;p&gt;I’m currently prioritizing which features to add next - there were quite a few that were cut from the initial release to get the game into your hands faster. I'm looking forward to bringing those back into the roadmap.&lt;/p&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store and Google Play Store.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;h3 id="become-a-beta-tester"&gt;Become a Beta Tester&lt;/h3&gt;
&lt;p&gt;Want to help catch bugs before they reach the public? I’d love your help. Simply send me a screenshot of your app review, and I’ll add you to the beta team. As a thank you, I’ll also give you a &lt;strong&gt;free copy of the app&lt;/strong&gt; to share with a friend!&lt;/p&gt;
&lt;h3 id="try-the-demo"&gt;Try the Demo&lt;/h3&gt;
&lt;p&gt;Want to try before you buy? Check out the &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for being part of this journey. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer Update 11: New Theme</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-11</link>
			<description>New theme, smarter background</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-11-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-11</guid>
			<pubDate>Thu, 05 Feb 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="new-theme"&gt;New Theme&lt;/h2&gt;
&lt;p&gt;Remember when I asked you which theme should come next?&lt;/p&gt;
&lt;p&gt;Well, you voted. And here it is.&lt;/p&gt;
&lt;p&gt;I've added a brand-new theme with 24 levels focusing on &lt;strong&gt;body parts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Hands, feet, eyes – the classics and a few tricky ones. It's the kind of vocabulary that actually helps kids describe their world.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Level Selection Screen&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Game Screen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/body_part_level_selection_screen.png" width="200" alt="Body Part Level Selection Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/body_part_game_screen.png" width="200" alt="Body Part Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="dynamic-backgrounds"&gt;Dynamic Backgrounds&lt;/h2&gt;
&lt;p&gt;Some of the more eagle eyed readers might have noticed that the background is a little different in the above 2 screen shots.&lt;/p&gt;
&lt;p&gt;There are no more generic bubbles. No more one-size-fits-all backgrounds. The backgrounds now change based on the theme.&lt;/p&gt;
&lt;p&gt;If you're playing the &lt;em&gt;Things We Eat&lt;/em&gt; theme, you see food. If you're in &lt;em&gt;Body Parts&lt;/em&gt;, you see... well, you know. It sounds simple, and it is. But, it gives kids an instant visual clue about what the theme is.&lt;/p&gt;
&lt;p&gt;To be honest, this works best on a tablet where you can really see the new elements.&lt;/p&gt;
&lt;p&gt;On a phone? It's more subtle – due to the lack of screen space. But if kids need a reminder of the theme, the hint screen has them covered.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/before_splash_screen.png" width="200" alt="Before Splash Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/after_splash_screen.png" width="200" alt="After Splash Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/before_level_selection_screen.png" width="200" alt="Before Level Selection Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/after_level_selection_screen.png" width="200" alt="After Level Selection Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/before_game_screen.png" width="200" alt="Before Game Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/after_game_screen.png" width="200" alt="After Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="beta-testers-save-the-day"&gt;Beta Testers Save the Day&lt;/h2&gt;
&lt;p&gt;Before I released this update to everyone, the beta testers found a couple of nasty bugs.&lt;/p&gt;
&lt;p&gt;One was particularly gnarly: if you completed the last level and then tried to click "next level," the game would crash. When you reopened it, the level selection screen would be completely borked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I'm so glad we caught it before release.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If that had made it to a paying user? Ooof. That would have sucked.&lt;/p&gt;
&lt;h3 id="want-to-be-a-beta-tester"&gt;Want to Be a Beta Tester?&lt;/h3&gt;
&lt;p&gt;If you're interested in helping me catch bugs before they reach everyone else, I'd love to have you on board. All you need to do is send me a screenshot of a review (to prove you've got the app). That's it.  As an added bonus, I will give you a free copy of the app to give to your friends.&lt;/p&gt;
&lt;h2 id="level-button-updates"&gt;Level Button Updates&lt;/h2&gt;
&lt;p&gt;Before the level selection screen, the level buttons were just random colors. They looked colorful but didn't give a clear indication of what level is completed and which one is newly unlocked. Now the level buttons can only have 1 of 3 colors - depending on the level status.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/before_level_selection_screen.png" width="200" alt="Before Level Selection Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-11/body_part_level_selection_screen.png" width="200" alt="After Level Selection Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="whats-next"&gt;What's Next?&lt;/h2&gt;
&lt;p&gt;I'm adding a proper menu screen. It's not flashy – just polish and table stakes. Things that I had cut for the intial release.&lt;/p&gt;
&lt;p&gt;More updates coming soon. Can't wait to share what's next.&lt;/p&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store and Google Play store.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Want to try before you buy? Check out our &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thank you for being a part of this journey with me! I'm eager to hear your thoughts and feedback. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer One Year In: Am I a Millionaire Yet?</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-one-year-am-millionaire-yet</link>
			<description>Am I Millionaire yet? A retrospective and some numbers one year after the launch of WordXplorer</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-one-year-am-millionaire-yet-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-one-year-am-millionaire-yet</guid>
			<pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;It's been just over a year since I released WordXplorer - a Wordle-inspired game for kids built in Unity. Available on the App Store and Play Store, this has been an interesting adventure.&lt;/p&gt;
&lt;p&gt;The million-dollar question that is on everyone's mind:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Are you a millionaire yet?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not quite. But I've crossed a few items off my &lt;a href="https://www.ankursheel.com/impossible-list"&gt;impossible list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most importantly, I've validated that people are willing to pay for something I've built. And that feels good.&lt;/p&gt;
&lt;p&gt;In this post, I'll share WordXplorer's performance over the past year, the feedback I've received, and the lessons learned along the way.&lt;/p&gt;
&lt;h2 id="a-little-backstory"&gt;A Little Backstory&lt;/h2&gt;
&lt;p&gt;In June 2024, I started working on a prototype of a Wordle-like game for my daughter. She loved trying to do the New York Times Wordle with us, but it was (at times) just too hard for her. It was also not very enticing for a kid. Greys and black are not the most kid-friendly colours.&lt;/p&gt;
&lt;p&gt;Thinking that other kids might want to play along with their Wordle-loving parents, I decided to make it into a paid app and announced a release date of December 1st in early November 2024. I priced it at USD 3.99 - roughly the cost of a coffee.&lt;/p&gt;
&lt;h2 id="what-went-right"&gt;What went right&lt;/h2&gt;
&lt;h3 id="my-daughter-still-plays-it"&gt;My daughter still plays it&lt;/h3&gt;
&lt;p&gt;Seeing my daughter play and enjoy something I built for her - that never gets old. That's why I started this whole thing. She's the 1st tester for all the changes I make and always has some good feedback for me. She's quick to tell me when a word is too hard or when she spots a bug I missed.&lt;/p&gt;
&lt;p&gt;Including my daughter, I still have 1-4 other active users on any given day.&lt;/p&gt;
&lt;h3 id="getting-help-early-on"&gt;Getting help early on&lt;/h3&gt;
&lt;p&gt;My artistic chops are average at best (and that's me being extremely generous). Getting Manjari to help me with the non-engineering side of things was one of the best decisions I made.&lt;/p&gt;
&lt;p&gt;Even though we are both extremely busy with work and syncs across the timezones were (and still are) challenging, having someone else to shoulder some of the load and talk through ideas made it a lot easier.&lt;/p&gt;
&lt;p&gt;The game also looks significantly better than I had made in the prototype phase. See for yourself.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-one-year-am-millionaire-yet/old_game_screen.jpg" alt="Old Game Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-one-year-am-millionaire-yet/new_game_screen.jpg" width="200" alt="New Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="github-actions-and-cicd"&gt;GitHub actions and CI/CD&lt;/h3&gt;
&lt;p&gt;One of the first things I did was set up GitHub actions to run automated tests on every merge to the master branch. I also set up workflows to deploy to the web and app stores.&lt;/p&gt;
&lt;p&gt;Since I don't have a Mac, this made deploying to the iOS App Store much cheaper and easier. I could trigger a deployment at any time. On PRs, I had test builds on the web using Netlify's branch deploy feature. Best of all, I could work in parallel on my local machine while it was building (which took upwards of 20 minutes).&lt;/p&gt;
&lt;h3 id="pre-orders"&gt;Pre-orders&lt;/h3&gt;
&lt;p&gt;I started the App Store pre-order process early. It let me navigate the approval process and work out most of the kinks before the December 1st launch day.&lt;/p&gt;
&lt;p&gt;One thing I learned during pre-order: I can't use "Wordle" in the app description or screenshots which kinda sucks since the game is essentially "Wordle For Kids" and would have been the easiest way to get people to buy it.&lt;/p&gt;
&lt;p&gt;Imagine being ready to launch and then hearing from the App Store &lt;del&gt;gods&lt;/del&gt; reviewers that "Wordle" is not allowed in the description or screenshots and having to delay the launch by a few weeks.&lt;/p&gt;
&lt;h3 id="making-hard-calls-to-ship"&gt;Making hard calls to ship&lt;/h3&gt;
&lt;p&gt;When I announced December 1st as the launch date, there was &lt;em&gt;so much&lt;/em&gt; to be done.&lt;/p&gt;
&lt;p&gt;It was fine when it was a free game that I made for my daughter, but a paid app for strangers is a totally different ball game. Deciding which features to cut for launch wasn't easy, but I had to ship, so I made the hard calls.&lt;/p&gt;
&lt;p&gt;Too many side projects die in "&lt;strong&gt;&lt;em&gt;just one more feature&lt;/em&gt;&lt;/strong&gt;" hell.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-one-year-am-millionaire-yet/feature_hell.png" width="300" alt="Feature Hell.png"&gt;&lt;/p&gt;
&lt;p&gt;Looking back, it was the right call even though I knew 30 levels wasn't enough and I didn't have all the polish. I had to get it out on time.&lt;/p&gt;
&lt;h3 id="validation"&gt;Validation&lt;/h3&gt;
&lt;p&gt;I didn't go viral. I didn't make life-changing money. But &lt;strong&gt;people paid real money for something I built&lt;/strong&gt;. That's something.&lt;/p&gt;
&lt;p&gt;Friends and family bought it initially - that's expected.&lt;/p&gt;
&lt;p&gt;But then someone else bought it. A complete stranger who had no obligation to support me. It was proof that someone out there thought what I built was worth paying for.&lt;/p&gt;
&lt;p&gt;If you're sitting on a side project, waiting for it to be "perfect" before launching - just ship it. Shipping something you know is not finished is scary, but you should still do it. You will learn a lot.&lt;/p&gt;
&lt;h3 id="natural-break-patterns"&gt;Natural break patterns&lt;/h3&gt;
&lt;p&gt;The average session is around &lt;strong&gt;3 minutes&lt;/strong&gt;. That's roughly one to two levels, which means most kids play, complete a level or two, and then leave the app to do something else.&lt;/p&gt;
&lt;p&gt;Some kids binge multiple levels and go up to 30 minutes, but the typical pattern is short sessions.&lt;/p&gt;
&lt;p&gt;This is great since I didn't want kids getting hooked and be on the screen for hours on end. And since most users have completed all 30 levels, they have clearly come back to play the levels.&lt;/p&gt;
&lt;p&gt;The average user has spent about 1 hour on the app total.&lt;/p&gt;
&lt;h2 id="what-went-wrong-aka-lessons-learned"&gt;What went wrong, aka lessons learned&lt;/h2&gt;
&lt;h3 id="pre-orders-are-great-if-you-actually-promote-them"&gt;Pre-orders are great (if you actually promote them)&lt;/h3&gt;
&lt;p&gt;The pre-order strategy was smart for working out the kinks with App Store approval. But not promoting it &lt;del&gt;widely&lt;/del&gt; at all? That was dumb.&lt;/p&gt;
&lt;p&gt;I fell into the classical engineer's mistake:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Build it, and they will come.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I didn't publicise the pre-order beyond some posts on this blog. A few days before launch, I directly messaged friends and family, which resulted in a grand total of 4 pre-orders. Two of those were later cancelled.&lt;/p&gt;
&lt;p&gt;Yeah. Not exactly a viral launch.&lt;/p&gt;
&lt;p&gt;Part of it was discomfort with self-promotion. Part of it was simply not knowing where to start.&lt;/p&gt;
&lt;p&gt;If you're going to offer a pre-order, be sure to let people know about it.&lt;/p&gt;
&lt;h3 id="googles-requirements"&gt;Google's requirements&lt;/h3&gt;
&lt;p&gt;Google requires 12 people to actively play your game for 10 days before you can even release an app to the store.&lt;/p&gt;
&lt;p&gt;And if it's a paid app then &lt;strong&gt;&lt;em&gt;they have to buy it&lt;/em&gt;&lt;/strong&gt; to even test it. It's unclear if they have to buy it again when the app store listing goes live, so to be safe I just made it free for a week so that people could download it without having to pay. Unfortunately promo codes just didn't work for this.&lt;/p&gt;
&lt;p&gt;Not just install it - actively play it for 10 days. I didn't know what would happen if someone quit after 2 days or didn't play every day, so I added 15 people to be safe. Getting friends and family to commit to playing for 10 days straight was challenging.&lt;/p&gt;
&lt;p&gt;It's a significant hurdle that slowed down the Android launch. It also meant people who otherwise might have bought the game got it for free.&lt;/p&gt;
&lt;h3 id="promo-codes"&gt;Promo codes&lt;/h3&gt;
&lt;p&gt;I gave 12 promo codes on iOS as a thank you for providing early feedback.&lt;/p&gt;
&lt;p&gt;The kicker: promo code users can't leave reviews on the App Store. These were my most supportive users - the ones who would have left positive reviews - and the platform prevented them from doing so.&lt;/p&gt;
&lt;p&gt;Even though I didnt know this at the time, I would have still given the promo codes.&lt;/p&gt;
&lt;h3 id="android-refunds"&gt;Android refunds&lt;/h3&gt;
&lt;p&gt;Android allows refunds within 2 hours of purchase, no questions asked. Out of the 20 orders I got, I had &lt;strong&gt;17 refunds&lt;/strong&gt;. Ouch.&lt;/p&gt;
&lt;p&gt;When I first saw them, I was confused. The game wasn't broken, so what went wrong?&lt;/p&gt;
&lt;p&gt;My best guess? A combination of things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Most people refunded within 15 minutes, so they probably didn't like the look of it or bought it by mistake.&lt;/li&gt;
&lt;li&gt;Maybe some played longer and felt 30 levels wasn't enough content for the price.&lt;/li&gt;
&lt;li&gt;The description and screenshots might not have communicated what the game actually is and the lack of polish probably didn't help either.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The frustrating part? No feedback. Just money taken back. If they'd told me &lt;em&gt;why&lt;/em&gt; they didn't like it, I could have fixed it for them or future users.&lt;/p&gt;
&lt;h3 id="procrastination"&gt;Procrastination&lt;/h3&gt;
&lt;p&gt;Most people have completed all 30 levels. I didn't realise how hard it would be to add a new theme and how much work it would entail. Some of my previous architectural decisions came back to haunt me.&lt;/p&gt;
&lt;p&gt;After the Android release in July 2025, the project was put on the back burner for six months. Not because of work and life (though that was a factor). I was procrastinating because building new themes felt too hard. Everything was stuffed into MonoBehaviours with very few abstractions. This made it nearly impossible to write automated tests, and adding features meant testing everything every time I made a change.&lt;/p&gt;
&lt;p&gt;Eventually, I bit the bullet and refactored the level selection screen/system and added automated tests. It took over a month. But it gave me confidence to make changes in that area without breaking everything.&lt;/p&gt;
&lt;h3 id="one-negative-review"&gt;One negative review&lt;/h3&gt;
&lt;p&gt;I also got &lt;strong&gt;one&lt;/strong&gt; negative review.  It seemed the person didn't understand how the game worked, and it didn't meet their expectations. That stung, but it was valuable feedback. Probably, if they were on Android, they would have just gotten a refund.&lt;/p&gt;
&lt;p&gt;Based on that review, I updated the description and made the instructions a lot clearer.&lt;/p&gt;
&lt;h2 id="show-me-the-money-numbers"&gt;Show me the &lt;del&gt;money&lt;/del&gt; numbers&lt;/h2&gt;
&lt;p&gt;Let's talk numbers.&lt;/p&gt;
&lt;p&gt;I built this on nights and weekends with help from Manjari.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Total cost:&lt;/strong&gt; About NZD 550 (dev accounts, IDE, etc).&lt;br&gt;
&lt;strong&gt;Total proceeds:&lt;/strong&gt; About NZD 110 (after Apple's and Google's cut but before taxes, etc)&lt;/p&gt;
&lt;p&gt;Forget "retirement money", this is not even "cover your costs money".&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iOS:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total Downloads: 29&lt;/li&gt;
&lt;li&gt;Total Orders: 17&lt;/li&gt;
&lt;li&gt;Total Revenue: NZD 127&lt;/li&gt;
&lt;li&gt;Proceeds (after Apple's cut): NZD 98&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Android (launched July 2025):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total Downloads: 35&lt;/li&gt;
&lt;li&gt;Total Orders: 20, but 17 refunds :(&lt;/li&gt;
&lt;li&gt;Total Revenue: NZD 22&lt;/li&gt;
&lt;li&gt;Proceeds (after Google's cut): NZD 17&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Monthly breakdown:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;iOS Units&lt;/th&gt;
&lt;th&gt;Android Units&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nov 2024&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dec 2024&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jan 2025&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mar 2025&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 2025&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jul 2025&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 2025&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sep 2025&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dec 2025&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most sales happened in the first month (no surprise), but I still got sporadic purchases throughout the year. iOS went completely silent from Jul to Nov - probably because I did no marketing and very few updates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Geographic Distribution:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Territory&lt;/th&gt;
&lt;th&gt;iOS Sales/Total Downloads&lt;/th&gt;
&lt;th&gt;Android Sales/Total Downloads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;8/13&lt;/td&gt;
&lt;td&gt;1/8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;7/9&lt;/td&gt;
&lt;td&gt;2/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New Zealand&lt;/td&gt;
&lt;td&gt;0/5&lt;/td&gt;
&lt;td&gt;0/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia&lt;/td&gt;
&lt;td&gt;1/1&lt;/td&gt;
&lt;td&gt;0/1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canada&lt;/td&gt;
&lt;td&gt;1/1&lt;/td&gt;
&lt;td&gt;0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;United Kingdom&lt;/td&gt;
&lt;td&gt;0/0&lt;/td&gt;
&lt;td&gt;0/1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Unsuprisingly, most downloads came from India, USA and New Zealand - those areas are where I focused my (minimal) outreach and gave out promo codes. The download from Canada was unexpected. I didn't speak to anyone there.&lt;/p&gt;
&lt;p&gt;Nearly half of my downloads came from organic App Store search. Not bad for zero marketing efforts.&lt;/p&gt;
&lt;p&gt;Someone even discovered the game in December 2025, a full year after its launch. I only found out from my monthly report, but that was a nice surprise. Even though I don't know what they were searching for, I am glad they bought it.&lt;/p&gt;
&lt;h2 id="shipped-improvements"&gt;Shipped improvements&lt;/h2&gt;
&lt;p&gt;Even though updates have been sporadic, watching my daughter play and looking at the analytics showed me what needed fixing. Some of the highlights from the past year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Contextual hints:&lt;/strong&gt; Kids were getting stuck. I added clues that unlock as they play, helping them think about the word differently without giving away the answer. Analytics show that this is a very popular feature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save progress:&lt;/strong&gt; Kids would start a level, leave, and have to start over. Before I added hints, some of them, including my daughter, would quit the level after attempt 6, and restart it essentially having unlimited tries to figure out the word. Kids are smart, and they will find ways to &lt;del&gt;break your game&lt;/del&gt; game the system. Saving their progress not only closed this loophole but also allowed them to take breaks and pick up where they left off.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web demo:&lt;/strong&gt; In response to the refunds, low purchase volume, and negative feedback, I added a &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt; a few months ago so people can try it before committing to buy. I still don't know if it has made any difference.&lt;/li&gt;
&lt;li&gt;Squashing all the bugs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="whats-next"&gt;What's Next?&lt;/h2&gt;
&lt;p&gt;Now that I've refactored some of the code, I can actually add content without spending hours clicking through the screens. I still need to refactor the main game screen though :(&lt;/p&gt;
&lt;p&gt;New theme dropping in February - &lt;strong&gt;body parts&lt;/strong&gt;. It's already in active testing and I'm working out the kinks.&lt;/p&gt;
&lt;p&gt;I'm also going to be adding the things that make it feel like a complete game, not just a prototype that shipped: proper menus, settings, polish. The stuff I cut to hit that December 1st deadline.&lt;/p&gt;
&lt;p&gt;I've figured out a sustainable schedule with a plan to write an update post every month. In fact, one has already gone out in January.&lt;/p&gt;
&lt;h2 id="so.was-it-worth-it"&gt;So… Was It Worth It?&lt;/h2&gt;
&lt;p&gt;Yes. Not because I made a ton of money (I didn't). Not because I have thousands of users (I don't).&lt;/p&gt;
&lt;p&gt;Eventually, this might actually be profitable. Even now I can claim some of the expenses to reduce my tax burden.&lt;/p&gt;
&lt;p&gt;But even if it never makes me a ton of money, I crossed items off my bucket list, got a creative outlet from professional work, and got to see my daughter play something I built for her. You can't put a price tag on that.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Sitting on a side project that's "almost ready"? Ship it. I'd love to hear what happens.&lt;/p&gt;
&lt;p&gt;Lost money on something you built but would do it again? Tell me about it.&lt;/p&gt;
&lt;p&gt;Want to be a beta tester and get updates earlier? Let me know!&lt;/p&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;    
&lt;p&gt;Want to try before you buy? Check out the &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt;&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer Update 10 : Fireworks, Levels, Polish</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-10</link>
			<description>Wordle for Kids update - 6 new levels, more fireworks, and smoother transitions. Plus: why I finally added automated tests.</description>
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-10</guid>
			<pubDate>Tue, 06 Jan 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s been a while since I wrote an update on this blog.&lt;/p&gt;
&lt;p&gt;Not because there were no update. Well, for some time that was true. You know work and life and all that. But the good news is that I have finally figured out a sustainable schedule, and updates have been going out in the app/Play Store. However, they didn't feel "blog-worthy." And let's be honest: once you miss one update, it’s surprisingly easy to miss the next one too.&lt;/p&gt;
&lt;p&gt;Anyway, here’s what’s changed.&lt;/p&gt;
&lt;h2 id="more-fireworks"&gt;More fireworks&lt;/h2&gt;
&lt;p&gt;I added more fireworks on level completion.&lt;/p&gt;
&lt;p&gt;It sounds trivial when I write it out like that. But you can never have too many fireworks.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-10/fireworks.gif" alt="fireworks"&gt;&lt;/p&gt;
&lt;h2 id="scene-transitions"&gt;Scene transitions&lt;/h2&gt;
&lt;p&gt;I also added scene transitions between screens.&lt;/p&gt;
&lt;p&gt;This is another one of those trivial changes. But, it makes the game feel less like a collection of screens and more like... well, a game.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-10/scene_transitioner.gif" alt="scene transitioner"&gt;&lt;/p&gt;
&lt;h2 id="level-select-now-opens-at-your-highest-unlocked-level"&gt;Level select now opens at your highest unlocked level&lt;/h2&gt;
&lt;p&gt;The level selection screen now opens on the page with your highest unlocked level.&lt;/p&gt;
&lt;p&gt;This is mostly just convenience (less clicking is always a win). This wasnt so much of an issue when it was just 3 screens but as I add themes and more levels to the game, this will make it much easier to navigate.&lt;/p&gt;
&lt;h2 id="more-levels"&gt;6 more levels&lt;/h2&gt;
&lt;p&gt;Since there are 12 levels on the level selection screen, ensuring each theme has a multiple of 12 levels makes the transitions much smoother. It's a little design hack that's going to make it much easier to add themes. But, you get 6 more levels while you wait for the next update.&lt;/p&gt;
&lt;h2 id="behind-the-scenes-refactoring-automated-tests"&gt;Behind the scenes: refactoring + automated tests&lt;/h2&gt;
&lt;p&gt;Alongside the visible changes, I’ve been refactoring the code and adding automated tests to make themes possible without turning every future change into a risky one.&lt;/p&gt;
&lt;p&gt;There was a point where I realised I was spending more time manually clicking through the app to make sure I hadn't broken anything than I was actually building new features. You know the drill: change one line of code, spend 20 minutes tapping screens to ensure everything still works.&lt;/p&gt;
&lt;p&gt;It was exhausting. And frankly, it was boring. And worst of all, it was leading me to procrastinate.&lt;/p&gt;
&lt;p&gt;Automated tests give me the confidence to move fast without that constant fear that I'm breaking something. If you’ve ever maintained a side project alone, you probably know exactly what I mean.&lt;/p&gt;
&lt;h2 id="what-theme-should-i-build-next"&gt;What theme should I build next?&lt;/h2&gt;
&lt;p&gt;I’ve been promising this for a while, and it’s finally (almost) here. I’ve made nearly all the changes needed to support more themes. I am just testing it out before I release it to everyone.&lt;/p&gt;
&lt;p&gt;The frontrunner for me right now is &lt;strong&gt;body parts&lt;/strong&gt;. It feels like a good “kid vocabulary” theme, and I think it could lead to levels that are fun without being too hard.&lt;/p&gt;
&lt;p&gt;But, I need your help. If you were picking a theme, what would you want to see?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Body parts&lt;/li&gt;
&lt;li&gt;Animals&lt;/li&gt;
&lt;li&gt;Nature&lt;/li&gt;
&lt;li&gt;Sports&lt;/li&gt;
&lt;li&gt;Transportation&lt;/li&gt;
&lt;li&gt;In the house&lt;/li&gt;
&lt;li&gt;Action words&lt;/li&gt;
&lt;li&gt;Something else&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What would you pick (and why)?&lt;/p&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Want to try before you buy? Check out our &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thank you for being a part of this journey with me! I'm eager to hear your thoughts and feedback. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Story Points Aren't the Problem</title>
			<link>https://www.ankursheel.com/blog/story-points-arent-problem</link>
			<description>Sprint planning shouldn’t be a time suck. Learn how story points get misused—and how to fix the process for good.</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/story-points-arent-problem-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/story-points-arent-problem</guid>
			<pubDate>Sat, 26 Jul 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Like many developers, I hate story pointing.&lt;/p&gt;
&lt;p&gt;It feels like I’ve wasted &lt;em&gt;years&lt;/em&gt; of my life in sprint planning sessions that spiral into arguments over whether something is a 2 or a 3. I’ve also sat through too many sprint planning sessions watching teams treat story points as time estimates, and walk away with no more clarity than when they started.&lt;/p&gt;
&lt;p&gt;It all feels likes a performance that gives &lt;del&gt;management&lt;/del&gt; everyone a false sense of control.&lt;/p&gt;
&lt;p&gt;In fact, some people just do away with story points altogether, and I get it. I was once in that camp too.&lt;/p&gt;
&lt;p&gt;But, what I if told you&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;story points aren't the problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’ve noticed three common mistakes that make story points and the whole sprint planning exercise feel like a giant waste of time. Let's see what they are and how we can fix them to make story-pointing not suck.&lt;/p&gt;
&lt;h2 id="mistake-1-treating-story-points-as-a-proxy-for-time"&gt;Mistake #1: Treating Story Points as a Proxy for Time&lt;/h2&gt;
&lt;p&gt;How often have you heard this in a sprint planning session -&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This should take 1 week, so… five story points?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s not how story points work. They’re not about time.&lt;/p&gt;
&lt;p&gt;A 3-point story might take one day or more than a week. I know you dont want to hear it but it doesn't matter.&lt;/p&gt;
&lt;p&gt;Story points should reflect the &lt;strong&gt;complexity&lt;/strong&gt; of the task.&lt;/p&gt;
&lt;p&gt;Here’s a better way to think about them:&lt;/p&gt;
&lt;h3 id="point"&gt;1 Point&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Potential Traits:&lt;/strong&gt; No unknowns, low risk, minimal code changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding logs to existing code&lt;/li&gt;
&lt;li&gt;Trivial changes behind a feature flag&lt;/li&gt;
&lt;li&gt;Cleaning up a simple feature flag&lt;/li&gt;
&lt;li&gt;Mechanical refactoring&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="points"&gt;2 Points&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Potential Traits:&lt;/strong&gt; Low risk, some minor unknowns, small test updates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple changes behind a feature flag&lt;/li&gt;
&lt;li&gt;Creating a new dashboard with existing data&lt;/li&gt;
&lt;li&gt;Cleaning up a complex feature flag&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="points-1"&gt;3 Points&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Potential Traits:&lt;/strong&gt; Some unknowns, moderate risk, might require coordination with others.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fixing a bug with a known root cause&lt;/li&gt;
&lt;li&gt;New functionality that can be feature flagged cleanly&lt;/li&gt;
&lt;li&gt;Work that involves coordination with another developer&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="points-2"&gt;5 Points&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Potential traits:&lt;/strong&gt; Some unknowns, large code changes, lots of new tests, external dependencies&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technical investigation for new features or bugs&lt;/li&gt;
&lt;li&gt;Work that's difficult to feature flag&lt;/li&gt;
&lt;li&gt;Cross-team coordination&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are good candidates to split into smaller tickets once the investigation is done.&lt;/p&gt;
&lt;h3 id="points-3"&gt;8 Points&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Potential traits:&lt;/strong&gt; Many unknowns, risky changes, hard to estimate confidently.&lt;/p&gt;
&lt;p&gt;If you’re estimating something at 8 points, you probably need a spike first. Timebox it, then use what you’ve learned to create smaller, pointable stories.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/story-points-arent-problem/infographic.png" alt="Storypoint Infographic"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: You can download the full sized infographic from &lt;a href="https://www.ankursheel.com/assets/images/posts/story-points-arent-problem/infographic_original.png"&gt;Printable Infographic&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="mistake-2-estimating-in-isolation"&gt;Mistake #2: Estimating in Isolation&lt;/h2&gt;
&lt;p&gt;Ever had a product owner or a tech lead show up to planning with stories already estimated?&lt;/p&gt;
&lt;p&gt;Estimation is a team sport. Different people catch different risks, assumptions, and edge cases. Getting everyone involved builds shared understanding and better team cohesion.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The number matters less than the conversation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here’s what to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The feature lead fleshes out the ticket ahead of planning.&lt;/li&gt;
&lt;li&gt;They give a 30-second overview during the session and answer questions.
&lt;ul&gt;
&lt;li&gt;No need to dive into every detail - but enough for the team to understand the &lt;strong&gt;why&lt;/strong&gt;, the &lt;strong&gt;approach&lt;/strong&gt;, and have the &lt;strong&gt;confidence&lt;/strong&gt; to estimate complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Then use Planning Poker to uncover different perspectives.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mistake-3-arguing-over-2-vs-3-points"&gt;Mistake #3: Arguing Over 2 vs 3 Points&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/story-points-arent-problem/two_vs_three.png" alt="two vs three"&gt;&lt;/p&gt;
&lt;p&gt;You know the moment. One person says it’s a 2. Another insists it’s a 3. Suddenly, you’re 20 minutes deep into a conversation that doesn’t matter.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You’re optimizing for precision in a system designed for approximation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s not just wasteful — it completely misses the point (pun unintended).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Timebox these debates.&lt;/strong&gt; If you can't agree in 1-2 minutes, pick the higher number and move on. It's not worth derailing the whole session.&lt;/p&gt;
&lt;p&gt;However, if there’s a big delta — say, someone thinks it’s a 2 and someone else thinks it’s a 5 — that is worth unpacking. Someone’s probably missing something.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="final-thoughts"&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Story points aren't about precision. They're about shared clarity.&lt;/p&gt;
&lt;p&gt;Used well, they build shared context. Used poorly, they fuel endless debates.&lt;/p&gt;
&lt;p&gt;So next time you're estimating ask yourself:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do we actually understand the scope, complexity, and purpose of this work?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the answer’s yes, the number doesn't matter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What’s your biggest frustration with story points — and how have you worked around it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Drop a comment and send this to someone who’s still fighting over 2 vs 3.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer Update #9: Save your guesses</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-9</link>
			<description>WordXplorer Update #9 introduces a convenient save feature that remembers your guessed words when you exit a level, allowing you to pick up right where you left off for uninterrupted gameplay.</description>
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-9</guid>
			<pubDate>Sun, 08 Jun 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="save-your-guesses"&gt;Save your guesses&lt;/h2&gt;
&lt;p&gt;Your guesses are now saved, letting you take as long as you need to solve each level without starting over.&lt;/p&gt;
&lt;p&gt;When you return to a level, you’ll see all your previous guesses, allowing you to continue right where you left off. Once you successfully complete a level, all your guesses for that puzzle will be cleared, giving you a fresh start when you decide to replay it.&lt;/p&gt;
&lt;p&gt;Here's how it works in action:&lt;/p&gt;
&lt;div style="position: relative;width: 100%;padding-bottom: 56.25%;margin: 1rem 0;"&gt;
  &lt;iframe src="https://www.youtube.com/embed/R5ZXlpfyUZY" title="Watch the Save State in action" style="position: absolute;width: 100%;height: 100%;left: 0;top: 0;right: 0;" allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;  
&lt;h2 id="bug-fixes"&gt;Bug Fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I've resolved the issue where the keyboard colour changed to white after moving to the next level.&lt;/li&gt;
&lt;li&gt;I've also fixed the problem where the latest level showed up as locked when you went back to the level selection screen after finishing a level.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Want to try before you buy? Check out our &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thank you for being a part of this journey with me! I'm eager to hear your thoughts and feedback. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer Update #8: New Hint System</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-8</link>
			<description>WordXplorer Update 8 introduces a new hint system with contextual clues to help kids learn and solve words more effectively</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-8-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-8</guid>
			<pubDate>Sat, 10 May 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="new-hint-system"&gt;New Hint System&lt;/h2&gt;
&lt;p&gt;We're excited to introduce our new hint system! This feature helps players when they're stuck, making the game more accessible while still maintaining the challenge.&lt;/p&gt;
&lt;p&gt;The hint system provides contextual clues about the word.&lt;/p&gt;
&lt;p&gt;Here's how the hints look when locked and unlocked:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The hint popup at the start of the game, showing all hints are locked&lt;/th&gt;
&lt;th&gt;The hint popup after 2 hints have been unlocked, showing the available clues&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-8/locked_hints.jpg" alt="Locked Hints"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-8/unlocked_hints.jpg" alt="Unlocked Hints"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And here's how it works in action:&lt;/p&gt;
&lt;div style="position: relative;width: 100%;padding-bottom: 56.25%;margin: 1rem 0;"&gt;
  &lt;iframe src="https://www.youtube.com/embed/RBW15-Eg97E" title="Watch the hint system in action" style="position: absolute;width: 100%;height: 100%;left: 0;top: 0;right: 0;" allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;These clues help kids think about the word in a different way, making the game both educational and fun. They can use them strategically to learn and improve their word-solving skills.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="get-wordxplorer"&gt;Get WordXplorer&lt;/h2&gt;
&lt;p&gt;WordXplorer is available on the iOS App Store&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Want to try before you buy? Check out our &lt;a href="https://wordxplorer.ankursheel.com/"&gt;web demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thank you for being a part of this journey with me! I'm eager to hear your thoughts and feedback. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Offset vs Cursor Based Pagination</title>
			<link>https://www.ankursheel.com/blog/offset-vs-cursor-based-pagination</link>
			<description>Understand SQL pagination with practical examples, comparing offset and cursor methods to manage datasets efficiently.</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/offset-vs-cursor-based-pagination-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/offset-vs-cursor-based-pagination</guid>
			<pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;SQL pagination helps manage large datasets by breaking them into smaller, more manageable chunks called 'pages.' It prevents overwhelming applications by retrieving data one page at a time, ensuring smoother performance and a better user experience.&lt;/p&gt;
&lt;p&gt;In this article, I'll discuss two primary SQL pagination methods—offset and cursor-based—and explore an advanced technique for efficiently handling large datasets, highlighting their specific use cases and benefits.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/offset-vs-cursor-based-pagination/cover.png" width="300" alt="A robot holding a book with turning pages, trying to find a specific page"&gt;&lt;/p&gt;
&lt;h2 id="offset-pagination"&gt;Offset Pagination&lt;/h2&gt;
&lt;p&gt;Offset pagination involves skipping a specified number of rows and then taking a fixed number of rows, allowing for flexible data retrieval.&lt;/p&gt;
&lt;h3 id="example"&gt;Example&lt;/h3&gt;
&lt;p&gt;Here's a typical query using offset pagination:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT *         
FROM product        
WHERE category = 1        
ORDER BY id         
LIMIT @pageSize         
OFFSET $offset;        
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;LIMIT&lt;/code&gt; clause defines how many rows to return, while &lt;code&gt;OFFSET&lt;/code&gt; skips a specified number of rows. &lt;code&gt;ORDER BY&lt;/code&gt; ensures results are consistent.&lt;/p&gt;
&lt;p&gt;Typically, &lt;code&gt;pageSize&lt;/code&gt; and &lt;code&gt;offset&lt;/code&gt; are set based on user input or application logic to control pagination dynamically. For example, if &lt;code&gt;pageSize&lt;/code&gt; is 100 and &lt;code&gt;offset&lt;/code&gt; is 200, the query retrieves rows 200-300.&lt;/p&gt;
&lt;h3 id="advantages"&gt;Advantages&lt;/h3&gt;
&lt;p&gt;Offset pagination allows you to jump directly to a specific set of records, which is useful for applications requiring quick access to specific data or "go to page X" functionality. For instance, you can easily access a specific range like 3000-3100.&lt;/p&gt;
&lt;p&gt;It is easier to implement, making it useful for smaller systems or static datasets like blog archives or product lists.&lt;/p&gt;
&lt;h3 id="disadvantages"&gt;Disadvantages&lt;/h3&gt;
&lt;p&gt;Offset pagination requires scanning all preceding rows to find the starting point. This can be inefficient and slow as the dataset grows. Performance degrades with larger datasets, as scanning more rows leads to slower response times.&lt;/p&gt;
&lt;p&gt;For example, in a query like &lt;code&gt;SELECT * FROM product ORDER BY id LIMIT 100 OFFSET 100000&lt;/code&gt;, Postgres scans and discards 100,000 rows even though only 100 are needed.&lt;/p&gt;
&lt;p&gt;While offset-based pagination is common in legacy systems, its utility in modern systems may be limited compared to cursor-based pagination.&lt;/p&gt;
&lt;h2 id="cursor-based-pagination"&gt;Cursor-based Pagination&lt;/h2&gt;
&lt;p&gt;Unlike offset pagination, cursor-based pagination fetches results using a unique identifier (or a combination of columns), avoiding the need to skip rows.&lt;/p&gt;
&lt;h3 id="example-1"&gt;Example&lt;/h3&gt;
&lt;p&gt;Here's how you might implement cursor-based pagination:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT *         
FROM product         
WHERE id &amp;gt; $lastId        
    AND category = 1        
ORDER BY id         
LIMIT $pageSize;        
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that, unlike the previous example, this query doesn't use &lt;code&gt;OFFSET&lt;/code&gt;. Instead, we keep track of the &lt;code&gt;id&lt;/code&gt; of the last record retrieved from the previous page and use it in the &lt;code&gt;WHERE&lt;/code&gt; clause to fetch the next set of records.&lt;/p&gt;
&lt;p&gt;To maintain reliable pagination, it's essential to ensure the cursor column is unique and immutable, as changes could disrupt the order of results. A good candidate is a primary key ID or a &lt;code&gt;CreatedTimestamp&lt;/code&gt; column.&lt;/p&gt;
&lt;h3 id="advantages-1"&gt;Advantages&lt;/h3&gt;
&lt;p&gt;Cursor-based pagination is generally more efficient, as it avoids scanning and discarding rows. Instead, it directly jumps to records based on the cursor value. This approach remains efficient even for large offsets because the database can quickly use an index on the cursor column to find the starting point.&lt;/p&gt;
&lt;p&gt;Cursor-based pagination provides stable results even if new records are inserted or deleted between page requests. Each page contains the expected set of records based on the cursor value.&lt;/p&gt;
&lt;h3 id="disadvantages-1"&gt;Disadvantages&lt;/h3&gt;
&lt;p&gt;Cursor-based pagination requires sequential page traversal and is more complex to implement because you must track the cursor value in your application code. This might be less convenient for users who want to navigate directly to a specific page.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; However, we can avoid the need for sequential page traversal by using PageInfo/PageCursor in GraphQL.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We need to be mindful of &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html"&gt;IDOR (Insecure Direct Object References)&lt;/a&gt;. If IDs are sensitive, we should encrypt cursor values with a random salt.&lt;/p&gt;
&lt;p&gt;It can become quite complex if you need extra features like sorting on multiple columns, etc.&lt;/p&gt;
&lt;h2 id="advanced-cursor-pagination-techniques"&gt;Advanced Cursor Pagination Techniques&lt;/h2&gt;
&lt;p&gt;When dealing with extremely large datasets, an advanced cursor-based technique can further optimise performance.&lt;/p&gt;
&lt;p&gt;In my experience using the basic cursor-based example on a table with 163 million rows, where only 300K rows met the &lt;code&gt;WHERE&lt;/code&gt; clause criteria, the query timed out even with a &lt;code&gt;LIMIT&lt;/code&gt; of 1000. This occurred because the query had to scan too many rows before finding 1000 that matched the criteria.&lt;/p&gt;
&lt;p&gt;Now, you might wonder why it would need to scan so many rows if you have an index. The index's benefit is that it lets the database quickly locate the starting point. Imagine that the first record is in row 1. Now, the second record is after 1 million rows. To reach the limit of 1000, at least 1 million rows will need to be scanned. Even though there is an index, it can (and probably will) result in a timeout because it requires scanning many non-matching rows to find the ones that match.&lt;/p&gt;
&lt;p&gt;In such scenarios, it's crucial to limit the number of rows scanned during each query execution. This can be achieved by adding a constraint that ensures efficient data retrieval, even if the &lt;code&gt;LIMIT&lt;/code&gt; is not fully met.&lt;/p&gt;
&lt;h3 id="example-2"&gt;Example&lt;/h3&gt;
&lt;p&gt;Here's an updated query:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT *         
FROM product         
WHERE id &amp;gt; $lastId        
    AND id &amp;lt;= $clampId        
    AND category = 1        
ORDER BY id         
LIMIT $pageSize;        
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;clampId&lt;/code&gt; serves as an upper boundary, calculated as &lt;code&gt;lastId&lt;/code&gt; plus a predefined range, to limit the number of rows scanned per query execution.&lt;/p&gt;
&lt;p&gt;For instance, if &lt;code&gt;lastId&lt;/code&gt; is 1000 and the scan limit is 10,000 rows, set &lt;code&gt;clampId&lt;/code&gt; to 11,000. The &lt;code&gt;LIMIT&lt;/code&gt; remains 1,000, ensuring immediate return if 1,000 matches are found or scanning at most 10,000 rows.&lt;/p&gt;
&lt;p&gt;By clamping the range, we can significantly reduce query execution time, making it feasible to handle large datasets without overwhelming the system.&lt;/p&gt;
&lt;h3 id="using-non-sequential-identifiers"&gt;Using Non-sequential Identifiers&lt;/h3&gt;
&lt;p&gt;While in the examples, the &lt;code&gt;id&lt;/code&gt; is assumed to be numeric and sequential, it doesn't need to be to implement cursor-based pagination. We can, for instance, combine multiple fields, e.g., &lt;code&gt;&amp;lt;last_updated_at&amp;gt;&lt;/code&gt; for sorting and &lt;code&gt;&amp;lt;last_id&amp;gt;&lt;/code&gt; as a UUID to get the last item on the current page.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT *       
FROM product      
WHERE (last_updated_at &amp;lt; $lastUpdatedAt)      
 OR (last_updated_at = $lastUpdatedAt AND last_id &amp;gt; $lastId)      
ORDER BY last_updated_at DESC, last_id      
LIMIT $pageSize;      
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Choosing the right pagination approach ensures optimal application performance and user satisfaction, regardless of dataset size.&lt;/p&gt;
&lt;p&gt;While offset pagination is simpler to implement, it suffers from significant performance degradation at scale. In contrast, cursor pagination maintains consistent performance regardless of page depth.&lt;/p&gt;
&lt;p&gt;However, offset-based pagination allows us to arbitrarily jump to any page. With cursor pagination, we have to traverse pages sequentially or use more complex techniques.&lt;/p&gt;
&lt;p&gt;The choice between these two approaches ultimately depends on your use case. Choose cursor pagination for high-performance needs with large datasets. Opt for offset pagination when dataset size is manageable, and random page navigation is required.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer #7: New Video &amp; Food Theme</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-7</link>
			<description>WordXplorer is now available for preorder on the App Store with special pricing before its December 1st launch. This updates also features a preview video and plans for an Android release.</description>
			<enclosure url="https://www.ankursheel.com/assets/images/preorder-appstore-badge.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-7</guid>
			<pubDate>Tue, 22 Oct 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I hope you’re having an awesome day! Since the last post, we’ve been hard at work, appeasing the Apple gods to ensure our game is ready for the App Store.&lt;/p&gt;
&lt;p&gt;And guess what? I’m thrilled to announce that our game has been approved.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/wordxplorer-guess-the-word/id6504664783" target="\_blank"&gt;&lt;img src="https://www.ankursheel.com/assets/images/preorder-appstore-badge.png" alt="Preorder WordXplorer"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="new-video-alert"&gt;🎥 New Video Alert!&lt;/h2&gt;
&lt;p&gt;I am excited to share a sneak preview of our brand-new video. You can catch this exclusive glimpse on our product page. It’s a taste of the adventure that awaits!&lt;/p&gt;
&lt;div style="position: relative;width: 100%;padding-bottom: 56.25%;margin: 1rem 0;"&gt;
  &lt;iframe src="https://www.youtube.com/embed/bAtj9bAYVS0/0" title="WordXplorer Preview Video" style="position: absolute;width: 100%;height: 100%;left: 0;top: 0;right: 0;" allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="exciting-new-theme"&gt;🎨 Exciting New Theme&lt;/h2&gt;
&lt;p&gt;To make things fun and engaging for kids, we’ve decided to theme the words around “Food Items.” There are a few reasons we have chosen this theme as the first one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can easily create 25-30 exciting levels for launch.&lt;/li&gt;
&lt;li&gt;Kids are familiar with food items and having all words around this central theme should make it easy for them to get started.&lt;/li&gt;
&lt;li&gt;Most importantly, we believe this theme will be a hit with children.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="special-preorder-pricing"&gt;💰 Special Preorder Pricing&lt;/h2&gt;
&lt;p&gt;You can preorder the game for just USD 3.99. Apple will use this base price and adjust it for your region.&lt;/p&gt;
&lt;p&gt;Remember, once we launch on December 1st, the price will increase to USD 4.99. So, why wait? Secure your copy now!&lt;/p&gt;
&lt;h2 id="how-to-preorder"&gt;🛒 How to Preorder&lt;/h2&gt;
&lt;p&gt;Pre-ordering is simple and ensures you’re among the first to experience the game:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Visit the &lt;a href="https://apps.apple.com/us/app/wordxplorer-guess-the-word/id6504664783"&gt;game’s product page on the App Store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the “Preorder” button.&lt;/li&gt;
&lt;li&gt;Follow the prompts to complete your preorder. By pre-ordering, you lock in the special price and guarantee immediate access on launch day!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rest assured, you will only be charged once the game officially launches on December 1st. And if you change your mind(I hope you don’t), you can cancel your preorder anytime before launch without penalty.&lt;/p&gt;
&lt;p&gt;For more details, feel free to check out &lt;a href="https://support.apple.com/en-nz/118414"&gt;Apple’s Preorder documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="google-play-progress"&gt;📱 Google Play Progress&lt;/h2&gt;
&lt;p&gt;Exciting news for our Android fans! I have a working proof of concept for the game on Android devices.&lt;/p&gt;
&lt;p&gt;While bringing the game to Google Play is definitely on our radar, our primary focus is ensuring a smooth and successful launch on the App Store.&lt;/p&gt;
&lt;p&gt;Rest assured, expanding to Android is part of our plans. We appreciate your patience and support as we prioritise the App Store release. Stay tuned for more updates!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="last-chance-for-playable-build"&gt;🕹️ Last Chance for Playable Build&lt;/h2&gt;
&lt;p&gt;This is your final opportunity to check out the latest update to our &lt;a href="https://golden-pony-d2c3f0.netlify.app/"&gt;Playable Web Build&lt;/a&gt; or TestFlight build. Your feedback would mean the world to us! If you’re eager to have the full experience, just let me know, and I’ll send you a TestFlight invite immediately. 😊&lt;/p&gt;
&lt;h2 id="whats-next"&gt;🚀 What’s Next&lt;/h2&gt;
&lt;p&gt;With a hard deadline in sight, we’re focused on adding those final touches to the game. We’re also building out the concept of theming levels into the gameplay to ensure a smooth experience for launch day. I am also trying to get in the Apple Small Business program to reduce the percentage of commission that they charge.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Know someone who’d love to try the game before its official release? &lt;del&gt;Share this &lt;a href="https://tally.so/r/wverVQ"&gt;form&lt;/a&gt; with
them—they’ll thank you!! ✨&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 26th December 2024:&lt;/em&gt;&lt;/strong&gt; You can now download the game.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Does your child have a favourite four-letter food they’d love to see in WordXplorer? Let me know, and I’ll do my best to add it to the game!&lt;/p&gt;
&lt;p&gt;Thank you for joining us on this journey. I can’t wait for you to dive into the game and share your thoughts!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Fun with (Feature) Flags</title>
			<link>https://www.ankursheel.com/blog/fun-feature-flags</link>
			<description>Let’s explore what feature flags are, how they work, and their pros and cons compared to some traditional approaches</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/fun-feature-flags-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/fun-feature-flags</guid>
			<pubDate>Fri, 06 Sep 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Feature flags give developers the ability to turn features on or off without deploying new code. This flexibility is key for managing the release of new features while allowing for controlled and incremental deployments.&lt;/p&gt;
&lt;p&gt;I still have conversations with several teams that are using development methods that don’t include feature flags. Some of them want to move to feature flags but are unsure how to start while others have never heard of them or don't think it's worth adopting.&lt;/p&gt;
&lt;p&gt;Let’s explore how feature flags work, and their pros and cons compared to some other approaches.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is loosely based on a presentation I had given at FirstAML.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/fun-feature-flags/fun-with-flags.jpg" alt="Flags"&gt;&lt;/p&gt;
&lt;h2 id="other-approaches"&gt;Other Approaches&lt;/h2&gt;
&lt;p&gt;Before diving into feature flags, let’s look at some &lt;del&gt;traditional&lt;/del&gt; ahem alternative approaches I’ve seen teams use.&lt;/p&gt;
&lt;p&gt;To better understand the differences, let’s consider a scenario where you’re working on a new feature that will allow users to edit the title of their uploaded documents.&lt;/p&gt;
&lt;p&gt;This feature requires changes to the frontend, backend, and database and can be broken down into several tasks:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the database schema to store the new title.&lt;/li&gt;
&lt;li&gt;Update the backend API to accept the new title.&lt;/li&gt;
&lt;li&gt;Update the backend to save the new title.&lt;/li&gt;
&lt;li&gt;Add an audit trail for the updates.&lt;/li&gt;
&lt;li&gt;Update the backend to return the new title.&lt;/li&gt;
&lt;li&gt;Update the frontend to display the new title.&lt;/li&gt;
&lt;li&gt;Update the frontend to allow users to edit the title.&lt;/li&gt;
&lt;li&gt;Add validation to the backend to ensure the new title is valid.&lt;/li&gt;
&lt;li&gt;Add validation to the frontend to ensure the new title is valid.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="approach-1-feature-branches"&gt;Approach 1: Feature Branches&lt;/h3&gt;
&lt;p&gt;A long, long time ago, when SVN was a thing (and even much later) most teams used &lt;strong&gt;Feature Branches&lt;/strong&gt; to manage new features. Typically, each feature would be developed on a separate branch, tested, and then merged into the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;
&lt;p&gt;In our example, you would create a branch for the new title feature, and each task would be developed on this branch. Once all tasks are complete, the branch would be merged into
&lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/fun-feature-flags/branch-hell.jpg" alt="Feature Branch Complexity.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Feature branches were popular because they were straightforward to use and possibly because that was the best we had at that time. They still work well for small-scale projects.&lt;/p&gt;
&lt;p&gt;However, in larger teams, feature branches can create technical debt. Tasks may overlap, and keeping branches in sync becomes more challenging, slowing down the development process.&lt;/p&gt;
&lt;p&gt;Here are some other downsides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since code in feature branches must be fully completed before merging, it often leads to large, complex pull requests with significant changes.&lt;/li&gt;
&lt;li&gt;Common changes like the same typo being fixed in two different branches can cause conflicts.&lt;/li&gt;
&lt;li&gt;Working on features in separate branches can hinder collaboration.&lt;/li&gt;
&lt;li&gt;Rolling a change back is difficult due to the intertwined nature of multiple features in the &lt;code&gt;main&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;Moving on to a new feature before the previous one is merged can result in forgotten branches and ToDo's.&lt;/li&gt;
&lt;li&gt;As branches are long-lived and diverge over time, refactoring becomes challenging.&lt;/li&gt;
&lt;li&gt;Continuous Integration/Continuous Deployment (CI/CD) becomes difficult due to the fragmented nature of feature branches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most teams I know start by using feature branches when they are just 1 or 2 developers. But as the team grows, managing multiple branches becomes increasingly complex, leading to merge conflicts and lower productivity.&lt;/p&gt;
&lt;h3 id="alternative-2-git-flow"&gt;Alternative 2: Git Flow&lt;/h3&gt;
&lt;p&gt;An evolution of feature branches is &lt;strong&gt;Git Flow&lt;/strong&gt;, a branching model I’ve also used with success for certain projects.&lt;/p&gt;
&lt;p&gt;Git Flow uses two main branches (&lt;code&gt;main&lt;/code&gt; and &lt;code&gt;develop&lt;/code&gt;). Feature branches are created off &lt;code&gt;develop&lt;/code&gt;, and once a feature is stable, it is merged into &lt;code&gt;develop&lt;/code&gt;. When &lt;code&gt;develop&lt;/code&gt; is stable, it is merged into &lt;code&gt;main&lt;/code&gt;, which is always production-ready. A &lt;code&gt;release&lt;/code&gt; branch is created from &lt;code&gt;main&lt;/code&gt; for each release and &lt;code&gt;hotfix&lt;/code&gt; branches are created to fix production issues.&lt;/p&gt;
&lt;p&gt;In our example, you would create one or more feature branches off &lt;code&gt;develop&lt;/code&gt; for the new title feature. Once the feature is complete, it would be merged into &lt;code&gt;develop&lt;/code&gt;. When &lt;code&gt;develop&lt;/code&gt; is stable, it would be merged into &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/fun-feature-flags/git-flow.jpg" alt="Git Flow"&gt;&lt;/p&gt;
&lt;p&gt;Git Flow offers several advantages compared to Feature Branches.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It ensures the main branch remains stable and production-ready, aiding in scheduled release cycles.&lt;/li&gt;
&lt;li&gt;It supports different versions, useful for desktop and mobile apps requiring hotfixes or patches.&lt;/li&gt;
&lt;li&gt;It works well for Waterfall or Waterfall-Pretending-to-be-Agile methodologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But it’s not without its downsides.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Merge conflicts can still occur in the &lt;code&gt;develop&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;Multiple features might be coupled together, preventing the release of any feature until all are complete.&lt;/li&gt;
&lt;li&gt;Continuous Deployment (CD) becomes challenging as it requires all features in the &lt;code&gt;develop&lt;/code&gt; branch to be stable before merging into &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my experience, Git Flow is less effective for SaaS and applications where supporting older versions isn’t necessary.&lt;/p&gt;
&lt;h2 id="feature-flags"&gt;Feature Flags&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/fun-feature-flags/sheldon-fun-with-flags.png" alt="Fun with flags"&gt;&lt;/p&gt;
&lt;p&gt;With feature flags, you can deploy code at any time, keeping features behind a switch that you control. This enables a smoother, more flexible development and release process.&lt;/p&gt;
&lt;p&gt;In its simplest form, a boolean flag determines whether a feature is active or not. This flag can be toggled on or off without redeploying the code.&lt;/p&gt;
&lt;h3 id="pros-of-feature-flags"&gt;Pros of Feature Flags&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Production code closely mirrors the development version, reducing integration issues.&lt;/li&gt;
&lt;li&gt;Allows for smaller, isolated changes to be merged, minimizing merge conflict risks.&lt;/li&gt;
&lt;li&gt;Features can be released in line with marketing or launch timelines.&lt;/li&gt;
&lt;li&gt;Smaller pull requests make bug fixes easier, with more confidence in the changes.&lt;/li&gt;
&lt;li&gt;They allow for A/B testing and canary releases, where a feature is gradually rolled out to a subset of users.&lt;/li&gt;
&lt;li&gt;It acts as a kill switch, allowing you to quickly turn off a feature if it causes issues in production.&lt;/li&gt;
&lt;li&gt;Turning a feature on for users carries low risk since it has already been thoroughly tested in a controlled environment.&lt;/li&gt;
&lt;li&gt;Shipping the features to users is a trivial config change.&lt;/li&gt;
&lt;li&gt;Easy to sunset features&lt;/li&gt;
&lt;li&gt;Frontend and backend teams can work in parallel since the feature is not live until the flag is activated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cons-of-feature-flags"&gt;Cons of Feature Flags&lt;/h3&gt;
&lt;p&gt;While feature flags offer many benefits, they come with their own set of challenges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The codebase accumulates immediate technical debt with numerous if-else statements and duplicated code paths.&lt;/li&gt;
&lt;li&gt;You have to test both the flag-on and flag-off paths.&lt;/li&gt;
&lt;li&gt;You have to remember to remove the flag and associated code once the feature is fully deployed.&lt;/li&gt;
&lt;li&gt;Inter-dependencies between flags can lead to complex configurations.&lt;/li&gt;
&lt;li&gt;Feature flags on the client side may reveal in-development features.&lt;/li&gt;
&lt;li&gt;It is difficult to apply flags for specific users for changes available before login.&lt;/li&gt;
&lt;li&gt;Managing feature flags at scale can become complex, particularly when dealing with numerous flags across different environments and user segments. Effective management requires robust tooling and practices to keep configurations organized and manageable. However, tools like LaunchDarkly can streamline this process, making it easy to track and control your flags.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="when-to-use-feature-flags"&gt;When to Use Feature Flags&lt;/h3&gt;
&lt;p&gt;Alright, let's play a little game. I'll list a few scenarios, and you tell me if you should use feature flags or not...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Low risk minor bug fix&lt;/li&gt;
&lt;li&gt;Copy changes&lt;/li&gt;
&lt;li&gt;New Feature&lt;/li&gt;
&lt;li&gt;Adding functionality to an existing feature&lt;/li&gt;
&lt;li&gt;Removing a feature&lt;/li&gt;
&lt;li&gt;Refactor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was a trick question.&lt;/p&gt;
&lt;p&gt;Feature flags are your safety net for almost every scenario above.&lt;/p&gt;
&lt;p&gt;The only exception is refactoring — where your automated tests should catch any issue. You are writing tests right?&lt;/p&gt;
&lt;h3 id="feature-flags-in-practice"&gt;Feature Flags in Practice&lt;/h3&gt;
&lt;p&gt;To use feature flags, start with a configuration per environment that defines the flags and their state. This could be a simple JSON file or a more complex system like LaunchDarkly.&lt;/p&gt;
&lt;p&gt;Here’s a basic config example in JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;{
  "FeatureFlags": {
    "feature1": {
      "enabled": true
    },
    "feature2": {
      "enabled": false
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having a JSON configuration file per environment, allows you to define and manage feature flags across different environments. By toggling the &lt;code&gt;enabled&lt;/code&gt; status, you can control feature visibility and rollout without redeploying code.&lt;/p&gt;
&lt;p&gt;Initially, the flag would be off in all environments - dev, QA and prod. This allows you to develop the feature in isolation.&lt;/p&gt;
&lt;p&gt;The code has &lt;code&gt;if-else&lt;/code&gt; statements to check if the feature is enabled or not.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Check if the feature flag is enabled before executing new logic
if(_featureFlagProvider.IsFeatureEnabled(AvailableFlags.Feature1))
{
    // new logic
}
else
{
    // old logic
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: Developing while the feature is on the dev environment can impact other devs.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once the feature is ready, you can turn the flag on in the dev and test environment for QA testing without exposing it to end users.&lt;/p&gt;
&lt;p&gt;After thorough testing, you can activate the feature flag in production. The feature is now live, but since it was already tested in a controlled environment, the risk is minimal.&lt;/p&gt;
&lt;p&gt;Finally, after the feature has been successfully deployed, and you’re confident it’s stable, it’s important to remove the feature flag and any associated code to prevent technical debt.&lt;/p&gt;
&lt;p&gt;In our example, you would create a feature flag for the new title feature. Each task would be developed behind a feature flag in isolated branches. As each task is completed, it would be merged into the &lt;code&gt;main&lt;/code&gt; branch. Once all tasks are complete, the feature flag would be turned on in the test environment for QA testing. After thorough testing, the feature flag would be activated in production, making the feature live.&lt;/p&gt;
&lt;h3 id="best-practices-with-feature-flags"&gt;Best Practices with Feature Flags&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;While embracing duplication might seem counterintuitive, it’s crucial for managing feature flags effectively. This is not the time to worry about keeping your code DRY. Duplication makes it easier to remove flags later.&lt;/li&gt;
&lt;li&gt;Optimize for removal. Code reviews of feature flag removal should be trivial and the reviewer should be just seeing deleted code.&lt;/li&gt;
&lt;li&gt;Define flags with enums or other types rather than strings to simplify management.&lt;/li&gt;
&lt;li&gt;Set up a process to regularly audit and remove outdated flags.&lt;/li&gt;
&lt;li&gt;Maintain a shared configuration for flags affecting both backend (BE) and frontend (FE) teams.&lt;/li&gt;
&lt;li&gt;Allow turning flags on/off using a FE widget tied to a user.&lt;/li&gt;
&lt;li&gt;Consider creating tools to manage feature flags effectively. Automation can help with flag updates and removal.
&lt;ul&gt;
&lt;li&gt;A widget that allows a developer/QA to toggle flags on/off just for themselves in dev/test environments is immensely useful.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;At FirstAML, I developed an open-source &lt;a href="https://github.com/AnkurSheel/FeatureFlagHelper"&gt;helper app that simplified updating feature flag files from the console&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Abstract the feature flag logic from the rest of the codebase to simplify future changes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="removal-strategies"&gt;Removal Strategies&lt;/h3&gt;
&lt;p&gt;Old feature flags can clutter your codebase and create headaches down the line. By implementing strategic removal practices, you can keep your code clean and efficient.&lt;/p&gt;
&lt;p&gt;Here are a few strategies to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a removal task when you introduce a feature flag.&lt;/li&gt;
&lt;li&gt;Add expiration dates to feature flags that either fail a test once expired or alert the team without failing.&lt;/li&gt;
&lt;li&gt;Limit the number of active feature flags in the system so that before adding a new one you have to clean up an older one.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion-are-feature-flags-right-for-you"&gt;Conclusion: Are Feature Flags Right for You?&lt;/h2&gt;
&lt;p&gt;Feature flags are a powerful tool that should be present in every modern teams toolkit. They offer flexibility, reduce risk, and allow for a more controlled release process. Despite their challenges, they provide significant benefits that often outweigh the drawbacks.&lt;/p&gt;
&lt;p&gt;Feature flags can be particularly advantageous for larger teams or projects with multiple stages of development. For smaller teams or projects, they help manage risk and allow for more flexible deployment strategies, reducing the chances of introducing bugs into production.&lt;/p&gt;
&lt;p&gt;When I joined FirstAML, (even though we were just 2 developers at the time) one of the first things I built was a basic feature flag system. This allowed us to test features in isolation, minimizing risks and enhancing our development process by enabling more controlled releases. They’re not just for big teams — they’re for anyone who values agility and controlled risk.&lt;/p&gt;
&lt;p&gt;If you’re not already using feature flags, I highly recommend exploring them. They can be a game-changer for your development process, enabling you to release features with confidence and control.&lt;/p&gt;
&lt;p&gt;For further reading, check out &lt;a href="https://martinfowler.com/articles/feature-toggles.html"&gt;Martin Fowler’s article on feature toggles&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer #6: New Look, Major Fixes &amp; App Store News</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-6</link>
			<description>New name &amp; icon, major bug fixes, and upcoming App Store release. Test the latest build and give your feedback!</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-6-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-6</guid>
			<pubDate>Mon, 02 Sep 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since we last spoke, it’s been full steam ahead with bug fixes and getting the game ready for the App Store! We’re thrilled to be just around the corner from giving your kids a fun and educational experience they’ll love!"&lt;/p&gt;
&lt;h2 id="new-name-and-icon"&gt;New Name and Icon&lt;/h2&gt;
&lt;p&gt;The name of the game has been updated from wordwhiz to WordXplorer.&lt;/p&gt;
&lt;p&gt;We’ve replaced the generic grey boxes with a vibrant, colorful icon that better reflects the fun and educational nature of WordXplorer.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-6/app_icon.png" alt="App_Icon"&gt;&lt;/p&gt;
&lt;h2 id="bugfixes"&gt;Bugfixes&lt;/h2&gt;
&lt;p&gt;Kids are great testers; they always manage to do things you’d never expect. So, what did they discover this time?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Previously, pressing keyboard keys while a popup was displayed could cause issues, as you couldn’t see the word you were entering. We’ve now disabled the keyboard when a popup is shown to prevent this.&lt;/li&gt;
&lt;li&gt;You could also press keyboard keys after completing a level, but before starting the next. This led to unintended effects, like having a prefilled row when the new level started. The keyboard is now disabled when the level is complete.&lt;/li&gt;
&lt;li&gt;That was not the only problem when a level ended. Did you know, you could press the Next Level button before the popup finished? Unsurprisingly, this caused issues. Now, the next level button only appears after the popup has fully disappeared. It also lets the kids enjoy the fireworks for a tad more time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also fixed feedback about the Next Level button not being centered on the iPad.&lt;/p&gt;
&lt;p&gt;These fixes ensure that your gameplay is smooth and frustration-free, with no more accidental key presses or misaligned buttons on the iPad, making your experience more enjoyable.&lt;/p&gt;
&lt;h2 id="infrastructural-changes"&gt;Infrastructural changes&lt;/h2&gt;
&lt;p&gt;To generate TestFlight and web builds, I have been using a combination of GitHub Actions, GameCI, and Fastlane.&lt;/p&gt;
&lt;p&gt;I’m excited to share that we’re now able to create builds ready for the App Store release. We’re making solid progress and are committed to delivering the final version of the game soon.&lt;/p&gt;
&lt;p&gt;Fastlane can update the App Store metadata, but I haven’t managed to get it working yet. It’s on my list to figure out. For now, we are manually updating the metadata in the portal. This isn’t ideal, but it is a minor issue that won’t affect our overall progress.&lt;/p&gt;
&lt;h2 id="updating-metadata-for-the-app-store"&gt;Updating Metadata for the App Store&lt;/h2&gt;
&lt;p&gt;To get the game ready for the App Store, a lot of metadata needs to be added. This includes the app name, subtitle, and a description.&lt;/p&gt;
&lt;p&gt;We also need to upload at least three screenshots for various device form factors.&lt;/p&gt;
&lt;p&gt;Here's a sneak preview&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-6/screenshot_1.png" alt="Screenshot 1"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-6/screenshot_2.png" alt="Screenshot 2"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-6/screenshot_3.png" alt="Screenshot 3"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These may end up in the final version, but we are not sure yet.&lt;/p&gt;
&lt;h2 id="submitting-for-review"&gt;Submitting for review&lt;/h2&gt;
&lt;p&gt;Once we updated all the metadata, we decided to submit the game for an App Store review. The intention was not to get the current version released but to ensure we can go through the approval process and be ready when the final version is prepared.&lt;/p&gt;
&lt;p&gt;We’ve encountered some challenges with Apple's approval process, but we're addressing them head-on.&lt;/p&gt;
&lt;p&gt;The 1st hurdle was related to analytics and data capturing. This has been easy enough to address as we are not capturing any PII, and the data that we capture for analytics is aggregated so we don't know anything about any specific individual.&lt;/p&gt;
&lt;p&gt;The next challenge we’re facing is Apple’s concern with our use of "Wordle" in the metadata and screenshots. Apple has guidelines to prevent copycat games and apparently using the Wordle goes against it. This was unexpected as we could see other games in the app store using Wordle. The plan is to resolve this by updating the metadata to remove any references to "Wordle" and providing a small blurb of why it's not just a copy.&lt;/p&gt;
&lt;p&gt;Hopefully, with the changes we’re making, Apple will see that our game is unique and far from a copycat.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next&lt;/h2&gt;
&lt;p&gt;Aside from getting a build that adheres to Apple’s guidelines and squashing bugs, we’re working on determining the price point and considering the option of preorders.&lt;/p&gt;
&lt;p&gt;Based on survey feedback, we’re leaning towards a price point of NZD 5.99 / USD 2.99 / INR 299 for approximately 20-25 levels.&lt;/p&gt;
&lt;p&gt;We’re also contemplating theming the words in the game. Two themes we’re considering are food and animals. If we proceed with this, we plan to release one theme now and the other later. Do you have a preference for which theme we should choose first?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="playable-build"&gt;Playable Build&lt;/h2&gt;
&lt;p&gt;We’re excited for you to test out the &lt;a href="https://golden-pony-d2c3f0.netlify.app/"&gt;Playable Web Build&lt;/a&gt;. Share your feedback, and if you want the full experience, just ask for a TestFlight invite—I’ll send it over right away!. 😊&lt;/p&gt;
&lt;p&gt;Know someone who’d love to try the game before its official release? &lt;del&gt;Share this &lt;a href="https://tally.so/r/wverVQ"&gt;form&lt;/a&gt; with
them—they’ll thank you!! ✨&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 26th December 2024:&lt;/em&gt;&lt;/strong&gt; You can now download the game.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Does your child have a favourite word they’d love to see in WordXplorer? Let me know, and I’ll do my best to add it to the game!&lt;/p&gt;
&lt;p&gt;Thank you for being a part of this journey with me! I’m eager to hear your thoughts and feedback. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer #5: UI Refresh</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-5</link>
			<description>WordXplorer Update 5 tweaks the UI to make for a better experience</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-5-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-5</guid>
			<pubDate>Mon, 19 Aug 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here's whats new since the last update&lt;/p&gt;
&lt;h2 id="updated-game-screen"&gt;Updated game screen&lt;/h2&gt;
&lt;p&gt;The main game screen now has more negative space, giving it a cleaner look. However, this change means the keyboard buttons are smaller. Give it a try and let me know how it feels!&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-5/old_game_screen.jpg" alt="Old Game Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-5/new_game_screen.jpg" alt="New Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="improved-popups"&gt;Improved Popups&lt;/h2&gt;
&lt;p&gt;Popups are no longer boring grey rectangles. They now match the color scheme of the rest of the game, making everything look more cohesive.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-5/popups.jpg" alt="Popups"&gt;&lt;/p&gt;
&lt;h2 id="enhanced-fireworks"&gt;Enhanced Fireworks&lt;/h2&gt;
&lt;p&gt;Last time, I added support for fireworks, but they were a bit hard to see. Now, the screen dims after you complete a level, so the fireworks really stand out. With explosive sound effects, it creates a much better experience!&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-5/old_fireworks.gif" alt="Old Fireworks"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-5/new_fireworks.gif" alt="New Fireworks"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="privacy-policy"&gt;Privacy Policy&lt;/h2&gt;
&lt;p&gt;To prepare the game for the App Store, I needed a privacy policy. Luckily, I’m not capturing any personal information, so it was easy to create one using ChatGPT. If you’re interested, you can check it out &lt;a href="https://www.ankursheel.com/personal-projects/wordxplorer/privacy-policy-wordxplorer" target="\_blank"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="playable-build"&gt;Playable Build&lt;/h2&gt;
&lt;p&gt;&lt;del&gt;Test it out by going to &lt;a href="https://golden-pony-d2c3f0.netlify.app/"&gt;Playable Web Build&lt;/a&gt; and let me know what you think
about the game so far. The TestFlight app is still the best way to play, so let me know if you want an invitation. 😊&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 26th December 2024:&lt;/em&gt;&lt;/strong&gt; You can now download the game.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Does your child have a favourite word they’d love to see in the game? Let me know, and I might add it!&lt;/p&gt;
&lt;p&gt;Thank you all for your support and patience. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer #4: Levels, Fireworks &amp; Fresh New Look</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-4</link>
			<description>WordXplorer Update 4 adds deterministic levels and fireworks</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-4-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-4</guid>
			<pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's been a while since the last update, and I wanted to tell you what's been happening. I wanted to get this game out by now, but life events got in the way.&lt;/p&gt;
&lt;h2 id="what-happened"&gt;What happened&lt;/h2&gt;
&lt;p&gt;The partnership that I had for the first game ended. This meant I had to setup new accounts for various third-party services and redo some of the infrastructural work, which took some time.&lt;/p&gt;
&lt;p&gt;After your feedback and some live playtests, it was clear that the game needed something more. Without someone else to help, I was getting overwhelmed with my day job and all the things that needed to be done for wordxplorer. Thankfully, my cousin Manjari stepped in to help with the creative side of things.&lt;/p&gt;
&lt;h2 id="whats-new"&gt;What's New&lt;/h2&gt;
&lt;h3 id="color-matters"&gt;Color Matters&lt;/h3&gt;
&lt;p&gt;Let's face it: The original colours weren't very appealing. The first thing Manjari changed after joining was updating the colours. But don't take my word for it, see for yourself:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Before&lt;/th&gt;
&lt;th style="text-align: center;"&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-4/old_instructions.jpg" alt="Old Instructions Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-4/new_instructions.jpg" alt="New Instructions Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-4/old_game_screen.jpg" alt="Old Game Screen"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-4/new_game_screen.jpg" alt="New Game Screen"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="fireworks"&gt;Fireworks&lt;/h3&gt;
&lt;p&gt;Previously, when you found the right word, there was no celebration - just a plain popup that quickly disappeared. Now, there are fireworks and explosions to celebrate your victory.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-4/fireworks.gif" alt="Fireworks"&gt;&lt;/p&gt;
&lt;h3 id="deterministic-levels"&gt;Deterministic Levels&lt;/h3&gt;
&lt;p&gt;The random word selection felt like playing the lottery. Now, there are levels and they are locked! After you complete one, the next one is unlocked, providing a sense of progress. Plus, your progress is saved, so you don’t have to start from Level 1 every time.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.ankursheel.com/assets/images/posts/wordxplorer-update-4/level_screen.jpg" alt="Level Selection Screen"&gt;&lt;/p&gt;
&lt;h3 id="web-build"&gt;Web Build&lt;/h3&gt;
&lt;p&gt;For those who didn’t want to install TestFlight or were not on an Apple device, you can now try the game on your browser. The TestFlight app is still the best way to play, so let me know if you want an invite. 😊&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;del&gt;Test it out by going to &lt;a href="https://golden-pony-d2c3f0.netlify.app/"&gt;Playable Web Build&lt;/a&gt; and let me know what you think
about the game so far.&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 26th December 2024:&lt;/em&gt;&lt;/strong&gt; You can now download the game.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Does your child have a favourite word they’d love to see in the game? Let me know, and I might add it!&lt;/p&gt;
&lt;p&gt;Thank you all for your support and patience. Stay tuned for more updates!&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Verifying Method Calls within a class using Mockito Spies</title>
			<link>https://www.ankursheel.com/blog/verifying-method-calls-class-mockito-spies</link>
			<description>How to remove the duplication of test scenarios when one method calls another within the same class</description>
			<guid>https://www.ankursheel.com/blog/verifying-method-calls-class-mockito-spies</guid>
			<pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;in order to get confidence, when one method calls another within the same class with just 1 or more additional parameters, we could write the same test for both the methods. This would lead to a lot of duplicated tests and a cluttered test suite.&lt;/p&gt;
&lt;p&gt;However, Mockito spies can help us in this scenario.&lt;/p&gt;
&lt;h2 id="scenario"&gt;Scenario&lt;/h2&gt;
&lt;p&gt;You want to write tests for a class that contains two public methods, one of which is essentially overloading the other and calling it without adding any additional logic.&lt;/p&gt;
&lt;p&gt;Here's a simplified example to illustrate this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public class Service {
    public int getTotalPrice(int quantity) {
        // some other code like error checking, etc
        return 5 * quantity;
        
    }

    public int getTotalPrice() {
        return getTotalPrice(1);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, the &lt;code&gt;getTotalPrice()&lt;/code&gt; method without parameters calls the overloaded &lt;code&gt;getTotalPrice(int quantity)&lt;/code&gt; method, supplying a default value.&lt;/p&gt;
&lt;h2 id="enter-the-world-of-spies"&gt;Enter the World of Spies&lt;/h2&gt;
&lt;p&gt;Here's where Mockito spies come into the picture.&lt;/p&gt;
&lt;p&gt;A spy acts as a wrapper around a real object, allowing us to override specific methods with mock behaviour while leaving others intact. You can use this functionality to verify the invocation of an actual method on an object.&lt;/p&gt;
&lt;p&gt;Let's see how you can simplify your tests by using Mockito spies&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public class MyTests {
    &amp;#64;Test
    public void test() {
        Service spy = Mockito.spy(new Service());
        spy.getTotalPrice();
        verify(spy).getTotalPrice(1);
    }

    // more tests that test `getTotalPrice(int quantity)`
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's break this down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Service spy = Mockito.spy(new Service())&lt;/code&gt;:  Create a spy object for &lt;code&gt;Service&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spy.getTotalPrice()&lt;/code&gt;: Invoke the &lt;code&gt;getTotalPrice&lt;/code&gt; method on the spy object.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;verify(spy).getTotalPrice(1)&lt;/code&gt;: Verify that the overloaded method is invoked on the spy object.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the overloaded method is not called, then the test would fail, alerting us to a potential issue.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Testing every possible combination of method calls, not only consumes valuable time but also clutters your test suite with redundant tests.&lt;/p&gt;
&lt;p&gt;Mockito spies offer a solution to this problem, allowing you to verify method interactions directly, thus focusing on the behavior rather than the implementation.&lt;/p&gt;
&lt;p&gt;However, it's essential to use Mockito spies judiciously. Spies should primarily be used when you're testing a class's external behavior and its interactions with other classes or methods within itself. Overusing spies can lead to tests that are overly focused on the internal workings of a class, potentially making your tests brittle and less focused on the actual outcomes.&lt;/p&gt;
&lt;p&gt;Happy testing, and may your code always perform as intended, both now and in the future.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>WordXplorer #3: 100 Words, Help Button &amp; Roadmap</title>
			<link>https://www.ankursheel.com/blog/wordxplorer-update-3</link>
			<description>WordXplorer Update 3 adds a help button and 100 words</description>
			<enclosure url="https://www.ankursheel.com/assets/images/social/wordxplorer-update-3-facebook.png" length="0" type="image" />
			<guid>https://www.ankursheel.com/blog/wordxplorer-update-3</guid>
			<pubDate>Sat, 03 Feb 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;You can see all the related updates &lt;a href="https://www.ankursheel.com/tags/wordxplorer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Those of you who downloaded the 1st early access version might have noticed an update in the test flight app over the holidays 🎉&lt;/p&gt;
&lt;h2 id="more-words-more-fun"&gt;🚀 More Words, More Fun&lt;/h2&gt;
&lt;p&gt;I am thrilled to announce that the game now boasts a whopping 100 words! 📬&lt;/p&gt;
&lt;h2 id="introducing-the-help-button"&gt;📜 Introducing the Help Button&lt;/h2&gt;
&lt;p&gt;Say goodbye to confusion! Explore the newly added Help Button right on the game screen. No need to restart the app — clarify the colour's meanings and game rules instantly&lt;/p&gt;
&lt;div style="position: relative;width: 100%;padding-bottom: 56.25%;margin: 1rem 0;"&gt;
  &lt;iframe src="https://www.youtube.com/embed/X71OQlezrpE" title="wordxplorer Help Button" style="position: absolute;width: 100%;height: 100%;left: 0;top: 0;right: 0;" allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="public-roadmap-unveiled"&gt;📬 Public Roadmap Unveiled&lt;/h2&gt;
&lt;p&gt;Your voice matters! Check out the new &lt;a href="https://changemap.co/transient-toys/wordxplorer/"&gt;public roadmap&lt;/a&gt;, your hub for suggesting and voting on new features.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;🔮 What's Next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This marks the final free version. We will soon be on the app store as a paid game.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 26th December 2024:&lt;/em&gt;&lt;/strong&gt; You can now download the game.&lt;/p&gt;
&lt;div class="appstore-badges-container"&gt;
  &lt;a href="https://apps.apple.com/in/app/wordxplorer-guess-the-word/id6504664783" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-app-store-badge.png" alt="Get WordXplorer on App Store"&gt;
  &lt;/a&gt;
  &lt;a href="https://play.google.com/store/apps/details?id=com.glhf.wordleforkids" target="_blank"&gt;
    &lt;img src="https://www.ankursheel.com/assets/images/download-play-store-badge.png" alt="Get WordXplorer on Play Store"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Does your child have a favorite word they would like to see in the game? Let me know&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
	</channel>
</rss>