<?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>DEV Community: Emiliano Allende</title>
    <description>The latest articles on DEV Community by Emiliano Allende (@emiliano_allende).</description>
    <link>https://dev.to/emiliano_allende</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4239535%2F39d5941a-d457-4d46-b9dc-f26301c1ed45.png</url>
      <title>DEV Community: Emiliano Allende</title>
      <link>https://dev.to/emiliano_allende</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emiliano_allende"/>
    <language>en</language>
    <item>
      <title>Cost-Effective LLM Integration: Enforcing Strict JSON Schemas and Reducing Token Costs</title>
      <dc:creator>Emiliano Allende</dc:creator>
      <pubDate>Thu, 12 Nov 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/emiliano_allende/cost-effective-llm-integration-enforcing-strict-json-schemas-and-reducing-token-costs-4fcl</link>
      <guid>https://dev.to/emiliano_allende/cost-effective-llm-integration-enforcing-strict-json-schemas-and-reducing-token-costs-4fcl</guid>
      <description>&lt;p&gt;&lt;em&gt;How to eliminate LLM parsing errors in production frontends, optimize system prompts, and keep AI generation costs around $0.05 or less per execution.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Integrating Large Language Models (LLMs) into production applications often introduces unpredictable behaviors: hallucinations, malformed text responses, and unexpected API rate limits. When an AI agent powers a user-facing interface (such as a React CRM dashboard previewing dynamic email drafts), a single broken markdown block or missing bracket can crash the entire frontend rendering pipeline.&lt;/p&gt;

&lt;p&gt;This guide explores (from personal experience) how to build a resilient, cost-effective LLM integration layer using &lt;strong&gt;OpenRouter&lt;/strong&gt;, &lt;strong&gt;Claude&lt;/strong&gt;, and structured validation patterns that guarantee reliable outputs while keeping operational costs minimal.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Core Problem: Unpredictable LLM Outputs
&lt;/h2&gt;

&lt;p&gt;Standard LLM completions return unstructured natural language or even code fragments and unknown symbols. In automated pipelines, relying on regular expressions or loose string parsing to extract JSON payloads from a model's response is fragile.&lt;/p&gt;

&lt;p&gt;To make LLM outputs safe for automated consumption, systems require two non-negotiable guarantees:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guaranteed Structural Compliance:&lt;/strong&gt; The model must output strictly parsed JSON matching a predefined schema. The architectural goal is to predefine the structured response so the client receives exactly what it wants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable Cost Control:&lt;/strong&gt; Token consumption must be tightly managed through prompt engineering and cost-effective model routing. Paradoxically, efficiency means designing systems where the AI performs the minimum required computation—achieved by preparing comprehensive templates that provide the model with everything it needs to simply populate the final payload.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Enforcing Strict JSON Outputs via System Prompts
&lt;/h2&gt;

&lt;p&gt;Instead of leaving output formatting to chance, the orchestration layer enforces strict boundaries directly in the system prompt configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Schema Definition:&lt;/strong&gt; The prompt explicitly defines the required JSON keys (such as &lt;code&gt;subject&lt;/code&gt;, &lt;code&gt;body&lt;/code&gt;, and corresponding nested fields), instructing the model to omit conversational filler, markdown code fences, and explanatory text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defensive Parsing:&lt;/strong&gt; Downstream handlers validate the incoming response against strict DTO models before passing data to the UI state. If validation fails, the pipeline safely catches the exception instead of propagating bad data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Optimizing Costs via OpenRouter and Model Selection
&lt;/h2&gt;

