These instructions are for AI assistants working in this project.
Always open @/openspec/AGENTS.md when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use @/openspec/AGENTS.md to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
ChaseApp is a sophisticated real-time notification and chat platform for live police chases, rocket launches, weather events, and disasters. The codebase represents a modern multi-platform application with three main components:
- Web Application (
web/) - Nuxt.js/Vue.js frontend with TypeScript - Mobile Application (
mobile/) - Flutter cross-platform app - Backend Services (
api/) - Mixed Node.js and Go serverless functions
This document provides a comprehensive analysis of the current architecture, Bazel migration strategy, and recommendations for future development including the feasibility of an Elixir rewrite.
chaseapp/
βββ web/ # Vue.js/Nuxt.js frontend
β βββ components/ # Vue components
β β βββ cards/ # Content cards
β β βββ chat/ # Chat functionality
β β βββ birds/ # Aircraft tracking
β β βββ firehose/ # Real-time data feeds
β β βββ events/ # Event management
β β βββ utils/ # Utility components
β βββ store/ # Vuex state management
β βββ types/ # TypeScript definitions
β βββ utils/ # Utility functions
β βββ nuxt.config.js # Nuxt configuration
β βββ package.json # Dependencies
β
βββ mobile/ # Flutter mobile app
β βββ lib/ # Dart source code
β βββ android/ # Android-specific code
β βββ ios/ # iOS-specific code
β βββ pubspec.yaml # Flutter dependencies
β
βββ api/ # Backend services
βββ JavaScript Functions/ # Node.js Cloud Functions
β βββ addUser/ # User management
β βββ algoliaIndex/ # Search indexing
β βββ chaseStats/ # Statistics tracking
β βββ fcmMessaging/ # Firebase Cloud Messaging
β βββ pusher/ # Pusher notifications
β βββ rocketAPI/ # Rocket launch data
β βββ updateChase/ # Chase data updates
β βββ weatherAPI/ # Weather data
β
βββ Go Functions/ # Go Cloud Functions
βββ API/ # Main API service
βββ bofTS/ # Birds of a Feather tracking
βββ getStreams/ # Stream management
βββ pusher/ # Pusher notifications (Go)
- Web: Nuxt.js 2 (Edge) + Vue.js 3 + TypeScript 4.6.2
- Mobile: Flutter 2.17.1 + Dart
- Styling: TailwindCSS + WindiCSS
- State Management: Vuex (Web), Riverpod (Mobile)
- Real-time: Firebase RTDB, Stream Chat SDK, Pusher WebSocket
- Mapping: Mapbox GL JS
- Authentication: Firebase Auth
- Runtimes: Node.js 16, Go 1.19
- Database: Firestore + Firebase Realtime Database
- Messaging: FCM, Pusher Notifications, Stream Chat
- Search: Algolia v4
- Deployment: Google Cloud Functions, Cloud Run
ChaseApp's multi-language, multi-service architecture makes it an ideal candidate for Bazel:
- Complex Dependencies: Mixed Node.js/Go/Flutter ecosystem
- Incremental Builds: Only rebuild changed components
- Parallel Execution: Build web, mobile, and backend simultaneously
- Cross-language Dependencies: Shared types and utilities
- Scalability: As codebase grows, Bazel's performance advantages increase
chaseapp-bazel/
βββ WORKSPACE # Bazel workspace configuration
βββ .bazelrc # Bazel flags and settings
βββ deps.bzl # External dependencies
βββ tools/ # Build tools and configurations
β βββ build_rules/ # Custom Bazel rules
β βββ scripts/ # Migration utilities
β
βββ web/ # Frontend application
β βββ src/ # Source files
β βββ BUILD.bazel # Web build config
β βββ package.json # Dependencies
β
βββ mobile/ # Flutter application
β βββ lib/ # Dart source
β βββ BUILD.bazel # Flutter build config
β βββ pubspec.yaml # Dependencies
β
βββ backend/ # Backend services
β βββ functions/ # Cloud functions
β β βββ js_functions/ # Node.js functions
β β βββ go_functions/ # Go functions
β βββ BUILD.bazel # Backend build config
β βββ go.mod # Go modules
β
βββ shared/ # Shared libraries
β βββ types/ # TypeScript definitions
β βββ protos/ # Protocol buffers
β βββ utils/ # Shared utilities
β βββ BUILD.bazel # Shared libraries build
β
βββ infrastructure/ # Deployment configs
βββ docker/ # Docker configurations
βββ terraform/ # Infrastructure as code
βββ cloudbuild/ # Cloud Build configs
workspace(name = "chaseapp")
# Node.js rules
http_archive(
name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/releases/download/5.8.0/rules_nodejs-5.8.0.tar.gz",
sha256 = "698d0fb1c5aaae6641e3bc0e0c7154459b21e65a5a65f4ce4d6b1df66b7c77a2",
)
load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "npm_install")
# Go rules
http_archive(
name = "io_bazel_rules_go",
url = "https://github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip",
sha256 = "7a9f20ff3bbd9525449c412f0ed32f7251a0e0449b3935463466c2b8c09d06e0",
)
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_repository")
go_register_toolchains(version = "1.19")
# Flutter rules (custom or community)
load("//tools/build_rules:flutter", "flutter_repositories")
flutter_repositories()
# Load dependencies
load("//:deps.bzl", "deps")
deps()
# Node.js dependencies
npm_install(
name = "npm",
package_json = "//web:package.json",
package_json_path = "web/package.json",
)load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "npm_package_bin")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
# Source library
js_library(
name = "web_src",
srcs = glob([
"src/**/*",
"nuxt.config.js",
"package.json",
]),
deps = [
"//shared/types",
"//shared/utils",
],
)
# Development server
npm_package_bin(
name = "dev",
tool = "@npm//:node_modules/nuxi-edge/dist/cli.js",
args = ["dev"],
data = [":web_src"],
)
# Production build
npm_package_bin(
name = "build",
tool = "@npm//:node_modules/nuxi-edge/dist/cli.js",
args = ["build"],
data = [":web_src"],
)
# Docker image
container_image(
name = "web_image",
base = "//infrastructure/docker:node_base",
files = [":build"],
cmd = ["yarn", "start"],
)load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_binary")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "js_binary")
# Go API function
go_library(
name = "api_lib",
srcs = glob(["go_functions/API/*.go"]),
importpath = "chaseapp.tv/backend/api",
deps = [
"//shared/types",
"@com_google_cloud_firestore//:go_default_library",
"@com_firebase_google_go//:go_default_library",
],
)
go_binary(
name = "api",
embed = [":api_lib"],
visibility = ["//visibility:public"],
)
# Node.js function
js_library(
name = "add_user_lib",
srcs = glob(["js_functions/addUser/**/*.js"]),
deps = [
"//shared/types",
"@npm//:node_modules/firebase-admin",
],
)
js_binary(
name = "add_user",
entry_point = "js_functions/addUser/index.js",
deps = [":add_user_lib"],
)- Incremental Builds: Only rebuild changed Cloud Functions
- Parallel Execution: Build web, mobile, and backend simultaneously
- Remote Caching: Share build artifacts across development team
- Deterministic Builds: Reproducible builds for deployment
- Cross-language Development: Seamless work with TypeScript, Go, and Dart
- IDE Integration: Better IntelliSense and refactoring support
- Testing: Unified test execution across all components
- Dependency Management: Centralized version control
-
Initialize Bazel Workspace
mkdir chaseapp-bazel cd chaseapp-bazel touch WORKSPACE -
Set Up Basic Rules
- Install rules_nodejs, rules_go, Flutter rules
- Create initial .bazelrc configuration
- Set up dependency management
-
Migrate Shared Libraries
- Extract common TypeScript types
- Create protocol buffer definitions
- Set up shared utilities
-
Configure Nuxt.js Build
- Set up TypeScript compilation
- Configure TailwindCSS processing
- Create Docker containerization rules
-
Development Workflow
- Configure hot reloading for development
- Set up testing with Jest
- Create deployment targets
-
Migrate Go Functions
- Set up Go build rules
- Configure deployment to Cloud Functions
- Add integration testing
-
Migrate Node.js Functions
- Configure TypeScript compilation
- Set up Firebase Functions deployment
- Add unit and integration tests
-
Flutter Integration
- Set up Flutter build rules
- Configure Android/iOS builds
- Create deployment packages
-
Cross-platform Testing
- Integration testing across platforms
- End-to-end testing setup
- Performance testing
-
GitHub Actions Integration
name: Build and Test on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: bazelbuild/setup-bazelisk@v2 - run: bazel test //... - run: bazel build //...
-
Cloud Build Integration
- Create Bazel-based build configurations
- Set up artifact caching
- Configure deployment triggers
ChaseApp's current stack performs well for its use case, but has specific challenges:
- Real-time Performance: Already handles WebSocket connections effectively
- Familiar Technologies: JavaScript/TypeScript and Go have large talent pools
- Firebase Integration: Deep integration with Google Cloud services
- Mobile Performance: Flutter provides excellent cross-platform performance
- Language Fragmentation: Context switching between Node.js, Go, and Dart
- Real-time Scaling: Pusher and Stream Chat costs increase with usage
- Service Coordination: Complex coordination between multiple cloud functions
- Deployment Complexity: Multiple deployment targets and configurations
- Phoenix Channels: 1M+ concurrent WebSocket connections on single instance
- BEAM VM: Automatic load balancing across CPU cores
- Fault Tolerance: Supervision trees prevent cascade failures
- Hot Code Reloading: Zero-downtime deployments
- Unified Language: Single language for backend services
- Phoenix LiveView: Real-time UI updates without complex JavaScript
- Built-in Pub/Sub: Eliminates need for external messaging services
- Ecto: Powerful database ORM with migrations and query optimization
- VM Hot-swapping: Update code without restarting servers
- Distribution: Native clustering and load balancing
- Observability: Built-in metrics and telemetry
- Concurrency: Lightweight processes for handling thousands of connections
Based on current industry benchmarks:
- Phoenix (Elixir): 1M+ concurrent WebSocket connections
- Go: 500K-750K concurrent connections
- Node.js: 200K-400K concurrent connections
- Go: ~45K requests/second (highest raw performance)
- Phoenix: ~38K requests/second (competitive)
- Node.js: ~22K requests/second (lowest)
- Go: Most memory efficient (~50MB base, linear growth)
- Phoenix: Higher base memory (~200MB) but stable under load
- Node.js: Variable usage, potential memory leaks
Technical Benefits:
- Real-time Superiority: Phoenix is designed for ChaseApp's exact use case
- Simplified Architecture: Single language reduces complexity
- Cost Efficiency: Eliminate Pusher/Stream Chat licensing costs
- Fault Tolerance: BEAM VM's "let it crash" philosophy improves uptime
- Scalability: Linear scaling for concurrent users
Business Benefits:
- Performance Gains: Better user experience during high-traffic events
- Operational Savings: Reduced infrastructure and third-party costs
- Developer Productivity: Unified stack reduces context switching
- Future-Proofing: Better positioned for scaling to millions of users
Risks and Costs:
- Team Transition: Learning curve for Elixir/Phoenix
- Talent Market: Smaller pool of experienced Elixir developers
- Migration Risk: Potential downtime during transition
- Ecosystem Maturity: Smaller ecosystem compared to Node.js
- Firebase Integration: Need to re-establish Firebase integrations
Current System Adequacy:
- Working Solution: Current system meets requirements
- Incremental Improvements: Can optimize existing stack
- Team Expertise: Current team proficient in existing technologies
- Risk Aversion: Rewrite introduces significant project risk
Rather than a complete rewrite, I recommend a phased hybrid approach:
- Target Service: Rewrite
pusherandgetStreamsfunctions in Phoenix - Goal: Validate Elixir performance for real-time features
- Team: Small team of 2-3 developers dedicated to pilot
- Success Metrics:
- Performance improvements (latency, concurrent connections)
- Development velocity metrics
- Operational cost savings
Based on pilot success:
- Migrate Real-time Services: Move chat, notifications, and live tracking to Phoenix
- Keep High-performance APIs: Maintain Go services for CPU-intensive tasks
- Preserve Web/Mobile: No changes to frontend applications
- Hybrid Architecture: Phoenix for real-time, Go for data processing, Node.js for legacy services
- Performance Analysis: Compare hybrid vs. current performance
- Cost-Benefit Analysis: Evaluate operational savings vs. migration costs
- Decision Point: Determine if full Elixir migration is justified
- Set up Bazel monorepo with current technologies
- Start Elixir pilot with real-time notification service
- Performance benchmarking of current stack
- Team training on Elixir/Phoenix fundamentals
- Bazel migration completion for all services
- Elixir pilot evaluation and results analysis
- Gradual service migration based on pilot success
- Infrastructure optimization using Bazel's caching
- Optimized hybrid architecture leveraging strengths of each technology
- Improved developer productivity through unified build system
- Enhanced performance for real-time features
- Reduced operational costs through architecture optimization
- Timeline: 2-3 months
- Effort: Medium
- Impact: High
- ROI: Significant long-term productivity gains
Actions:
- Start with Bazel workspace setup this week
- Migrate shared libraries first
- Focus on build performance improvements
- Gradual migration of individual services
- Timeline: 3-6 months for pilot
- Effort: High
- Impact: Potentially transformative
- ROI: High but with significant upfront investment
Actions:
- Begin team training on Elixir/Phoenix
- Start pilot project with real-time notification service
- Establish performance benchmarks
- Evaluate based on pilot results
- Timeline: 6-12 months
- Effort: Medium
- Impact: Medium
- ROI: Moderate
Actions:
- Consolidate duplicate functionality
- Implement better service boundaries
- Add comprehensive monitoring and observability
- Optimize Firebase usage patterns
- Timeline: Ongoing
- Effort: Low to Medium
- Impact: High
- ROI: Significant
Actions:
- Cross-training on different parts of the stack
- Documentation and knowledge sharing
- Best practices establishment
- Tooling and workflow improvements
ChaseApp currently has massive cost inefficiencies that can be reduced by 60-90%:
- Stream Chat: $200-800/month (pay-per-message)
- Pusher WebSocket: $150-600/month (pay-per-active-user)
- Pusher Beams: $100-400/month (push notifications)
- Mapbox: $100-500/month (pay-per-map-request)
- Algolia Search: $50-300/month (pay-per-search-operation)
- Firebase Usage: $200-800/month (Firestore, Storage, Functions)
- Sentry Error Monitoring: $50-200/month
Problem: Using BOTH Stream Chat AND Pusher simultaneously Solution: Choose ONE service and migrate the other
# Edit web/package.json - Remove these lines:
"pusher-js": "^7.0.6",
"pusher-js-react": "^1.0.0"
# Edit mobile/pubspec.yaml - Remove these lines:
"pusher_channels_flutter": ^2.0.2,
"pusher_beams": ^1.1.1
# Remove Pusher environment variables from:
# api/API/.env.yaml-SAMPLE
# api/pusher/functions/index.js
# api/getStreams/functions/index.js- Week 1: Update package.json files, remove dependencies
- Week 1: Update all components using Pusher to use Stream Chat instead
- Week 2: Remove Pusher Cloud Functions and API endpoints
- Week 2: Deploy and test
Files to modify:
web/components/chat/- All chat componentsweb/plugins/pusher.client.js- Remove this pluginmobile/lib/src/modules/chat/- Update Flutter chat modulesapi/pusher/- Delete entire directoryapi/getStreams/- Update to use Stream Chat SDK
Problem: Running 3 separate Google Analytics implementations Solution: Keep only GA4, remove Universal Analytics
// Edit web/nuxt.config.js
export default {
// REMOVE these lines:
'UA-87374124-3', // Universal Analytics (deprecated)
'G-V87EKNP10J', // Duplicate Firebase Analytics
// KEEP only GA4:
googleAnalytics: {
id: 'G-BYC6KDR1PM' // Primary GA4 property
}
}
// Remove web/plugins/firebaseAnalytics.client.ts (duplicate)MapLibre is open-source fork of Mapbox GL - completely FREE
# Install MapLibre
cd web
npm install maplibre-gl @maplibre/maplibre-gl-geocoder
# Remove Mapbox dependencies
npm uninstall mapbox-gl @mapbox/mapbox-gl-geocoder @mapbox/mapbox-gl-traffic<!-- components/Map.vue - Update imports -->
<script>
// REMOVE:
import mapboxgl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css'
// REPLACE with:
import maplibregl from 'maplibre-gl'
import 'maplibre-gl/dist/maplibre-gl.css'
export default {
mounted() {
// REMOVE:
// mapboxgl.accessToken = process.env.MAPBOX_TOKEN
// REPLACE with:
this.map = new maplibregl.Map({
container: this.$refs.mapContainer,
style: 'https://demotiles.maplibre.org/style.json', // Free tiles
center: [-74.5, 40],
zoom: 9
})
}
}
</script>
<style>
/* UPDATE CSS classes */
.mapboxgl-map β .maplibregl-map
.mapboxgl-popup β .maplibregl-popup
/* Update all mapbox CSS classes to maplibregl */
</style>Typesense is open-source, typo-tolerant search - completely FREE
# Install Typesense
cd web
npm install typesense
# Remove Algolia
npm uninstall algoliasearch# docker-compose.yml
version: '3.8'
services:
typesense:
image: typesense/typesense:26.0
ports:
- "8108:8108"
environment:
- TYPESENSE_API_KEY=your-secure-api-key-here
- TYPESENSE_DATA_DIR=/data
volumes:
- ./typesense-data:/data
restart: unless-stopped// plugins/typesense.client.js
import Typesense from 'typesense'
export default ({ app }, inject) => {
const typesenseClient = new Typesense.Client({
nodes: [{
host: 'localhost',
port: '8108',
protocol: 'http'
}],
apiKey: process.env.TYPESENSE_API_KEY,
connectionTimeoutSeconds: 2
})
inject('typesense', typesenseClient)
}
// Replace Algolia calls in components:
// BEFORE:
// const index = algoliaClient.initIndex('chases')
// const results = await index.search(query)
// AFTER:
// const results = await app.$typesense.collections('chases').documents().search({
// q: query,
// query_by: 'title,description'
// })Replace Pusher Beams with Firebase Cloud Messaging (already included)
// api/fcmMessaging/functions/index.js - Already exists, expand usage
// web/composables/useNotifications.js
import { getMessaging, getToken, onMessage } from 'firebase/messaging'
export const useNotifications = () => {
const requestPermission = async () => {
try {
const permission = await Notification.requestPermission()
if (permission === 'granted') {
const messaging = getMessaging()
const token = await getToken(messaging, {
vapidKey: process.env.VAPID_PUBLIC_KEY
})
// Send token to backend
await $fetch('/api/register-fcm-token', {
method: 'POST',
body: { token }
})
return token
}
} catch (error) {
console.error('Notification permission error:', error)
}
}
const setupMessageListener = () => {
const messaging = getMessaging()
onMessage(messaging, (payload) => {
console.log('Received foreground message:', payload)
// Handle notification display
})
}
return { requestPermission, setupMessageListener }
}Problem: 25+ separate Cloud Functions causing unnecessary costs Solution: Merge similar functions into microservices
// Create consolidated services:
// NEW: api/services/userService/index.js
// Consolidates: addUser, updateUser, getUser, deleteUser
export const userService = async (req, res) => {
const { action, data } = req.body
switch (action) {
case 'CREATE':
return await createUser(data)
case 'UPDATE':
return await updateUser(data)
case 'GET':
return await getUser(data.id)
case 'DELETE':
return await deleteUser(data.id)
default:
throw new Error('Invalid action')
}
}
// NEW: api/services/notificationService/index.js
// Consolidates: fcmMessaging, pusher notifications
export const notificationService = async (req, res) => {
const { type, recipients, message } = req.body
switch (type) {
case 'PUSH':
return await sendPushNotification(recipients, message)
case 'EMAIL':
return await sendEmail(recipients, message)
case 'IN_APP':
return await sendInAppNotification(recipients, message)
}
}Reduce Firebase database reads by 70-90%
# Add Redis dependency
cd api
npm install ioredis// api/middleware/cache.js
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL)
export const cacheMiddleware = async (req, res, next) => {
// Only cache GET requests
if (req.method !== 'GET') return next()
const cacheKey = `cache:${req.path}:${JSON.stringify(req.query)}`
const cached = await redis.get(cacheKey)
if (cached) {
res.setHeader('X-Cache', 'HIT')
return res.json(JSON.parse(cached))
}
// Intercept response to cache it
const originalJson = res.json
res.json = function(data) {
// Cache for 5 minutes
redis.setex(cacheKey, 300, JSON.stringify(data))
res.setHeader('X-Cache', 'MISS')
return originalJson.call(this, data)
}
next()
}
// Apply to functions:
export const chaseAPI = async (req, res) => {
await cacheMiddleware(req, res, async () => {
// Your function logic here
})
}Replace external CDN dependencies
// web/nuxt.config.js
export default {
// BEFORE:
// script: [
// { src: 'https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.11/lottie.min.js' }
// ]
// AFTER:
script: [
{ src: '/js/lottie.min.js' } // Local file
]
}
// Download and place in web/static/js/lottie.min.js- Day 1: Remove duplicate analytics (save $20-100/month)
- Day 2-3: Choose real-time service (keep Stream Chat, remove Pusher)
- Day 4-5: Update web dependencies (remove Pusher packages)
- Day 6-7: Update mobile dependencies and rebuild app
- Week 1 Savings: $400-1000/month
- Day 8-10: Update all Pusher references to use Stream Chat
- Day 11-12: Remove Pusher Cloud Functions and APIs
- Day 13-14: Deploy and test real-time functionality
- Week 2 Savings: $350-1400/month
- Week 3: Replace Mapbox with MapLibre (save $100-500/month)
- Week 4: Deploy Typesense for search (save $50-300/month)
- Week 4: Set up FCM push notifications (save $100-400/month)
- Month 1 Total Savings: $600-2300/month
- Week 5-6: Consolidate Firebase Functions (save $100-300/month)
- Week 7-8: Implement Redis caching (save $150-400/month)
- Month 2 Additional Savings: $250-700/month
- Week 9-10: Self-host static assets
- Week 11-12: Performance monitoring and optimization
- Month 3 Additional Savings: $50-200/month
| Timeline | Current Cost | Optimized Cost | Monthly Savings |
|---|---|---|---|
| Week 1-2 | $850-3600 | $450-2200 | $400-1400 (40-50%) |
| Month 1 | $850-3600 | $250-1200 | $600-2400 (70-80%) |
| Month 2 | $850-3600 | $200-900 | $650-2700 (75-85%) |
| Month 3+ | $850-3600 | $150-800 | $700-2800 (80-90%) |
#!/bin/bash
# cost_optimization_week1.sh
echo "π Starting ChaseApp Cost Optimization - Week 1"
# Remove duplicate analytics
echo "π Removing duplicate analytics..."
sed -i 's/"UA-87374124-3"//g' web/nuxt.config.js
sed -i 's/"G-V87EKNP10J"//g' web/nuxt.config.js
# Remove Pusher from web
echo "π Removing Pusher from web..."
cd web
npm uninstall pusher-js pusher-js-react
rm -f plugins/pusher.client.js
# Remove Pusher from mobile
echo "π± Removing Pusher from mobile..."
cd ../mobile
flutter pub remove pusher_channels_flutter pusher_beams
# Install updated dependencies
echo "π¦ Installing updated dependencies..."
cd ../web
npm install
cd ../mobile
flutter pub get
echo "β
Week 1 cost optimization complete!"
echo "π° Expected savings: $400-1000/month"#!/bin/bash
# service_replacements_month1.sh
echo "π Starting Service Replacements - Month 1"
# Replace Mapbox with MapLibre
echo "πΊοΈ Replacing Mapbox with MapLibre..."
cd web
npm install maplibre-gl @maplibre/maplibre-gl-geocoder
npm uninstall mapbox-gl @mapbox/mapbox-gl-geocoder @mapbox/mapbox-gl-traffic
# Replace Algolia with Typesense
echo "π Replacing Algolia with Typesense..."
npm install typesense
npm uninstall algoliasearch
# Start Typesense Docker container
echo "π³ Starting Typesense..."
cd ..
docker-compose up -d typesense
echo "β
Service replacements complete!"
echo "π° Expected additional savings: $250-700/month"// Add to web/plugins/analytics.client.js
export default ({ app }, inject) => {
const performanceMonitor = {
trackPageLoad: () => {
const navigation = performance.getEntriesByType('navigation')[0]
console.log('Page load time:', navigation.loadEventEnd - navigation.fetchStart, 'ms')
},
trackAPIPerformance: (url, duration) => {
console.log(`API call to ${url} took ${duration}ms`)
// Send to analytics for monitoring
}
}
inject('performanceMonitor', performanceMonitor)
}// Create web/components/admin/CostDashboard.vue
<template>
<div class="cost-dashboard">
<h2>Monthly Cost Tracking</h2>
<div class="metrics-grid">
<div class="metric">
<h3>Current Month</h3>
<p class="cost">${{ currentMonthCost }}</p>
<p class="savings">Saved: ${{ monthlySavings }}</p>
</div>
<div class="metric">
<h3>API Calls</h3>
<p>{{ apiCalls.toLocaleString() }}</p>
<p class="reduction">{{ apiCallReduction }}% reduction</p>
</div>
<div class="metric">
<h3>Database Reads</h3>
<p>{{ dbReads.toLocaleString() }}</p>
<p class="reduction">{{ dbReadReduction }}% reduction</p>
</div>
</div>
</div>
</template># Create feature flags for gradual rollout
# api/config/featureFlags.js
export const featureFlags = {
USE_TYPESENSE: process.env.USE_TYPESENSE === 'true',
USE_MAPLIBRE: process.env.USE_MAPLIBRE === 'true',
USE_REDIS_CACHE: process.env.USE_REDIS_CACHE === 'true',
CONSOLIDATED_FUNCTIONS: process.env.CONSOLIDATED_FUNCTIONS === 'true'
}# If issues occur, rollback with:
git checkout HEAD~1 -- web/package.json mobile/pubspec.yaml
npm install # web
flutter pub get # mobile
# Restore old services
docker-compose down typesense # if Typesense issues
# Keep old Algolia/Search functions temporarily- Full application backup
- Performance baseline measurements
- Cost analysis of current services
- Team training on new tools
- Staging environment testing
- Week 1: Remove duplicates, consolidate analytics
- Week 2: Remove Pusher, finalize real-time service
- Week 3: Replace Mapbox with MapLibre
- Week 4: Replace Algolia with Typesense
- Week 5-6: Implement FCM push notifications
- Week 7-8: Consolidate Firebase Functions
- Week 9-10: Implement Redis caching
- Week 11-12: Final optimizations and monitoring
- Performance validation
- Cost verification
- User acceptance testing
- Documentation updates
- Monthly cost tracking setup
ChaseApp currently has significant cost inefficiencies that can be immediately addressed:
Primary Recommendation (Do This Week):
- Eliminate Pusher/Stream Chat redundancy - Save $350-1400/month
- Consolidate analytics - Save $20-100/month
- Expected immediate savings: $400-1000/month
Secondary Recommendations (Next Month):
- Replace Mapbox with MapLibre - Save $100-500/month
- Replace Algolia with Typesense - Save $50-300/month
- Self-host push notifications - Save $100-400/month
Long-term Optimizations:
- Consolidate Firebase Functions - Save $100-300/month
- Implement Redis caching - Save $150-400/month
Total Potential Savings: $700-2800/month (80-90% cost reduction)
This cost optimization plan provides immediate financial benefits while improving application performance and reducing technical debt. All recommended replacements use mature, open-source alternatives that have proven track records and active communities.
Next Steps:
- Start with Week 1 optimizations immediately
- Set up cost tracking dashboard
- Monitor performance metrics
- Execute gradual rollout plan
Updated: December 2025 - Added comprehensive cost optimization guide Next Review: January 2026