<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eric Rodríguez</title>
    <description>The latest articles on Forem by Eric Rodríguez (@ericrodriguez10).</description>
    <link>https://forem.com/ericrodriguez10</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3677578%2Fcebf54ee-c6c0-436d-8570-7412b62d4321.jpeg</url>
      <title>Forem: Eric Rodríguez</title>
      <link>https://forem.com/ericrodriguez10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ericrodriguez10"/>
    <language>en</language>
    <item>
      <title>Day 91: Why I stopped using GenAI for budgeting math</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Tue, 26 May 2026 13:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-91-why-i-stopped-using-genai-for-budgeting-math-1d4k</link>
      <guid>https://forem.com/ericrodriguez10/day-91-why-i-stopped-using-genai-for-budgeting-math-1d4k</guid>
      <description>&lt;p&gt;When building AI-powered apps, you have to know when to turn the AI off.&lt;/p&gt;

&lt;p&gt;Today, I was testing my Serverless Financial Agent. I asked it a simple question: "How much can I spend today without breaking my goal?"&lt;/p&gt;

&lt;p&gt;The Amazon Bedrock LLM gave me a beautifully written, highly confident answer. The only problem? The math was completely hallucinated.&lt;/p&gt;

&lt;p&gt;The Problem: LLMs predict the next best word; they don't do arithmetic. If you let GenAI calculate daily limits based on raw transaction data, it will eventually confidently authorize spending that bankrupts your user.&lt;/p&gt;

&lt;p&gt;The Fix: I added a deterministic interceptor in my AWS Lambda backend.&lt;/p&gt;

&lt;p&gt;Instead of passing the prompt directly to the AI, my Python router now uses regex to catch specific intents. If the user asks about their daily limit, Python takes over:&lt;/p&gt;

&lt;h1&gt;
  
  
  Deterministic Math &amp;gt; AI Hallucinations
&lt;/h1&gt;

&lt;p&gt;safe_daily_limit = (recorded_monthly_income / days_in_month) - daily_savings_goal&lt;/p&gt;

&lt;p&gt;If the system detects 0 recorded income for the month, it hard-stops and refuses to calculate a limit. The AI is only used to format the final delivery in its designated "tough love" persona, but the underlying numbers are strictly governed by standard code.&lt;/p&gt;

&lt;p&gt;I also decoupled my public landing page from my authenticated React SPA.&lt;br&gt;
If you want to use Google OAuth in your app, GCP requires publicly accessible /privacy and /terms pages. By splitting the routing, unauthenticated users hit a lightning-fast static landing page cached by AWS CloudFront, while authenticated users get routed directly to the heavy dashboard.&lt;/p&gt;

&lt;p&gt;Route language to the LLM. Route math to Python. Code for intelligence, but architect for accuracy!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8er8g39aeczs8qbw1xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8er8g39aeczs8qbw1xg.png" alt=" " width="799" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>react</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Day 90: Building a Resilient App Shell &amp; Theme Engine in React</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Mon, 25 May 2026 14:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-90-building-a-resilient-app-shell-theme-engine-in-react-27oc</link>
      <guid>https://forem.com/ericrodriguez10/day-90-building-a-resilient-app-shell-theme-engine-in-react-27oc</guid>
      <description>&lt;p&gt;A robust serverless backend is useless if your frontend feels like an unfinished prototype. Today, I executed a complete visual and structural redesign of my Financial Agent.&lt;/p&gt;

