From 6 Codebases to 1: How We Rebuilt PrayAI on Rails 8 + Turbo Native

Here's a number that should make any CTO uncomfortable: 2.8 to 1.

That was the ratio of infrastructure code to application code in PrayAI's original platform. For every line of code that served a user, nearly three lines existed just to keep the platform running. 35,934 lines of CloudFormation templates. 107 infrastructure configuration files. All supporting an application with 12,900 lines of actual business logic.

We rebuilt the entire platform on Rails 8 + Turbo Native. The infrastructure is now 184 lines. The application serves the same product across web, iOS, and Android. Here's how it happened and what the numbers look like.

What PrayAI Does

PrayAI is a faith-based mobile and web app that generates personalized, AI-powered prayers with full audio. Users enter a prayer topic, the system generates a biblically-grounded prayer using OpenAI GPT-4 mini with an elaborate theology-first prompt, then converts it to audio via text-to-speech. Users can choose male or female voices, favorite prayers, set daily reminders, track weekly prayer goals, and subscribe for premium access via RevenueCat.

It's available on the web, iOS, and Android. The product has real-time updates (you watch your prayer generate live), push notifications (daily reminders, weekly goal tracking, prayer-ready alerts), and a full subscription lifecycle across both app stores.

Not a trivial app. But not an enterprise platform either. And that's what made the original architecture so puzzling.

The Old Platform

The original PrayAI was built on React Native + AWS Amplify. It was a credible engineering choice at the time. Here's what it looked like:

4 separate repositories:

  • Backend — Node.js/AWS Amplify with 7 Lambda functions, AppSync GraphQL, DynamoDB, Cognito, S3, SQS queues, ElastiCache Redis, and a custom VPC
  • Monorepo — NX monorepo containing a React Native/Expo mobile app and Next.js web pages, with 4 shared libraries
  • Landing page — A separate standalone Next.js site
  • Emails — React Email templates in their own repo

The dependency situation:

  • 252 direct npm dependencies across 19 package.json files
  • 1,472 installed packages in the monorepo's node_modules
  • 65,184 total lines of yarn.lock across all repos
  • A shared UI components library that contained exactly 1 component (a card)

The infrastructure:

  • 15+ AWS managed services (Lambda, AppSync, DynamoDB, Cognito, S3, SQS, ElastiCache, VPC, IAM, Polly, CloudFormation, CloudWatch, and more)
  • 107 CloudFormation template files totaling 35,934 lines
  • 4 custom CloudFormation stacks (SQS queue, dead letter queue, VPC, Redis cluster)
  • 4 separate deployment pipelines (2 GitHub Actions + 2 Amplify Hosting)
  • 3 hosting platforms (AWS Amplify Backend, Amplify Hosting, Expo EAS)

The engineers who built this weren't wrong. They chose technologies that solved real problems: React Native for cross-platform mobile, AWS Amplify for managed infrastructure, DynamoDB for scalability. These are defensible choices.

But the result was a platform where the plumbing had become more complex than the product.

The Breaking Point

Every major dependency was significantly behind its current version:

DependencyPrayAI VersionCurrent VersionBehind Expo SDK49523 major versions React Native0.720.775 releases NX16204 major versions Next.js13152 major versions AWS Amplify JSv5v61 major (breaking)

The estimated cost to just upgrade everything to current: $48,000–$78,000. And that number was growing by $1,000–2,000 every month as each framework released new versions, widening the gap.

But here's what really crystallized the decision: to ship a single feature, a developer had to modify the GraphQL schema in one repo, update Lambda functions in plain JavaScript (while the frontend was TypeScript), run amplify push to provision CloudFormation stacks, update GraphQL queries in the monorepo's shared libraries, update React Native screens, potentially update the web share pages, run an NX build with Metro bundler, deploy via EAS for iOS and Android, and deploy web via Amplify Hosting.

That's 9 steps across 2+ repositories and 4 deployment pipelines. For one feature.

Onboarding a new developer to this stack took 3–4 months. The hiring requirement was someone who knew React Native + Expo + NX + Next.js + AWS Amplify + AppSync + DynamoDB + CloudFormation. That's a specialist premium that costs real money.

The New Platform

We rebuilt PrayAI on Rails 8 + Turbo Native. The architecture is simple enough to fit in one paragraph:

A single Rails 8 application serves all business logic, all UI, and all data. It uses SQLite3 for the database, Solid Queue for background jobs, Solid Cache for caching, and Solid Cable for WebSockets. It deploys to a single server via Kamal with a 72-line Dockerfile and a 112-line deploy config. Two thin native shells—one in Swift for iOS, one in Kotlin for Android—wrap the Rails app using Hotwire Native, rendering the same server-generated HTML in native app containers.

That's it. No Terraform. No Kubernetes. No Redis. No message queues. No API gateway. No service mesh. No CloudFormation.

To ship a feature now:

  1. Write Ruby/ERB code in the Rails app
  2. Push to main
  3. CI runs (under 10 minutes)
  4. Deploy via bin/kamal deploy
  5. Feature is live on web, iOS, and Android simultaneously

No separate mobile builds required for UI or feature changes. No coordinated multi-repo deployments. A Rails developer can ship features to all three platforms without knowing Swift or Kotlin.

The native shells handle only what must be native: authentication flows (Apple/Google Sign-In), the prayer audio player (for performance), bridge components for native UI elements, and push notification token registration. Everything else is HTML served by Rails and rendered by Turbo Native.

The Numbers

