{"id":202,"date":"2026-02-09T09:21:34","date_gmt":"2026-02-09T09:21:34","guid":{"rendered":"https:\/\/iamraviraj.com\/blog\/?p=202"},"modified":"2026-02-10T22:40:20","modified_gmt":"2026-02-10T22:40:20","slug":"building-cardbundle-a-developers-journey","status":"publish","type":"post","link":"https:\/\/iamraviraj.com\/blog\/building-cardbundle-a-developers-journey\/","title":{"rendered":"Building CardBundle \u2014 A Developer&#8217;s Journey"},"content":{"rendered":"<!-- First, add these in Case Study Details meta box:\n     Project Label: Case Study\n     Hero Subtitle: From concept to production \u2014 the story of architecting and shipping a cross-platform loyalty card wallet with a full-stack backend, native widgets, and 17-language support.\n-->\n\n\n<section style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">About the Builder<\/div>\n<h2>Enterprise Architect. Hands-on Developer.<\/h2>\n<p style=\"font-size: 1.05rem; line-height: 1.7;\">CardBundle wasn&#8217;t born in a boardroom. It was built by someone who bridges the gap between enterprise architecture and hands-on code \u2014 someone who draws system diagrams and then implements them.<\/p>\n<p style=\"font-size: 1.05rem; line-height: 1.7; margin-top: 1rem;\">With deep expertise across the full technology spectrum, I brought a disciplined, architecture-first approach to what started as a personal itch: too many plastic loyalty cards, not enough pockets.<\/p>\n\n<div class=\"creds\" style=\"display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 2rem 0;\">\n    <div class=\"cred\" style=\"display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; font-size: 0.85rem; font-weight: 500;\">\n        <span class=\"icon\" style=\"width: 8px; height: 8px; border-radius: 50%; background: var(--accent);\"><\/span>AWS Certified Enterprise Architect\n    <\/div>\n    <div class=\"cred\" style=\"display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; font-size: 0.85rem; font-weight: 500;\">\n        <span class=\"icon\" style=\"width: 8px; height: 8px; border-radius: 50%; background: var(--blue);\"><\/span>TOGAF Certified\n    <\/div>\n    <div class=\"cred\" style=\"display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; font-size: 0.85rem; font-weight: 500;\">\n        <span class=\"icon\" style=\"width: 8px; height: 8px; border-radius: 50%; background: var(--green);\"><\/span>PMP Certified\n    <\/div>\n    <div class=\"cred\" style=\"display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; font-size: 0.85rem; font-weight: 500;\">\n        <span class=\"icon\" style=\"width: 8px; height: 8px; border-radius: 50%; background: var(--purple);\"><\/span>SME Mobile App Developer\n    <\/div>\n    <div class=\"cred\" style=\"display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; font-size: 0.85rem; font-weight: 500;\">\n        <span class=\"icon\" style=\"width: 8px; height: 8px; border-radius: 50%; background: var(--pink);\"><\/span>Full-Stack Hands-on Engineer\n    <\/div>\n<\/div>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">The Problem<\/div>\n<h2>Loyalty cards deserve better<\/h2>\n<p style=\"font-size: 1.05rem; line-height: 1.7;\">We all carry them \u2014 supermarket cards, coffee shop punch cards, gym memberships. They clutter wallets, get lost, and their barcodes fade. Existing solutions were bloated, privacy-invasive, or locked into specific ecosystems.<\/p>\n<p style=\"font-size: 1.05rem; line-height: 1.7; margin-top: 1rem;\">I wanted something minimal and universal: scan your card, store it, pull up the barcode at checkout. That&#8217;s it. No accounts with retailers, no data harvesting \u2014 just your cards, on your phone, always ready.<\/p>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">At a Glance<\/div>\n<h2>The build in numbers<\/h2>\n<div class=\"stats\" style=\"display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; margin: 2.5rem 0;\">\n    <div class=\"stat\" style=\"text-align: center; padding: 1.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px;\">\n        <div class=\"stat-value\" style=\"font-size: 2rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), #f0c987); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">2<\/div>\n        <div class=\"stat-label\" style=\"font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;\">Platforms<\/div>\n    <\/div>\n    <div class=\"stat\" style=\"text-align: center; padding: 1.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px;\">\n        <div class=\"stat-value\" style=\"font-size: 2rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), #f0c987); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">17<\/div>\n        <div class=\"stat-label\" style=\"font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;\">Languages<\/div>\n    <\/div>\n    <div class=\"stat\" style=\"text-align: center; padding: 1.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px;\">\n        <div class=\"stat-value\" style=\"font-size: 2rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), #f0c987); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">14<\/div>\n        <div class=\"stat-label\" style=\"font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;\">API Endpoints<\/div>\n    <\/div>\n    <div class=\"stat\" style=\"text-align: center; padding: 1.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px;\">\n        <div class=\"stat-value\" style=\"font-size: 2rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), #f0c987); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">4<\/div>\n        <div class=\"stat-label\" style=\"font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;\">Barcode Formats<\/div>\n    <\/div>\n    <div class=\"stat\" style=\"text-align: center; padding: 1.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px;\">\n        <div class=\"stat-value\" style=\"font-size: 2rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), #f0c987); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">2<\/div>\n        <div class=\"stat-label\" style=\"font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;\">Native Widgets<\/div>\n    <\/div>\n    <div class=\"stat\" style=\"text-align: center; padding: 1.5rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px;\">\n        <div class=\"stat-value\" style=\"font-size: 2rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), #f0c987); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">1<\/div>\n        <div class=\"stat-label\" style=\"font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;\">Developer<\/div>\n    <\/div>\n<\/div>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">The Journey<\/div>\n<h2>From first commit to the App Store<\/h2>\n\n<div class=\"timeline\" style=\"position: relative; padding-left: 2rem; margin-top: 2.5rem;\">\n    <div style=\"content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: linear-gradient(to bottom, var(--accent), var(--border));\"><\/div>\n\n    <div class=\"timeline-item\" style=\"position: relative; margin-bottom: 2.5rem;\">\n        <div style=\"content: ''; position: absolute; left: -2rem; top: 0.5rem; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); transform: translateX(-4px);\"><\/div>\n        <div class=\"timeline-phase\" style=\"font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 0.35rem;\">Phase 1 \u2014 Foundation<\/div>\n        <h3 style=\"color: var(--text); font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem;\">Initial Architecture &amp; Scaffolding<\/h3>\n        <p style=\"color: var(--text-muted); font-size: 0.95rem;\">Started with a clear separation: a Sails.js backend serving a RESTful API backed by PostgreSQL, and an Expo-managed React Native frontend. Applied TOGAF principles \u2014 defined bounded contexts early (auth, cards, users, subscriptions) and established clean API contracts before writing a line of UI code.<\/p>\n    <\/div>\n\n    <div class=\"timeline-item\" style=\"position: relative; margin-bottom: 2.5rem;\">\n        <div style=\"content: ''; position: absolute; left: -2rem; top: 0.5rem; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); transform: translateX(-4px);\"><\/div>\n        <div class=\"timeline-phase\" style=\"font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 0.35rem;\">Phase 2 \u2014 Core Backend<\/div>\n        <h3 style=\"color: var(--text); font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem;\">Auth, API &amp; Data Layer<\/h3>\n        <p style=\"color: var(--text-muted); font-size: 0.95rem;\">Built JWT-based authentication with Passport.js \u2014 access tokens (10min) and refresh tokens (15 days). Implemented a smart token refresh queue on the mobile client that prevents race conditions when multiple API calls fail simultaneously. Designed the LoyaltyCard model with Waterline ORM, supporting image uploads via Multer with automatic cleanup of old files on update\/delete.<\/p>\n    <\/div>\n\n    <div class=\"timeline-item\" style=\"position: relative; margin-bottom: 2.5rem;\">\n        <div style=\"content: ''; position: absolute; left: -2rem; top: 0.5rem; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); transform: translateX(-4px);\"><\/div>\n        <div class=\"timeline-phase\" style=\"font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 0.35rem;\">Phase 3 \u2014 Mobile Experience<\/div>\n        <h3 style=\"color: var(--text); font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem;\">Scanning, Cards &amp; State Management<\/h3>\n        <p style=\"color: var(--text-muted); font-size: 0.95rem;\">Integrated expo-barcode-scanner with custom overlay UI and auto-format detection (CODE128, EAN-13, EAN-8, UPC-A). Built the card management flow with Redux Toolkit \u2014 async thunks for CRUD operations, Redux-Persist for offline access, and a card preview with luminance-aware text colors. Added card image flip animations for front\/back views.<\/p>\n    <\/div>\n\n    <div class=\"timeline-item\" style=\"position: relative; margin-bottom: 2.5rem;\">\n        <div style=\"content: ''; position: absolute; left: -2rem; top: 0.5rem; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); transform: translateX(-4px);\"><\/div>\n        <div class=\"timeline-phase\" style=\"font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 0.35rem;\">Phase 4 \u2014 Going Native<\/div>\n        <h3 style=\"color: var(--text); font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem;\">Home Screen Widgets (iOS &amp; Android)<\/h3>\n        <p style=\"color: var(--text-muted); font-size: 0.95rem;\">This was the most challenging phase. Built iOS WidgetKit extensions using Swift with App Groups for data sharing, and Android widgets using react-native-android-widget. Created Redux middleware that intercepts card CRUD actions and automatically syncs data to both widget systems. Added deep linking (cardbundle:\/\/) so tapping a widget card opens the app directly to that card.<\/p>\n    <\/div>\n\n    <div class=\"timeline-item\" style=\"position: relative; margin-bottom: 2.5rem;\">\n        <div style=\"content: ''; position: absolute; left: -2rem; top: 0.5rem; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); transform: translateX(-4px);\"><\/div>\n        <div class=\"timeline-phase\" style=\"font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 0.35rem;\">Phase 5 \u2014 Global Reach<\/div>\n        <h3 style=\"color: var(--text); font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem;\">17-Language Internationalization<\/h3>\n        <p style=\"color: var(--text-muted); font-size: 0.95rem;\">Implemented i18n-js with expo-localization for automatic device locale detection. Full translation coverage across English, French, Spanish, Portuguese, German, Italian, Polish, Russian, Ukrainian, Korean, Japanese, Turkish, Hindi, Bengali, Marathi, Telugu, Tamil, and Gujarati. Every screen, button, error message, and placeholder \u2014 localized.<\/p>\n    <\/div>\n\n    <div class=\"timeline-item\" style=\"position: relative; margin-bottom: 2.5rem;\">\n        <div style=\"content: ''; position: absolute; left: -2rem; top: 0.5rem; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); transform: translateX(-4px);\"><\/div>\n        <div class=\"timeline-phase\" style=\"font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 0.35rem;\">Phase 6 \u2014 Production<\/div>\n        <h3 style=\"color: var(--text); font-size: 1.25rem; font-weight: 600; margin-bottom: 0.75rem;\">Deployment, Monitoring &amp; Store Compliance<\/h3>\n        <p style=\"color: var(--text-muted); font-size: 0.95rem;\">Deployed the backend to production, integrated Firebase Analytics and Crashlytics for real-time monitoring. Navigated Apple&#8217;s review process \u2014 encryption declarations, camera permission descriptions, App Transport Security compliance. Resolved Android Play Store 16KB page size warnings. Configured SMTP via Zoho for transactional emails (password resets, verification).<\/p>\n    <\/div>\n<\/div>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">Architecture<\/div>\n<h2>System design overview<\/h2>\n<p style=\"font-size: 1.05rem; line-height: 1.7;\">Applying enterprise architecture patterns to a mobile-first product. Clean separation of concerns, stateless API design, and event-driven widget synchronization.<\/p>\n\n<div class=\"arch-diagram\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 2rem; margin-top: 2rem; font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; line-height: 1.8; color: var(--text-muted); overflow-x: auto; white-space: pre;\"><span class=\"highlight\" style=\"color: var(--accent); font-weight: 500;\">CLIENT LAYER<\/span>\n<span class=\"blue\" style=\"color: var(--blue);\">React Native (Expo)<\/span>  \u2500\u2500  Redux Toolkit  \u2500\u2500  Redux-Persist\n      \u2502                              \u2502\n      \u2502  REST \/ JWT Bearer           \u2502  <span class=\"green\" style=\"color: var(--green);\">Widget Middleware<\/span>\n      \u2502                              \u2502\n<span class=\"highlight\" style=\"color: var(--accent); font-weight: 500;\">API LAYER<\/span>                          \u25bc\n<span class=\"blue\" style=\"color: var(--blue);\">Sails.js<\/span>  \u2500\u2500  Passport.js     <span class=\"green\" style=\"color: var(--green);\">iOS WidgetKit<\/span>\n      \u2502         (JWT Auth)      <span class=\"green\" style=\"color: var(--green);\">Android Widget<\/span>\n      \u2502                              \u25b2\n<span class=\"highlight\" style=\"color: var(--accent); font-weight: 500;\">DATA LAYER<\/span>                         \u2502\n<span class=\"purple\" style=\"color: var(--purple);\">PostgreSQL<\/span>  \u2500\u2500  Waterline    App Groups \/ SharedPrefs\n<span class=\"purple\" style=\"color: var(--purple);\">File Store<\/span>  \u2500\u2500  Multer\n\n<span class=\"highlight\" style=\"color: var(--accent); font-weight: 500;\">SERVICES<\/span>\n<span class=\"pink\" style=\"color: var(--pink);\">Firebase<\/span>    \u2500\u2500  Analytics + Crashlytics\n<span class=\"pink\" style=\"color: var(--pink);\">Nodemailer<\/span>  \u2500\u2500  Zoho SMTP\n<span class=\"pink\" style=\"color: var(--pink);\">Stripe<\/span>      \u2500\u2500  Subscriptions<\/div>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">Tech Stack<\/div>\n<h2>Tools of the trade<\/h2>\n\n<div class=\"tech-grid\" style=\"display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; margin-top: 2rem;\">\n    <div class=\"tech-card\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; transition: border-color 0.2s, transform 0.2s;\">\n        <h4 style=\"font-size: 0.95rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text);\">Frontend<\/h4>\n        <p style=\"font-size: 0.85rem; color: var(--text-muted); line-height: 1.6;\">React Native 0.74 with Expo 51. TypeScript for type safety. TailwindCSS (twrnc) for styling with dark mode support.<\/p>\n        <span class=\"tech-tag\" style=\"display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 0.75rem; font-weight: 500; background: var(--accent-dim); color: var(--accent);\">React Native + Expo<\/span>\n    <\/div>\n\n    <div class=\"tech-card\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem;\">\n        <h4 style=\"font-size: 0.95rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text);\">Backend<\/h4>\n        <p style=\"font-size: 0.85rem; color: var(--text-muted); line-height: 1.6;\">Sails.js v1.5 \u2014 a convention-over-configuration Node.js MVC framework. Waterline ORM for database abstraction.<\/p>\n        <span class=\"tech-tag\" style=\"display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 0.75rem; font-weight: 500; background: rgba(96, 165, 250, 0.12); color: var(--blue);\">Sails.js + PostgreSQL<\/span>\n    <\/div>\n\n    <div class=\"tech-card\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem;\">\n        <h4 style=\"font-size: 0.95rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text);\">Authentication<\/h4>\n        <p style=\"font-size: 0.85rem; color: var(--text-muted); line-height: 1.6;\">Passport.js with JWT strategy. Dual-token system with transparent refresh. bcrypt password hashing.<\/p>\n        <span class=\"tech-tag\" style=\"display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 0.75rem; font-weight: 500; background: rgba(74, 222, 128, 0.12); color: var(--green);\">JWT + Passport.js<\/span>\n    <\/div>\n\n    <div class=\"tech-card\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem;\">\n        <h4 style=\"font-size: 0.95rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text);\">State Management<\/h4>\n        <p style=\"font-size: 0.85rem; color: var(--text-muted); line-height: 1.6;\">Redux Toolkit with 4 slices (auth, cards, user, language). Redux-Persist to AsyncStorage for offline support.<\/p>\n        <span class=\"tech-tag\" style=\"display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 0.75rem; font-weight: 500; background: rgba(167, 139, 250, 0.12); color: var(--purple);\">Redux Toolkit<\/span>\n    <\/div>\n\n    <div class=\"tech-card\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem;\">\n        <h4 style=\"font-size: 0.95rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text);\">Native Widgets<\/h4>\n        <p style=\"font-size: 0.85rem; color: var(--text-muted); line-height: 1.6;\">iOS WidgetKit via @bittingz\/expo-widgets. Android widgets via react-native-android-widget. Redux middleware for sync.<\/p>\n        <span class=\"tech-tag\" style=\"display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 0.75rem; font-weight: 500; background: rgba(244, 114, 182, 0.12); color: var(--pink);\">WidgetKit + Android Widgets<\/span>\n    <\/div>\n\n    <div class=\"tech-card\" style=\"background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem;\">\n        <h4 style=\"font-size: 0.95rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text);\">Observability<\/h4>\n        <p style=\"font-size: 0.85rem; color: var(--text-muted); line-height: 1.6;\">Firebase Analytics for user behavior tracking. Crashlytics for real-time crash monitoring. Custom event logging.<\/p>\n        <span class=\"tech-tag\" style=\"display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 0.75rem; font-weight: 500; background: rgba(251, 191, 36, 0.12); color: #fbbf24;\">Firebase Analytics<\/span>\n    <\/div>\n<\/div>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem;\">\n<div class=\"section-label\">Challenges &amp; Solutions<\/div>\n<h2>What made it hard<\/h2>\n\n<ul class=\"challenge-list\" style=\"list-style: none; margin-top: 1.5rem; padding: 0;\">\n    <li style=\"padding: 1.25rem 1.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 0.75rem; font-size: 0.95rem;\">\n        <strong style=\"display: block; margin-bottom: 0.35rem; color: var(--text);\">Token refresh race conditions<\/strong>\n        <span style=\"color: var(--text-muted); font-size: 0.88rem;\">Multiple API calls failing simultaneously would trigger parallel refresh requests. Solved with a single-queue mechanism \u2014 the first 401 triggers a refresh, subsequent failures wait for the same promise, then all retry with the new token.<\/span>\n    <\/li>\n    <li style=\"padding: 1.25rem 1.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 0.75rem; font-size: 0.95rem;\">\n        <strong style=\"display: block; margin-bottom: 0.35rem; color: var(--text);\">Dual-platform native widgets<\/strong>\n        <span style=\"color: var(--text-muted); font-size: 0.88rem;\">iOS WidgetKit (Swift) and Android widgets (Java\/Kotlin) have fundamentally different data-sharing models. Bridged them through Redux middleware that intercepts card state changes and writes to App Groups (iOS) and SharedPreferences (Android) simultaneously.<\/span>\n    <\/li>\n    <li style=\"padding: 1.25rem 1.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 0.75rem; font-size: 0.95rem;\">\n        <strong style=\"display: block; margin-bottom: 0.35rem; color: var(--text);\">Barcode format detection<\/strong>\n        <span style=\"color: var(--text-muted); font-size: 0.88rem;\">Different loyalty cards use different barcode formats. Built a dynamic detection system that analyzes the scanned number pattern and selects the correct format (CODE128, EAN-13, EAN-8, UPC-A) for accurate rendering.<\/span>\n    <\/li>\n    <li style=\"padding: 1.25rem 1.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 0.75rem; font-size: 0.95rem;\">\n        <strong style=\"display: block; margin-bottom: 0.35rem; color: var(--text);\">Apple review compliance<\/strong>\n        <span style=\"color: var(--text-muted); font-size: 0.88rem;\">Navigated encryption declarations (HTTPS\/TLS), camera permission justifications, and App Transport Security requirements. Each rejection was a lesson in reading Apple&#8217;s guidelines more carefully.<\/span>\n    <\/li>\n    <li style=\"padding: 1.25rem 1.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 0.75rem; font-size: 0.95rem;\">\n        <strong style=\"display: block; margin-bottom: 0.35rem; color: var(--text);\">Email delivery in production<\/strong>\n        <span style=\"color: var(--text-muted); font-size: 0.88rem;\">Went through multiple SMTP port configurations (465, 587) and TLS settings with Zoho before landing on a stable email delivery pipeline for password resets and verification.<\/span>\n    <\/li>\n<\/ul>\n<\/section>\n\n\n\n<div class=\"divider\" style=\"max-width: 900px; margin: 2rem auto; padding: 0 2rem;\"><hr style=\"border: none; border-top: 1px solid var(--border);\"><\/div>\n\n\n\n<section class=\"animate-on-scroll\" style=\"max-width: 900px; margin: 0 auto; padding: 3rem 2rem 5rem;\">\n<div class=\"section-label\">Reflection<\/div>\n<h2>What I took away<\/h2>\n<p style=\"font-size: 1.05rem; line-height: 1.7;\">CardBundle started as a weekend project and evolved into a production application serving users across both major mobile platforms. Building it solo \u2014 from database schema design to App Store submission \u2014 reinforced something I&#8217;ve always believed: the best architects are the ones who still write code.<\/p>\n<p style=\"font-size: 1.05rem; line-height: 1.7; margin-top: 1rem;\">My enterprise architecture background (TOGAF, AWS) shaped how I approached system boundaries, API contracts, and separation of concerns. My PMP training kept scope creep in check. But it was the hands-on development \u2014 debugging a WidgetKit extension at midnight, wrestling with Expo prebuild configs, writing the 17th translation file \u2014 that made this product real.<\/p>\n<p style=\"font-size: 1.05rem; line-height: 1.7; margin-top: 1rem;\">Every loyalty card scanned, every barcode displayed at a checkout counter, every widget glanced at on a home screen \u2014 that&#8217;s architecture in action.<\/p>\n<\/section>\n\n","protected":false},"excerpt":{"rendered":"<p>About the Builder Enterprise Architect. Hands-on Developer. CardBundle wasn&#8217;t born in a boardroom. It was built by someone who bridges the gap between enterprise architecture and hands-on code \u2014 someone who draws system diagrams and then implements them. With deep expertise across the full technology spectrum, I brought a disciplined, architecture-first approach to what started&#8230;<\/p>\n","protected":false},"author":1,"featured_media":206,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,16],"tags":[],"class_list":["post-202","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-engineering","category-vibe-coding"],"_links":{"self":[{"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/posts\/202","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/comments?post=202"}],"version-history":[{"count":1,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/posts\/202\/revisions"}],"predecessor-version":[{"id":203,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/posts\/202\/revisions\/203"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/media\/206"}],"wp:attachment":[{"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/media?parent=202"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/categories?post=202"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iamraviraj.com\/blog\/wp-json\/wp\/v2\/tags?post=202"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}