&lt;p&gt;Here are the technical highlights of the frontend overhaul:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Full Theme Persistence&lt;br&gt;
I built a theme engine supporting System, Light, and Black modes using #171717 and #f4f5f0 base colors. Instead of just keeping this state in the browser, the React app sends the appearance_preference to the AWS backend. This ensures cross-device UI continuity when the user authenticates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;App Shell Architecture&lt;br&gt;
I ripped out the top-bar navigation and implemented a proper desktop App Shell. It features a sticky left sidebar and a dedicated right insights panel for desktop. To fix native scrolling physics, I applied overscroll-behavior: none to the body, preventing the browser's default background rubber-banding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hardening the Local App Lock&lt;br&gt;
The PIN lock screen got a UX and security upgrade. I added an invisible input over the PIN dots to seamlessly trigger the mobile keyboard. Architecturally, I implemented a custom PBKDF2 fallback for environments where the native crypto.subtle API is blocked, and wired up a backend endpoint for server-side PIN verification.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Structure your UI with the same rigor you apply to your databases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y5g1lxz7gialmn52srh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y5g1lxz7gialmn52srh.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>ui</category>
      <category>aws</category>
      <category>saas</category>
    </item>
    <item>
      <title>Day 89: Building a Savings Dashboard &amp; Reusing AI Responses (FinOps)</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Sat, 23 May 2026 14:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-89-building-a-savings-dashboard-reusing-ai-responses-finops-5hd</link>
      <guid>https://forem.com/ericrodriguez10/day-89-building-a-savings-dashboard-reusing-ai-responses-finops-5hd</guid>
      <description>&lt;p&gt;Adding features to an AI-powered SaaS is easy. Doing it without increasing your LLM token consumption is the real architectural challenge.&lt;/p&gt;

&lt;p&gt;Today, I replaced the "Transactions" navigation tab with a dedicated "Savings" view in my Serverless Financial Agent. I moved existing gamification widgets there and built a new Daily Savings chart using Recharts to visualize the user's daily pacing against their target goals.&lt;/p&gt;

&lt;p&gt;The biggest win was the FinOps strategy. The new dashboard needed a financial recommendation. Instead of triggering a new, expensive Amazon Nova prompt, I decided to recycle data.&lt;/p&gt;

&lt;p&gt;Every morning, my AWS Lambda generates a daily report email containing a financial "Advice" section. I modified the backend to extract that exact block of text and persist it in the user's DynamoDB profile as latest_daily_savings_advice.&lt;/p&gt;

&lt;p&gt;Now, when the React frontend loads the Savings view, it simply fetches that cached string instead of pinging the AI. If there isn't enough signal, it defaults to a deterministic math calculation. By decoupling the AI generation from the UI rendering, I expanded the product's capabilities while keeping the marginal cost at exactly zero.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhm8av1ye1mhklyn4vt8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhm8av1ye1mhklyn4vt8n.png" alt=" " width="799" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>finops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Day 88: Architecting a Serverless Monetization Engine (DynamoDB &amp; Affiliate APIs)</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Fri, 22 May 2026 14:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-88-architecting-a-serverless-monetization-engine-dynamodb-affiliate-apis-13g9</link>
      <guid>https://forem.com/ericrodriguez10/day-88-architecting-a-serverless-monetization-engine-dynamodb-affiliate-apis-13g9</guid>
      <description>&lt;p&gt;Building a cloud application is fun until you get the AWS bill. To make my Serverless Financial Agent sustainable, I started building a contextual Monetization Engine today by applying to strict Fintech affiliate networks (financeAds and AdCredy).&lt;/p&gt;

&lt;p&gt;Getting approved by these networks is hard if you just have a static site. But when you explain that your traffic sits behind an AWS Cognito login and uses an AI model to evaluate real-time API banking data, you become a premium publisher.&lt;/p&gt;

&lt;p&gt;The Architecture of Affiliate Links:&lt;br&gt;
Never hardcode affiliate links in your frontend code. If a campaign pauses, your app breaks, and you have to trigger a full CI/CD pipeline just to swap a URL.&lt;/p&gt;

