<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: enes sevinc</title>
    <description>The latest articles on DEV Community by enes sevinc (@enes_sevinc_cd49f266e0371).</description>
    <link>https://dev.to/enes_sevinc_cd49f266e0371</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4137403%2Fb88c78fd-7464-4790-94ca-93779762724c.png</url>
      <title>DEV Community: enes sevinc</title>
      <link>https://dev.to/enes_sevinc_cd49f266e0371</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/enes_sevinc_cd49f266e0371"/>
    <language>en</language>
    <item>
      <title>Five Times Pixbu's Tests Measured the Wrong Thing</title>
      <dc:creator>enes sevinc</dc:creator>
      <pubDate>Tue, 22 Sep 2026 11:33:51 +0000</pubDate>
      <link>https://dev.to/enes_sevinc_cd49f266e0371/five-times-pixbus-tests-measured-the-wrong-thing-55c2</link>
      <guid>https://dev.to/enes_sevinc_cd49f266e0371/five-times-pixbus-tests-measured-the-wrong-thing-55c2</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://hackernoon.com/five-times-pixbus-tests-measured-the-wrong-thing" rel="noopener noreferrer"&gt;HackerNoon&lt;/a&gt;. Two product details in that version have since gone out of date; this one is corrected and re-measured.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm a solo dev shipping &lt;strong&gt;Pixbu&lt;/strong&gt; — a self-care app where a pixel creature grows as you look after yourself. It's live on both stores in six languages, from one Flutter codebase, with 2,699 automated tests.&lt;/p&gt;

&lt;p&gt;This post isn't about the features. It's about a habit that saved the launch: whenever I caught myself &lt;em&gt;knowing&lt;/em&gt; something about my own app, I measured it instead. I was wrong often enough that it stopped being embarrassing and started being a process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that taught me the pattern
&lt;/h2&gt;

&lt;p&gt;Pixbu's creature evolves through six stages. Each stage should be visibly bigger than the last — that's the entire emotional payoff of the app.&lt;/p&gt;

&lt;p&gt;I replaced the whole art set. Tests stayed green. Ship it, right?&lt;/p&gt;

&lt;p&gt;Then I actually counted the drawn pixels in each stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;baby 6572 → teen 5627 → adult 5840 → elder 6244 → mythic 6338
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The creature &lt;strong&gt;shrank 14% at the first evolution&lt;/strong&gt; — the transition users see most. The single moment the app exists to deliver was running backwards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk92yvz84ipdi4d6mkyoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk92yvz84ipdi4d6mkyoa.png" alt="Five stages of the creature side by side, with the drawn-pixel count under each; the second is 14.4% smaller than the first" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why were the tests green? Because the guard read the stage sizes from a metadata file, and the metadata hadn't been re-measured from the new art yet. The test wasn't lying. It was answering a different question than the one I thought I'd asked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the pattern.&lt;/strong&gt; Not "I made a mistake." The pattern is: &lt;em&gt;the check I trusted was measuring something adjacent to the thing I cared about.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once I started looking for that shape, it was everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four more, all real
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. I measured the wrong dimension and found nothing
&lt;/h3&gt;

&lt;p&gt;Hats have to sit on the creature's head across five body shapes. They were floating. I measured the head's bounding box across every frame — and found almost no variation, ±1 px. Dead end.&lt;/p&gt;

&lt;p&gt;The bounding box &lt;strong&gt;includes the ears&lt;/strong&gt;, and the ears are the tallest thing on the creature. I was measuring ear tips and calling them skulls.&lt;/p&gt;

&lt;p&gt;The measurement that worked was counting &lt;em&gt;filled pixels per row&lt;/em&gt;: at ear height the count is 8; at the skull it jumps to 73. Completely unambiguous once you count the right thing. That fixed 514 frames.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1a75z0ha326zo7m4nefw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1a75z0ha326zo7m4nefw.png" alt="The same sprite measured two ways: the bounding-box top lands on an 8-pixel ear tip, the filled-pixel count lands on the 73-pixel skull, ten rows lower" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A null result is not evidence of no problem. It can just as easily be evidence that your instrument points somewhere else.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A guard test that asserted nothing
&lt;/h3&gt;

&lt;p&gt;I had a test checking that a critical function was actually called. Green for weeks. Then I mutated the source — wrapped the call in &lt;code&gt;if (false)&lt;/code&gt; — and the test &lt;strong&gt;stayed green.&lt;/strong&gt; It was checking that the call &lt;em&gt;text existed in the file&lt;/em&gt;, not that it ran.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3makttnejnyqq4tgq2l3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3makttnejnyqq4tgq2l3.png" alt="A guard test staying green while the code it guards is disabled, because it matches source text rather than behaviour" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I mutate every guard I write. If a test can't fail, it isn't a test; it's a comment that costs CI time. Three of mine died in a single afternoon this way.&lt;/p&gt;