We ran a full Platform Velocity Index assessment on both platforms. Seven dimensions, each weighted by business impact:

DimensionOld PlatformNew PlatformDelta Dependency Gravity2/108/10+6 Architectural Complexity2/109/10+7 Feature Delivery Velocity2/109/10+7 Operational Overhead3/109/10+6 Talent Accessibility3/108/10+5 Upgrade & Migration Safety1/109/10+8 Platform Unification3/109/10+6 Composite PVI2.2 (F-)8.7 (A)+6.5

And the raw metrics:

MetricOld PlatformNew Platform Repositories43 (1 app + 2 shells) Direct dependencies252 npm packages32 gems + 6 importmap pins Infrastructure code35,934 lines184 lines Application code12,900 LOC across 4 repos11,547 LOC in 1 repo Monthly hosting$2,250$50 Deployment pipelines41 AWS services15+0 Environment variables40+4 Developer onboarding3–4 months2–4 weeks Upgrade debt$48K–$78K$0

The infrastructure reduction is the one I keep coming back to: 35,934 lines down to 184. That's a 175x reduction. And the application delivers the same product to the same users on the same platforms.

What Stayed the Same

This is the part people worry about most, so let me be explicit:

  • All user data was preserved and migrated. No data loss.
  • Every feature carried over. Prayer generation, audio playback, favorites, subscriptions, push notifications, weekly goals—all present on the new platform.
  • External integrations remain intact. RevenueCat handles subscriptions. OpenAI generates prayers and audio. Apple and Google handle authentication. Push notifications go through APNs and FCM.
  • The team didn't change. Same developers, now shipping faster.
  • Users didn't notice. The app continued to work. Features started shipping faster.

In fact, the new platform does things the old one couldn't. Real-time prayer generation updates via Turbo Streams (no polling). A blog with rich text editing via Action Text. A feedback system. Server-side rendered pages that work instantly on any device. All of that came for free with Rails conventions.

The Payoff

The migration took approximately 8–12 weeks and cost roughly $87,000. Here's what it returned:

MetricValue Migration investment~$87,000 Annual savings~$262,500 Payback period4 months Year 1 ROI202% 3-Year ROI805% 3-Year net savings$700,500 Feature velocity2x (48/year vs 24/year, same team) Cost per feature$14,106 vs $39,150 (64% cheaper)

With the same 2-person team, the new platform ships twice as many features at 64% lower cost per feature. The 4-month payback means the migration paid for itself before the end of the first quarter.

Lessons Learned

Complexity is not sophistication. The old platform used Lambda, AppSync, DynamoDB, SQS, ElastiCache, CloudFormation, React Native, Expo, NX, and Next.js. These are genuinely sophisticated technologies. But deploying all of them for an app with 12,900 lines of business logic is like hiring a Formula 1 pit crew to change a bicycle tire. The sophistication is real. The match to the problem is not.

The plumbing-to-product ratio reveals everything. When your infrastructure code outweighs your application code 2.8 to 1, your engineering talent is solving platform problems, not customer problems. The old platform had 35,934 lines of CloudFormation. The new platform has a 72-line Dockerfile. Both serve the same users. The difference is where the engineering effort goes.

Turbo Native is the unifier. The single biggest architectural win was eliminating the need to build features three times. On the old platform, the shared UI library had 1 component in it—the aspiration of cross-platform code reuse never materialized. With Turbo Native, the Rails app's 11,547 lines of shared code actually serve all three platforms. A feature built in Ruby/ERB appears on web, iOS, and Android with a single deploy.

SQLite is production-ready. This still surprises people. PrayAI runs on a single server with SQLite3 for everything—primary database, cache (Solid Cache), job queue (Solid Queue), WebSockets (Solid Cable). No Redis. No Postgres. No connection pooling headaches. Rails 8 made this a first-class deployment pattern, and for applications at this scale, it eliminates an entire category of operational complexity.

The upgrade treadmill is real. The old platform had every major dependency 2–4 versions behind, with an upgrade bill of $48K–$78K growing monthly. The new platform sits on Rails 8.0.3, Ruby 3.4.1, and every gem at its latest version. Rails has a 20-year track record of providing clear, step-by-step upgrade guides. The annual cost of staying current went from $63,000 to $2,400.

The Uncomfortable Truth

The engineers who built the original PrayAI platform were solving genuinely hard problems—CloudFormation template orchestration, Lambda cold start optimization, AppSync resolver configuration, DynamoDB partition key design. These problems demand real skill.

But they are plumbing problems, not customer problems.

When infrastructure shrinks from 35,934 lines to 184 lines, the cognitive space freed up is enormous. The same developer who spent a week debugging CloudFormation stack drift now spends that week building a feature that ships to all three platforms in a single deploy.

This isn't about choosing simpler tools because the team can't handle complex ones. It's about choosing simpler tools precisely because the team is too talented to waste on plumbing.

What's Next

If you're running a multi-platform product on a stack that feels heavier than it should, the first step is knowing the number. We built the Platform Velocity Index to give you that number—a single score across 7 dimensions that tells you whether your tech stack is an asset or a liability.

PrayAI went from 2.2 to 8.7. The migration cost $87,000 and paid for itself in 4 months. Every month of delay on the old platform was costing $21,875 in excess operational spending and forfeited feature delivery.

Get in touch at Ruby Growth Labs to schedule a free PVI assessment for your codebase. The assessment reads actual files, counts actual dependencies, and traces actual deployment paths. No guessing. No hypotheticals. Just the number.

]]>