&lt;p&gt;Instead, I am designing a database-driven approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage: A new FinanceAgent-Offers table in DynamoDB will hold the offer_id, affiliate_url, and category.&lt;/li&gt;
&lt;li&gt;Logic: My Python Lambda function calculates the user's financial score. If they are broke, it pulls a credit offer from the DB. If they are saving well, it pulls an investment broker offer.&lt;/li&gt;
&lt;li&gt;Delivery: The React app just receives a clean JSON array and renders the UI cards dynamically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monetization should be treated as a backend microservice, entirely decoupled from your frontend presentation layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpstpdl8kqdg3oztw5xa7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpstpdl8kqdg3oztw5xa7.png" alt=" " width="799" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>saas</category>
      <category>architecture</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Day 87: Voice-Interactive AI with Amazon Polly &amp; DynamoDB Caching</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Thu, 21 May 2026 14:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-87-voice-interactive-ai-with-amazon-polly-dynamodb-caching-jdg</link>
      <guid>https://forem.com/ericrodriguez10/day-87-voice-interactive-ai-with-amazon-polly-dynamodb-caching-jdg</guid>
      <description>&lt;p&gt;Imagine asking your app directly, "What was my biggest expense this month?" and having it instantly reply aloud, scolding you like a ruthless personal financial coach.&lt;/p&gt;

&lt;p&gt;Today, I built a Voice Interaction loop into my Serverless Financial Agent. The architecture mixes native web APIs with AWS machine learning services, bound together by strict cost controls.&lt;/p&gt;

&lt;p&gt;The Audio Pipeline&lt;br&gt;
For input, I used the native browser SpeechRecognition API. It's completely free and captures the user's voice, submitting the form automatically upon silence.&lt;br&gt;
For output, the Python Lambda calls Amazon Polly to synthesize the AI's response using high-fidelity Neural voices.&lt;/p&gt;

&lt;p&gt;FinOps: The Caching Layer&lt;br&gt;
Amazon Polly can get expensive if abused. I built a caching mechanism in DynamoDB (FinanceAgent-Cache). Every time text is synthesized, the Base64 MP3 is saved with a SHA-256 hash of the text and language. If the same response needs to be read again, the backend serves the cached audio instead of calling Polly.&lt;/p&gt;

&lt;p&gt;Backend Gating &amp;amp; IAM&lt;br&gt;
I didn't just hide the microphone button in the UI. I secured the backend route (?action=synthesize_voice) to strictly reject any requests that don't match specific premium user emails verified via Cognito JWTs. Finally, I locked down the Lambda execution role with a single inline policy: polly:SynthesizeSpeech.&lt;/p&gt;

&lt;p&gt;Voice AI is a fantastic feature, but caching and access control are what make it production-ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gplhxy00f5p8ytwg54o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gplhxy00f5p8ytwg54o.png" alt=" " width="799" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>serverless</category>
      <category>python</category>
    </item>
    <item>
      <title>Day 86: Adding Multi-Language Support with AI Translation Caching</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Wed, 20 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-86-adding-multi-language-support-with-ai-translation-caching-29f3</link>
      <guid>https://forem.com/ericrodriguez10/day-86-adding-multi-language-support-with-ai-translation-caching-29f3</guid>
      <description>&lt;p&gt;Translating a static React app is easy. Translating a dynamic AI agent without draining your cloud budget requires architectural discipline.&lt;/p&gt;

&lt;p&gt;Today, I added support for 5 languages (en, es, fr, de, it) to my Serverless Financial Agent.&lt;/p&gt;

&lt;p&gt;The biggest risk was cost. If a user toggles the language dropdown, asking the LLM to regenerate the entire financial analysis is a waste of money.&lt;/p&gt;

&lt;p&gt;To fix this, I built a specific POST ?action=translate_message endpoint. It translates the currently visible AI string once and caches it in DynamoDB (FinanceAgent-Cache) using a SHA-256 hash of the language and text. Subsequent language toggles hit the NoSQL cache (cache_hit: true) instead of Amazon Nova, dropping the translation cost to zero.&lt;/p&gt;

&lt;p&gt;For scheduled tasks like daily SMS alerts and email reports, the backend simply reads the user's preferred_language from DynamoDB and generates the text natively during its normal run.&lt;/p&gt;

