The confession first
I am a software engineer. I ship things for a living. And I have never once, in my adult life, opened a to-do app and felt anything.
I have a very specific flavor of procrastination. It is not "I'll do it tomorrow." It is "I will reorganize my entire dotfiles repo, migrate my terminal to a different shell, and read four blog posts about keyboard layouts before I touch the one task that's actually due." I am extremely productive, as long as it is not on the correct thing.
At some point I stopped blaming myself and started looking at the tools. And I noticed two things that every abandoned to-do app had in common:
- Nothing was holding me accountable. A checkbox does not care if you ignore it. A friend does.
- There was no reward. Finishing a task felt structurally identical to not finishing it. If you designed an incentive system that badly on purpose, you'd get fired.
So, instead of doing my actual work, I built a to-do app. Naturally. I called it Dunbit, and the whole idea is: you finish your daily tasks, and you hatch a little collectible creature. A friend can opt in as your accountability partner and gets pinged when you actually follow through. It's a Tamagotchi that quietly judges you, and it turns out I respond to that far better than to discipline.
This post is half "here's how I built it" and half "here's the exact code I wrote to avoid doing something else." Both are true.
What Dunbit actually is
The loop is simple on purpose:
- Complete your tasks to earn one hatch per calendar day. One creature per day, enforced on the server so you can't fast-forward your phone clock and cheese it. (I tried. I closed the hole.)
- Collect creatures across a rarity spectrum — Common, Uncommon, Rare, Legendary.
- Keep a streak with a login flame and XP that quietly nudge your luck.
- Add an accountability partner who opts in and gets a heads-up when you do the thing.
- Share your collection through an auto-generated public web page — no account, no username, nothing to sign up for.
The core app is free forever. Adding tasks, completing them, and hatching never lock. That was a deliberate call: nothing kills momentum like a paywall standing between you and the reward loop.
The architecture (or: how I justified this to myself as "real engineering")
Dunbit is two separate TypeScript projects with one rule I refused to break: the backend is king. The app is a pure rendering layer with essentially zero business logic in the bundle. Every game value, rarity table, and piece of content lives on the server.
The stack:
- Frontend: React Native + Expo (managed workflow), plus a home screen widget via an Expo config plugin.
- Backend: AWS Lambda + CDK (Node 20, ESM, esbuild), behind API Gateway, with assets on S3 + CloudFront.
- Database: MongoDB Atlas. New creatures and entire seasons are just document inserts — no code changes, no redeploy.
- Scheduling: EventBridge crons handle timezone-aware midnight resets, so your "day" ends at your midnight, not a server's in us-east-1.
Why so strict about server-authoritative design? Because I wanted to tune the game like a config change, not a release cycle. If Rare creatures feel too stingy, I edit a document in Mongo. I don't cut a build, wait on App Store review, and pray. That discipline is annoying to hold on day one and pays for itself every single day after.
The fun part: a rarity engine that lies to you (nicely)
Each hatch draws from a weighted distribution, and a few invisible factors nudge the odds — your streak, whether you've got an accountability partner, and a hidden pity system so you're never stuck in bad-luck purgatory.
Roughly, the probability of hatching a creature of tier t is:
P(t) = (w_t * (1 + b_streak + b_partner)) / Σ (w_i * (1 + b_streak + b_partner))
where w_t is the base weight of the tier and the b terms are small, quietly-applied boosts.
Two rules keep it feeling fair:
- The rarity floor is always Common. A hatch never produces nothing. You earned it, you get something.
- Legendaries never duplicate. If you already own all of them, the roll gracefully falls back to a lower tier instead of handing you a fifth copy of the one you already have.
The single best lesson here: invisible mechanics feel better than visible ones. Players enjoy good luck far more when they don't know it was engineered. The pity system works precisely because nobody sees it. The moment you put "bad luck protection: 3/5" on screen, you've turned a nice surprise into a progress bar.
The thing that actually took the longest
It was not the Lambdas. It was not the timezone math (though — timezones are a personal attack, "reset at midnight" sounds trivial until you remember midnight is a moving target across a planet, which is exactly why the resets are cron-driven and per-user).
It was the art. I needed a whole roster of creatures that (a) looked like they belonged to the same universe and (b) did not accidentally borrow someone else's copyrighted universe. Getting a cohesive, original visual identity — bubbly shapes, electric purple, a clear family resemblance across all of them — took far more iteration than I'd like to admit. Early batches looked like five different games having an argument in one folder.
What I learned
- Motivation is a systems problem, not a willpower problem. Reward plus accountability did more for my procrastination than any amount of self-lecturing ever has. I say this as the self-lecturing champion of the world.
- Server-authoritative design is worth the upfront discipline. Balancing the game became a config edit instead of a deploy.
- Keep the client dumb on purpose. Every time I was tempted to sneak "just a little" logic into the app, I had to talk myself back out of it. Future me always thanked past me.
- Ship the reward early. The hardest product problem isn't the tech, it's that the first dopamine hit is a full day away. Still working on that one.
Try it / talk to me
Dunbit is live on the App Store: Beat Procrastination — Dunbit. iOS for now.
It's built to grow — the schema already supports unlimited creatures and seasons. If you're the kind of person who's reading a build post instead of doing your actual work right now: hi, this app was made for you specifically.
If you build something with a server-authoritative game loop, or you've solved the "first reward is too far away" problem better than I have, I genuinely want to hear about it in the comments.
Beat procrastination, one creature at a time. And yes, I wrote this post instead of doing something else.
Top comments (0)