&lt;p&gt;Running heavy frontier models for routine generation tasks quickly inflates operational budgets. Cost efficiency is achieved through targeted optimization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategic Model Routing:&lt;/strong&gt; Utilizing cost-effective, high-intelligence models via &lt;strong&gt;OpenRouter&lt;/strong&gt; (such as routing between Claude Sonnet and Haiku variants depending on task complexity) reduces overhead drastically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized Token Budgets:&lt;/strong&gt; Tuning system prompts to be concise reduces input token bloat, successfully bringing generation costs down to &lt;strong&gt;~$0.04 - $0.06 per execution&lt;/strong&gt; while significantly reducing generation latency.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Principles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never trust raw LLM output:&lt;/strong&gt; Always enforce strict schema validation before passing AI-generated payloads to frontend components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt engineering is architecture:&lt;/strong&gt; Clear system constraints save compute cycles, reduce retries, and protect downstream systems from malformed data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart routing preserves margins:&lt;/strong&gt; Leveraging multi-model gateways like OpenRouter allows high-performance AI integration without breaking unit economics.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>frontend</category>
      <category>llm</category>
      <category>performance</category>
    </item>
    <item>
      <title>Building a Production-Ready Backend: From Modular Workflows to FastAPI, Clean Architecture, and CQRS</title>
      <dc:creator>Emiliano Allende</dc:creator>
      <pubDate>Wed, 11 Nov 2026 20:49:03 +0000</pubDate>
      <link>https://dev.to/emiliano_allende/building-a-production-ready-backend-from-modular-workflows-to-fastapi-clean-architecture-and-cqrs-3lp2</link>
      <guid>https://dev.to/emiliano_allende/building-a-production-ready-backend-from-modular-workflows-to-fastapi-clean-architecture-and-cqrs-3lp2</guid>
      <description>&lt;p&gt;&lt;em&gt;How an evolutionary architectural approach—moving from decoupled low-code workflows to a robust Python backend—ensures long-term scalability and maintainability.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As growing applications scale, combining routing logic, database queries, and business rules inside a single monolithic file or massive workflow becomes unsustainable. Scaling out requires a deliberate architectural shift: separating concerns so that systems remain maintainable, testable, and ready for future technology transitions.&lt;/p&gt;

&lt;p&gt;This article explores how to design a robust backend using &lt;strong&gt;FastAPI&lt;/strong&gt;, &lt;strong&gt;Clean Architecture&lt;/strong&gt;, and &lt;strong&gt;CQRS&lt;/strong&gt;, leveraging &lt;strong&gt;Supabase (PostgreSQL)&lt;/strong&gt; and drawing from lessons learned in modular automation design.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Evolutionary Path: Why Modularity Matters
&lt;/h2&gt;

&lt;p&gt;Before scaling into a heavy backend, systems often start as modular, decoupled blocks (such as independent automation flows handling specific tasks). This approach relies on three foundational pillars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scalability &amp;amp; Separation of Concerns:&lt;/strong&gt; Splitting functionality ensures that no single execution handler becomes a massive bottleneck consuming unnecessary resources. Each component handles a single, well-defined responsibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; When an issue arises, isolated modules make it straightforward to trace the exact source of failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frictionless Technology Transition:&lt;/strong&gt; When migrating core logic from low-code tools to a native code stack like Python (FastAPI), having a pre-existing modular structure makes translating functions clean, incremental, and highly systematic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. The Layered Architecture: Inward Dependencies
&lt;/h2&gt;

&lt;p&gt;Following the core principle of &lt;strong&gt;Clean Architecture&lt;/strong&gt;, dependencies must always point inward. The business logic remains completely agnostic of the framework (FastAPI) or the database driver (Supabase/PostgreSQL).&lt;/p&gt;

&lt;p&gt;A clean directory structure enforces this boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── api/                   # Frameworks &amp;amp; Drivers (FastAPI routers &amp;amp; middlewares)
├── application/           # Use cases / Application orchestration (CQRS Commands &amp;amp; Queries)
├── domain/                # Enterprise &amp;amp; Business Rules (Pure core, ports, value objects)
├── infrastructure/        # External adapters (Supabase, Brevo, OpenRouter integrations)
├── agents/                # Structured LLM orchestration agents
└── shared/                # Cross-cutting concerns (Result types, logging, config)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strict Data Validation with Pydantic
&lt;/h3&gt;

&lt;p&gt;To prevent silent data coercion and guarantee type safety across boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strict Frontiers:&lt;/strong&gt; API request/response schemas inherit from &lt;code&gt;BaseModel&lt;/code&gt; configured with &lt;code&gt;model_config = ConfigDict(strict=True)&lt;/code&gt; (and &lt;code&gt;extra="forbid"&lt;/code&gt; on critical payloads) to reject unexpected properties or invalid types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DTO Separation:&lt;/strong&gt; HTTP request/response models reside in &lt;code&gt;api/{module}/schemas.py&lt;/code&gt;, while &lt;strong&gt;Application DTOs&lt;/strong&gt; handle data transfer between handlers and the domain, decoupling external JSON contracts from internal logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Any Policy:&lt;/strong&gt; The codebase enforces strict typing (supported by &lt;code&gt;mypy --strict&lt;/code&gt;), eliminating untyped dictionaries in favor of explicitly bounded structures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Implementing CQRS and Decoupling the Database
&lt;/h2&gt;