&lt;p&gt;Lesson learned: When scaling AI globally, caching is your best financial defense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0m1a2j8siyyfsk3nnnzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0m1a2j8siyyfsk3nnnzd.png" alt=" " width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>serverless</category>
      <category>finops</category>
    </item>
    <item>
      <title>Day 85: Building a Secure App Lock in React with PBKDF2</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Tue, 19 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-85-building-a-secure-app-lock-in-react-with-pbkdf2-3ca1</link>
      <guid>https://forem.com/ericrodriguez10/day-85-building-a-secure-app-lock-in-react-with-pbkdf2-3ca1</guid>
      <description>&lt;p&gt;Authentication gets you into the application, but what happens when a user leaves their laptop open? When building financial tools, an unattended screen is a critical vulnerability.&lt;/p&gt;

&lt;p&gt;Today, I added a Local App Lock to my Serverless Financial Agent. While AWS Cognito handles the primary session, this new layer acts as a local inactivity shield.&lt;/p&gt;

&lt;p&gt;The Cryptography Layer&lt;br&gt;
A 4-digit PIN is easy to brute-force if stored poorly. I ensured the PIN is never saved in plain text. Instead, I used the native Web Crypto API. The application generates a random salt and hashes the PIN using the PBKDF2 algorithm with SHA-256. Only pinHash and pinSalt live in localStorage, keeping the device secure.&lt;/p&gt;

&lt;p&gt;Idle Detection&lt;br&gt;
I built an event listener hook that tracks pointer movements, key presses, and touch events. If the user is inactive for 10 minutes, the financial dashboard is immediately obscured by the lock screen. It also tracks the visibilitychange event, measuring the exact elapsed time when the tab is running in the background.&lt;/p&gt;

&lt;p&gt;The "Forgot PIN" Failsafe&lt;br&gt;
If a user forgets their local PIN, there is no simple "reset link." For maximum security, clicking "Forgot PIN" instantly clears the local App Lock state and executes a hard sign-out via the AWS Amplify SDK. The user must fully re-authenticate with their Cognito credentials and create a brand new PIN from scratch.&lt;/p&gt;

&lt;p&gt;Frontend security is about defense in depth. Never trust local storage with plain text secrets, even if it is just a simple 4-digit PIN.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fob4td1xi94j32oay12zg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fob4td1xi94j32oay12zg.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>security</category>
      <category>javascript</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Day 84: Merging Cognito Identities &amp; Surviving a 502 Gateway Error</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Mon, 18 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-84-merging-cognito-identities-surviving-a-502-gateway-error-3hb5</link>
      <guid>https://forem.com/ericrodriguez10/day-84-merging-cognito-identities-surviving-a-502-gateway-error-3hb5</guid>
      <description>&lt;p&gt;Today I almost broke my serverless production environment, but AWS disaster recovery features saved the day.&lt;/p&gt;

&lt;p&gt;I was deploying a major update to my Financial Agent to fix a split identity issue. Users logging in with Email/Password and Google OAuth were getting separate DynamoDB partitions. I rewrote the backend to unify the identity resolution around the verified email address.&lt;/p&gt;

&lt;p&gt;At the same time, I was updating the transaction classification logic. The system needed to strictly separate refunds/credits from actual income so the AI wouldn't hallucinate false financial praise.&lt;/p&gt;

&lt;p&gt;The Deployment Incident:&lt;br&gt;
I deployed the new Python code to AWS Lambda, but I forgot to package a newly required module in the deployment zip. The immediate result was a global 502 Bad Gateway. While trying to patch it quickly, I caused a 500 Internal Server Error by calling an unimported configuration variable.&lt;/p&gt;

&lt;p&gt;The Rollback:&lt;br&gt;
Because I treat my serverless infrastructure seriously, I had taken two precautions before deploying:&lt;/p&gt;