&lt;p&gt;There's a second floor to this basement. Two of my mutations &lt;strong&gt;never reached the file&lt;/strong&gt; — a line-ending mismatch meant the patch silently matched nothing, the test ran against unmodified source, and passed. A green mutation test that proved nothing about a guard that proved nothing. So now the mutation script also asserts that the mutation was &lt;em&gt;applied&lt;/em&gt;. You have to verify your verifier's verifier, at least once.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. "There's no way to check that" is a guess wearing a lab coat
&lt;/h3&gt;

&lt;p&gt;I wrote in my own notes: &lt;em&gt;"there is no API way to confirm a store update was approved."&lt;/em&gt; I had reasons. They were even individually true.&lt;/p&gt;

&lt;p&gt;An hour later I found it — I'd been reading the store page's &lt;em&gt;status&lt;/em&gt; and never its &lt;em&gt;content&lt;/em&gt;. The live version string, the update date, and the release notes all change at approval. Three signals, sitting in the HTML the whole time.&lt;/p&gt;

&lt;p&gt;Same class, more expensive: one network timeout made me conclude I had no network access to my git remote. I stopped pushing. &lt;strong&gt;Eight days and 95 commits later&lt;/strong&gt; — including the entire source of a version already live in 177 countries — I tested it properly: 10 out of 10 connections fine. One failed reading is not a capability assessment.&lt;/p&gt;

&lt;p&gt;Now, if I'm about to write "impossible," "unsupported," or "can't be tested," I have to attach the date, the exact command, and its output. Undated impossibility claims are treated as stale and can't block anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The dashboard was showing me fake money
&lt;/h3&gt;

&lt;p&gt;Filling in a hackathon submission, I opened my revenue dashboard: &lt;strong&gt;$1,573 in revenue, 136 customers.&lt;/strong&gt; Encouraging!&lt;/p&gt;

&lt;p&gt;There was a toggle at the top of the page reading &lt;strong&gt;"Sandbox data."&lt;/strong&gt; It was on. Every figure on that screen came from test purchases I had made myself, on my own phone.&lt;/p&gt;

&lt;p&gt;I hadn't misread a number. I'd misread &lt;em&gt;which world the number was from&lt;/em&gt; — and I'd have cheerfully quoted it in a public submission. The habit that saved me was boring: before using a figure anywhere, read what the screen says &lt;em&gt;about&lt;/em&gt; the figure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this actually costs
&lt;/h2&gt;

&lt;p&gt;Almost nothing, and it isn't discipline. It's three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Am I measuring the thing, or something next to it?&lt;/strong&gt; (bounding box vs skull)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can this check fail?&lt;/strong&gt; (mutate it and see)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is this a measurement or a memory?&lt;/strong&gt; (if there's no date and no command output, it's a memory)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third one is the cheapest and catches the most. Most of my false certainties weren't wrong when written — they were &lt;em&gt;right once&lt;/em&gt;, and then the world moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Pixbu stands
&lt;/h2&gt;

&lt;p&gt;Live on the App Store and Google Play from one Flutter codebase, in English, Turkish, Spanish, Portuguese, German and French. Local-first, no account, no backend — server cost $0. No advertising SDK at all; not "ads turned off," the library simply isn't in the app. The paid tier unlocks species, scenes, widget themes and a signature animation for each creature.&lt;/p&gt;

&lt;p&gt;There's no pet death, no decay, no punishment for missing a day, and — the one I'll defend hardest — &lt;strong&gt;the recovery is never the thing you pay for.&lt;/strong&gt; Break a streak and you get a week to repair it by hugging your creature and doing one small task. That path is free, and it is the only path; there is no button that sells it to you.&lt;/p&gt;

&lt;p&gt;The pet sleeps at night (23:00–06:00). Tap it and it mumbles &lt;em&gt;"shh… I think I was dreaming about you."&lt;/em&gt; That line shipped the same night I wrote it. I checked it on my own phone at 00:13 — which, now that I think about it, is the same habit as everything above.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Pixbu is free on &lt;a href="https://play.google.com/store/apps/details?id=com.pixbu.app" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt; and the &lt;a href="https://apps.apple.com/app/id6796922533" rel="noopener noreferrer"&gt;App Store&lt;/a&gt;. &lt;a href="https://enessevinc25.github.io/" rel="noopener noreferrer"&gt;Site&lt;/a&gt; · &lt;a href="https://youtu.be/yUcsFUu-d20" rel="noopener noreferrer"&gt;29-second demo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>flutter</category>
      <category>indiehackers</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
