Mobile · Joc iOS + Android

Tiny Fates — simulator de viață mobil cu motor pur funcțional, 22 de limbi și monetizare RevenueCat

Tiny Fates e un simulator de viață text-based pentru iOS și Android: jucătorul îmbătrânește câte un an pe rând, alege un eveniment pe an, iar stats, bani, relații și flag-uri se propagă până la moarte și epitaf. Motorul e pur funcțional (fără mutații), scris în TypeScript, cu peste 50 de pachete de evenimente. Disponibil în 22 de limbi, monetizat prin RevenueCat + reclame AdMob/AppLovin.

IndustrieMobile / Gaming
Durată12 luni (ongoing)
StatusLive · App Store + Google Play
Echipă2 dezvoltatori + 1 scriitor
ȚarăRomânia

Contextul de business

Genul life-sim (BitLife, InstLife) are zeci de milioane de descărcări, dar concurența e brutală: BitLife dominește cu buget marketing masiv. Tiny Fates trebuia să diferențieze prin: (1) conținut narativ mai profund — nu doar evenimente aleatoare, ci arcuri narative și lume vie care reacționează la alegeri, (2) localizare în 22 de limbi (nu doar EN), (3) monetizare respectuoasă — freemium real, nu paywall agresiv.

Alegerea arhitecturală cheie: motor pur funcțional. Fiecare funcție (advanceAge, resolveEvent, createCharacter) returnează un obiect Character nou, niciodată nu mută starea. Asta permite testare deterministă, undo/redo în debugging, și coverage de mutați (Stryker) care validează că testele chiar prind bug-uri, nu doar ating linii de cod.

Tiny Fates — Vârsta 27 Vârsta 27 Programator · București SĂNĂTATE 82/100 BANI €34.500 FERICIRE 68/100 Îmbătrânește → Eveniment: Promoție Managerul ți-a oferit rol de Team Lead cu +30% salariu. Accept Refuz ENGINE PUR FUNCȚIONAL advanceAge() → Character nou 0 mutații · testare deterministă 22 limbi 80% coverage engine
Gameplay loop + motor pur funcțional — design ilustrativ

Decizii tehnice cheie

1. Motor pur funcțional — fără mutații de stare

Fiecare funcție din src/game/engine/ primește un Character și returnează un Character nou. advanceAge, resolveEvent, pickNextEvent, createCharacter — toate imutabile. Beneficii: testare deterministă (același input produce același output), undo în debugging, și posibilitatea de a rula mutați testing (Stryker) care modifică codul și verifică dacă testele prind modificarea.

2. React Native 0.86 bare workflow (fără Expo)

Am eliminat Expo/EAS pentru control nativ complet. Directorul ios/ și android/ sunt sursa de adevăr pentru build-uri. Code-sharing între platforme peste 90%, dar module native (AdMob, AppLovin, RevenueCat) sunt configurate manual în Xcode și Android Studio. Build-uri headless de pe laptop: scripts/build-ios-sim.sh și scripts/build-android-release.sh.

3. Zustand pentru stare, cu persistență AsyncStorage

Starea jocului (Character curent, save slots, setări) trăiește într-un store Zustand 5. Store-ul e compus din slices (gameSlice, lifeSlice, datingSlice, financeSlice). Persistență automată în AsyncStorage — jucătorul poate închide aplicația la jumătatea unui an și revine exact unde a rămas. Save compat între versiuni prin hydrateCharacterForF1.

4. RevenueCat + AdMob/AppLovin pentru monetizare hibridă

Modelul: freemium real. Jocul complet e gratuit; reclamele (AdMob interstitial + AppLovin rewarded) sunt opționale și oferă bonusuri, nu blochează progresul. RevenueCat gestionează abonamentul premium care elimină reclamele și deblochează cosmetice. Un singur SDK pe ambele platforme, sincronizare entitlement-uri prin Supabase.

5. i18n în 22 de limbi cu i18n-js + react-native-localize

Conținutul narativ (texte evenimente, alegeri, rezultate) e localizat în 22 de limbi. Cataloage UI scrise manual în src/locales/; cataloage de gameplay generate automat (multi-MB) în src/locales/generated/. Fiecare schimbare de string trebuie propagată în toate cele 22 de limbi — script automat verifică cheile lipsă.

React Native 0.86 TypeScript 6 React 19 Zustand 5 React Navigation 7 RevenueCat 10 Supabase AdMob AppLovin Sentry PostHog Jest 30 Stryker

Provocări întâlnite

Provocarea 1: Save compatibility între versiuni

Jucătorii au save-uri vechi de luni de zile. Când adăugăm un câmp nou la Character (ex: datingScore), save-urile vechi nu au valoarea. Soluție: hydrateCharacterForF1 rulează la încărcarea fiecărui save și backfill-uiește câmpurile lipsă cu valori default. Câmpurile noi rămân opționale în tipurile TypeScript — un save vechi nu sparge niciodată aplicația.

Provocarea 2: 50+ pachete de evenimente — performanță la încărcare

Peste 50 de fișiere cu evenimente sunt concatenate într-un singur ALL_EVENTS. La 10.000+ evenimente, încărcarea la pornire apăsa pe timpul de startup. Soluție: barrel exports în src/data/events/index.ts, cu code-splitting pe categorii (childhood, career, relationship, crisis). Încărcarea lazy a pachetelor pe bază de vârstă și flag-uri active.

Provocarea 3: Stryker mutation testing la 80% coverage pe engine

Coverage de linii nu garantează că testele prind bug-uri. Stryker modifică codul (schimbă > în >=, șterge condiții) și rulează testele — dacă testele trec pe cod mutat, testele sunt slabe. Am setat pragul la 80% pe src/game/engine/core.ts. Mutanții care supraviețuiesc sunt bug-uri potențiale ascunse.

Rezultate măsurate

22Limbi localizate (catalog UI + gameplay)
80%Coverage engine core (mutation-tested Stryker)
50+Pachete de evenimente narative
0Mutații de stare în motor (pur funcțional)
92%Code-sharing iOS/Android (React Native bare)
1.4.5Versiune live pe App Store + Google Play

Lecții pentru proiecte similare

  1. Motorul pur funcțional se plătește în testare. Imutabilitatea elimină o clasă întreagă de bug-uri de race-condition și face mutation testing posibil. Cost: boilerplate de spread, beneficiu: confidence totală.
  2. Save compatibility NU e opțională pentru jocuri live. Un jucător care pierde un save de 50 de ore nu se întoarce. Backfill automat la încărcare, câmpuri noi opționale mereu.
  3. Stryker mutation testing e singura dovadă reală de calitate a testelor. Coverage de linii minte — un mutați care supraviețuiește e un bug care așteaptă.
  4. RevenueCat elimină 90% din durerea IAP cross-platform. Nu construi propriul layer de billing pentru un joc mobil — Apple și Google îl vor schimba.
  5. 22 de limbi cere pipeline automat de verificare chei. Traducerile manuale devin inconsistent; un script care verifică cheile lipsă la fiecare build salvează ore de QA.

«Tiny Fates rulează pe un motor pur funcțional cu mutation testing la 80% coverage. Jucătorii nu văd arhitectura — văd un joc fluid în 22 de limbi fără bug-uri de stare. Faintech a construit fundația tehnică care face conținutul narativ scalabil.» — Eduard Gridan, founder Tiny Fates

Ai un proiect similar?

Documentație tehnică detaliată în 48h, cu specificație de arhitectură și calcul ROI pe stack alternativ.

Discută proiectul

Citește în continuare