&lt;p&gt;I created DynamoDB On-Demand backups for my Cache, Memory, and Transactions tables.&lt;/p&gt;

&lt;p&gt;I published numbered versions of my Lambda function instead of just overwriting $LATEST.&lt;/p&gt;

&lt;p&gt;Instead of panicking and writing hotfixes in the AWS Console, I simply rolled back the API Gateway to point to Lambda Version 1. The system stabilized instantly. I then fixed my local code, bundled the dependencies correctly, and deployed Version 4 cleanly.&lt;/p&gt;

&lt;p&gt;The lesson here is simple: Never deploy major logic changes without database backups and function versioning. Rollbacks should be a one-click operation, not a debugging session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvkbf3yisrv5xe54cim0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvkbf3yisrv5xe54cim0.png" alt=" " width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>serverless</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Day 83: AWS SNS SMS Alerts &amp; Segment Cost Optimization</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Fri, 15 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-83-aws-sns-sms-alerts-segment-cost-optimization-1e6d</link>
      <guid>https://forem.com/ericrodriguez10/day-83-aws-sns-sms-alerts-segment-cost-optimization-1e6d</guid>
      <description>&lt;p&gt;Adding SMS alerts to your serverless application is incredibly easy with AWS SNS. Keeping those alerts cheap, however, requires a bit of FinOps knowledge.&lt;/p&gt;

&lt;p&gt;Today, I restored the SMS alerting feature for my Serverless Financial Agent. If a user spends more than 100€ in a day, the system sends them a harsh financial reality check via text.&lt;/p&gt;

&lt;p&gt;Here are the critical architectural updates I made to secure the pipeline and optimize costs:&lt;/p&gt;

&lt;p&gt;The ASCII-Only Rule&lt;br&gt;
Initially, the AI included emojis in the SMS text. This was a costly mistake. Emojis force the message into UCS-2 encoding, shrinking the SMS segment limit from 160 characters to just 70. By refactoring the Python logic to use ASCII-only rotating phrases, I avoided multi-segment billing charges entirely.&lt;/p&gt;

&lt;p&gt;Strict IAM Policies&lt;br&gt;
I removed any broad SNS permissions from the Lambda execution role. Instead, I attached a granular inline policy: FinanceAgentSmsPublish. This ensures the Lambda only has the sns:Publish action allowed.&lt;/p&gt;

&lt;p&gt;Account-Level Guardrails&lt;br&gt;
AWS SNS can get expensive if a bug causes an infinite loop of messages. I configured the SNS MonthlySpendLimit attribute to a strict $1 limit while operating in the SNS Sandbox environment.&lt;/p&gt;

&lt;p&gt;The Lesson: When writing cloud-native code, you must treat your cloud bill as an architectural constraint. Optimize your payload encoding and always lock down your IAM roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdrxj8asepab8mq4chko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdrxj8asepab8mq4chko.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>serverless</category>
      <category>fintec</category>
    </item>
    <item>
      <title>Day 80: Building an Event-Driven Bank Sync Pipeline (Webhooks, SQS &amp; DynamoDB Locks)</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Fri, 15 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-80-building-an-event-driven-bank-sync-pipeline-webhooks-sqs-dynamodb-locks-4emk</link>
      <guid>https://forem.com/ericrodriguez10/day-80-building-an-event-driven-bank-sync-pipeline-webhooks-sqs-dynamodb-locks-4emk</guid>
      <description>&lt;p&gt;When building fintech applications, letting the client dictate when to fetch third-party banking data will eventually destroy your API rate limits and skyrocket your cloud bill.&lt;/p&gt;

&lt;p&gt;Today, I tore down my reactive data-fetching logic and replaced it with a resilient, event-driven webhook architecture using AWS Lambda, Amazon SQS, and DynamoDB. Here is how I solved the core architectural bottlenecks in my Serverless Financial Agent.&lt;/p&gt;