&lt;p&gt;Separating read operations (&lt;strong&gt;Queries&lt;/strong&gt;) from state mutations (&lt;strong&gt;Commands&lt;/strong&gt;) keeps the application lightweight, testable, and maintainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Dependency Inversion via Domain Ports (&lt;code&gt;domain/ports&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;To prevent domain logic from coupling directly to Supabase client libraries, the system defines abstract interfaces using Python's &lt;code&gt;typing.Protocol&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientPort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>fastapi</category>
    </item>
    <item>
      <title>Building a Resilient B2B Email Marketing CRM Using n8n, LangChain, and OpenRouter</title>
      <dc:creator>Emiliano Allende</dc:creator>
      <pubDate>Wed, 11 Nov 2026 19:04:32 +0000</pubDate>
      <link>https://dev.to/emiliano_allende/building-a-resilient-b2b-email-marketing-crm-using-n8n-langchain-and-openrouter-4g73</link>
      <guid>https://dev.to/emiliano_allende/building-a-resilient-b2b-email-marketing-crm-using-n8n-langchain-and-openrouter-4g73</guid>
      <description>&lt;p&gt;&lt;em&gt;How to split AI content generation from transactional mailing to prevent spam, ensure strict JSON outputs, and keep API costs low.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When building B2B outreach automation, combining AI generation and email dispatch into a single linear workflow is a recipe for disaster. If the AI you are using hallucinates, outputs malformed text, or triggers a rate limit, the entire execution fails, or even worse, sends an incorrect email to a client.&lt;/p&gt;

&lt;p&gt;This guide focuses on how to solve this by designing a Two-Step architecture using n8n, LangChain, and OpenRouter (Claude), integrated with a clean database layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Challenge
&lt;/h2&gt;

&lt;p&gt;Standard email automation tools lack intelligent contextual personalization, while raw LLM implementations are unpredictable. For a modern B2B public sector CRM, the architecture required a system that could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract organization metadata and generate hyper-personalized email drafts using advanced LLMs.&lt;/li&gt;
&lt;li&gt;Force the LLM to output strict JSON structures so the frontend React interface can safely preview and edit the draft.&lt;/li&gt;
&lt;li&gt;Enforce strict business logic (anti-spam cooldowns) before dispatching via an email provider API.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution: a Two-Step Workflow Architecture
&lt;/h2&gt;

&lt;p&gt;Instead of a monolithic flow, the logic was split into two decoupled n8n workflows: &lt;strong&gt;MailWriter&lt;/strong&gt; and &lt;strong&gt;Mailer&lt;/strong&gt;.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F07wk0eu7ucx32ig9gj7l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F07wk0eu7ucx32ig9gj7l.png" width="799" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: The MailWriter Workflow (AI Generation)
&lt;/h2&gt;

&lt;p&gt;The first workflow receives the organization's payload from the frontend, queries contextual data, and passes it to an AI agent node.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LLM Orchestration:&lt;/strong&gt; LangChain was utilized inside n8n connected to OpenRouter running Claude, optimizing prompt engineering to act as an expert B2B copywriter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict JSON Output:&lt;/strong&gt; To prevent downstream parsing errors in the React frontend, the system prompt explicitly enforces a JSON schema containing &lt;strong&gt;subject&lt;/strong&gt; and &lt;strong&gt;body&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Efficiency:&lt;/strong&gt; By tuning system prompts and leveraging cost-effective routing via OpenRouter, leading to a significant reduction in generation costs to ~$0.04-0.06 per email.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: The Mailer Workflow (Business Logic &amp;amp; Dispatch)
&lt;/h2&gt;

&lt;p&gt;Once the user reviews and approves the draft in the CRM interface, the second workflow takes over.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Anti-Spam Date Checker:&lt;/strong&gt; Before any HTTP request is fired, a logic node checks the last contact timestamp stored in the database. If fewer than 15 days have passed since the previous outreach, the workflow safely halts, preventing over-communication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactional Dispatch:&lt;/strong&gt; If conditions are met, it triggers the Brevo API to dispatch the email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Synchronization:&lt;/strong&gt; Immediately after a successful send, it updates the database record with the new timestamp, maintaining seamless inter-system traceability.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling is key:&lt;/strong&gt; Separating generation from dispatch gives humans a chance to review AI outputs safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails save operations:&lt;/strong&gt; Implementing strict date-check logic protects client relationships from accidental spamming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modular automation:&lt;/strong&gt; Using n8n combined with structured API nodes bridges the gap between no-code speed and full-stack reliability.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>crm</category>
      <category>webdev</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
