In the excitement of building Epic 1, it’s easy to let technical debt slide. “I’ll fix it later” is the most dangerous sentence in software engineering.

Before moving on to the complex features of Epic 2 (The Feed), I decided to pull the emergency brake. I wanted to ensure Roumi was built on bedrock, not quicksand.

For this session, I enlisted the help of Claude Code Opus Thinking mode, arming it with specialized skills like Vercel React Best Practices and React Native Architecture Guidelines.

Here is the deep dive into what we found, and how I fixed it.


🕵️ The Audit: What We Found

I ran a full scan of the packages/ and apps/ directories. The good news? The monorepo structure was solid. The bad news? We found 10 critical to medium issues.

1. 🚨 Security: The Open Door

The most alarming finding was in packages/business-logic/src/auth/firebaseConfig.ts. My API keys were hardcoded right in the file.

// ❌ BEFORE: Publicly committed keys
const firebaseConfig = {
    apiKey: "A...", // Oops
    // ...
};

If I had pushed this to a public repo, bots would have scraped these keys in seconds.

2. 📦 Dependency Hell

My gemi-landing Next.js app was pulling in React 19.2.3, while the mobile app (gemi) was locked to React 19.1.0 (required by React Native 0.81). This mismatch caused npm ls to scream in red text. In a monorepo, version drift is the silent killer of productivity.

3. 👯 The “Copy-Paste” Syndrome

I audited my login.tsx and signup.tsx screens.

  • Login: 305 lines of code.
  • Signup: 281 lines of code.

70% of that code was identical. The layout, the gradients, the brand headers, the error banners—all duplicated. If I wanted to change the background color, I’d have to do it twice.


🛠️ The Refactor: Cleaning House

Armed with this list, I went to work.

🛡️ Securing the Environment

I moved all sensitive configuration to specific environment files. I used Expo’s process.env.EXPO_PUBLIC_ convention, which natively injects these variables at build time.

// ✅ AFTER: Safe and sound
const firebaseConfig = {
    apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
    // ...
};

I created a .env.example template for the team and a git-ignored .env.local for my secrets.

📐 The “AuthLayout” Abstraction

To cure the duplication, I extracted the common UI pattern into a new component: AuthLayout.

It handles:

  • The StatusBar configuration
  • The LinearGradient background
  • The KeyboardAvoidingView behavior
  • The Brand Header and Title typography
  • The generic Error Banner logic

Now, my login.tsx focuses only on the form itself.

The result?

  • Login Screen: Dropped from 305 → 155 lines (-49%)
  • Signup Screen: Dropped from 281 → 130 lines (-54%)

🎨 Polishing the Design System

I dug into theme.ts and realized I had been sloppy.

  • I had defined white twice.
  • My font weights were strings ('700'), but React Native prefers numbers (700).
  • I was missing crucial tokens.

I standardized the system and added new scales:

export const spacing = {
    md: 16,
    lg: 24,
    xl: 32,
    // ...
};

export const borderRadius = {
    lg: 16,
    xl: 22,
    full: 9999,
};

This enforces consistency. No more guessing if padding should be 15 or 20; it’s always spacing.md or spacing.lg.


🐛 Squashing the “Stale Closure” Bug

The specialized code review skills caught a subtle bug in my useAuth hook:

// ❌ Bug: initializing was captured in the closure
if (initializing) setInitializing(false); 

Since initializing was true when the effect first ran, this looked fine. But inside the Firebase callback, that value was stale. I switched to an unconditional update to ensure the loading state always clears.


🚀 The Result & Next Steps

After npm install and a final pass with expo-doctor:

  • 0 Security Vulnerabilities
  • 0 Dependency Conflicts
  • 50% Less Code in critical flows

The app feels snappier, the code reads like a book, and I can sleep at night knowing my keys aren’t public.

Now, we are truly ready. Next stop: Epic 2, where we build the infinite feed engine.

See you in the next one.