&lt;p&gt;Fixing the Stale Dashboard &amp;amp; Identity Resolution&lt;br&gt;
My React dashboard was showing stale data, while the daily automated emails had real-time numbers. The root cause? An identity partition mismatch. The background worker was using a default ID, while the frontend sometimes resolved to an email address instead of the strict Cognito sub ID. I unified the identity resolution algorithm so both endpoints read and write to the exact same DynamoDB partition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jwh7k4isgbuom0lf4rv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jwh7k4isgbuom0lf4rv.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Event-Driven Sync Pipeline&lt;br&gt;
I deprecated the dangerous user-triggered "Force Sync" pattern. Instead, I implemented a persistent Plaid Access Token and shifted to a Webhook-to-SQS pipeline.&lt;br&gt;
Now, when a transaction is added, modified, or removed, the Plaid API fires a webhook to my Lambda. To avoid timeouts, the Lambda instantly pushes a sync_transactions task to an SQS queue (FinanceAgent-SyncQueue) and returns a fast 202 Accepted back to the bank.&lt;/p&gt;

&lt;p&gt;Cursor-Based Sync &amp;amp; DynamoDB Locks&lt;br&gt;
The background SQS worker processes the event using a new /transactions/sync logic with a persistent cursor, fetching only the exact delta of changes. If the cursor fails, it gracefully falls back to the standard /transactions/get method.&lt;br&gt;
To prevent API abuse, I built atomic sync locks in DynamoDB. If a sync is already running, the request is safely ignored. I also added strict routing logic to protect my live Wise profile from this Plaid-specific reactive sync behavior.&lt;/p&gt;

&lt;p&gt;Curing AI Hallucinations&lt;br&gt;
Finally, I patched the semantic AI layer. The system was treating credit card refunds and cashbacks as actual "Income," artificially inflating the user's AI Financial Health Score. I updated the classification engine to isolate real income (payroll, Gusto, interest) from simple cashflow corrections, ensuring the AI context is 100% accurate.&lt;/p&gt;

&lt;p&gt;Verification &amp;amp; Next Steps&lt;br&gt;
I verified the backend with Python compile checks, confirmed Lambda deploys (LastUpdateStatus=Successful), and validated the webhook endpoints. Next up: replacing the Sandbox Link flow with real production access tokens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2hxqp7n7hqzzhjzmg23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2hxqp7n7hqzzhjzmg23.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>architecture</category>
      <category>python</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Day 80: Stop letting your AI hallucinate on dirty data (Fintech Edge-cases)</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Thu, 14 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-80-stop-letting-your-ai-hallucinate-on-dirty-data-fintech-edge-cases-3i23</link>
      <guid>https://forem.com/ericrodriguez10/day-80-stop-letting-your-ai-hallucinate-on-dirty-data-fintech-edge-cases-3i23</guid>
      <description>&lt;p&gt;When you build AI-powered applications, the most important code you write has nothing to do with AI. It has to do with data sanitization.&lt;/p&gt;

&lt;p&gt;Today, my Serverless Financial Agent was aggressively lecturing me for spending 500€. The catch? I didn't spend it. I transferred it between my own multi-currency accounts using Wise. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs09cioko79xfsity7yyi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs09cioko79xfsity7yyi.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Problem:&lt;br&gt;
My backend was blindly feeding raw transaction arrays to Amazon Bedrock. The LLM saw a &lt;code&gt;-500€&lt;/code&gt; transaction and did exactly what I prompted it to do: it roasted me for bad financial habits. If your context window is fed garbage, the output will be garbage.&lt;/p&gt;

&lt;p&gt;The Fix:&lt;br&gt;
I had to build a deterministic sanitization layer before the LLM ever sees the data. I wrote a strict filter in the frontend to handle business logic mapping.&lt;/p&gt;

&lt;p&gt;const INTERNAL_TRANSFER_RE = /^to (eur|usd|gbp|chf|pln|ron|huf|czk|sek|nok|dkk)\b/;&lt;/p&gt;

&lt;p&gt;const isInternalTransfer = (tx) =&amp;gt; {&lt;br&gt;
  const desc = String(tx.description || '').trim().toLowerCase();&lt;br&gt;
  return tx.is_internal === true || desc === 'balance' || INTERNAL_TRANSFER_RE.test(desc);&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;By explicitly flagging internal transfers and edge cases like intrst (Interest Payments), I prevented them from being grouped into the totalExpenses array.&lt;/p&gt;

&lt;p&gt;Alongside this, I shipped two new UI components: Velocity Analysis and a Savings Streak calculator. Now, the React app mathematically proves to the user exactly why they are hitting their daily goal, comparing their dailyIncome against their todayExpenses.&lt;/p&gt;

&lt;p&gt;The Golden Rule:&lt;br&gt;
Do not use GenAI for deterministic math or data cleaning. Clean the data with code, calculate the limits with math, and use the LLM solely to explain the clean results to the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsu7vrqzly30ya7prqen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsu7vrqzly30ya7prqen.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>python</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Day 80: When your Frontend and Backend disagree on math (Fixing Data Sync in Fintech)</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Wed, 13 May 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-80-when-your-frontend-and-backend-disagree-on-math-fixing-data-sync-in-fintech-4go3</link>
      <guid>https://forem.com/ericrodriguez10/day-80-when-your-frontend-and-backend-disagree-on-math-fixing-data-sync-in-fintech-4go3</guid>
      <description>&lt;p&gt;When building full-stack financial apps, your standard math logic needs serious guardrails.&lt;/p&gt;

&lt;p&gt;Today (Day 80 of my build!), I was fine-tuning the Month-End Projection widget for my AI Financial Agent.&lt;/p&gt;

&lt;p&gt;The Problem: My React dashboard looked great, showing €86 in expenses. But my Serverless Python backend was returning a projection of €168. To make matters worse, if I clicked on "April" (a past month), the system would project a completely broken number instead of just showing the final closed amount.&lt;/p&gt;

&lt;p&gt;The Fix: I had to implement three architectural changes to force my backend to respect the passage of time and match the frontend's business logic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Aligning Business Rules (The Mirror Filter)
My React code was hiding internal currency conversions (like Wise "To EUR" transfers) from the expense chart. But my Python Lambda was just summing up everything that wasn't an income.
Fix: I added an aggressive regex-style filter in the backend to match the UI perfectly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Python&lt;/p&gt;

&lt;h1&gt;
  
  
  Aggressive Filter for Wise and Plaid
&lt;/h1&gt;

&lt;p&gt;is_internal = (&lt;br&gt;
    t.get('is_internal', False) or &lt;br&gt;
    any(x in desc for x in ['to eur', 'to usd', 'to gbp', 'balance', 'conversion'])&lt;br&gt;
)&lt;br&gt;
if not is_income_tx(desc, amt) and not is_internal:&lt;br&gt;
    target_month_expenses += abs(amt)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The "Smart Sync" Pattern&lt;br&gt;
I have a 7 AM Cron Job that fetches new bank data daily. But what if the month rolls over to May 1st at 2 AM, and the user logs in before the Cron runs? They would see an empty dashboard.&lt;br&gt;
Fix: A lightweight fallback. On load, the Lambda checks the server's absolute clock. If the DynamoDB query for the current month (2026-05) returns empty, it forces a real-time fetch to the bank APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Temporal Math&lt;br&gt;
You can't calculate a "daily burn rate" projection for a month that is already over.&lt;br&gt;
Fix: The backend now checks the target month against the current system month. If it's a past month, projected_spend = total_expenses. Period.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Code for the happy path, but architect for the edge cases!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspf9vthpo3h9j2pa9qhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspf9vthpo3h9j2pa9qhl.png" alt=" " width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>serverless</category>
      <category>python</category>
    </item>
  </channel>
</rss>
