{"slug": "if-else-or-boundaries", "title": "If-Else or Boundaries?", "summary": "BotSailor's developer advocates a layered architecture for chatbot logic, separating AI-driven language interpretation from deterministic business rules and backend data. The approach warns against both excessive if-else chains and over-reliance on LLMs for deterministic decisions, citing OWASP's guidance on excessive agency. The recommended model uses AI for parsing user input, structured validation, backend APIs for truth, and deterministic conditions for predictable paths, with human fallback for edge cases.", "body_md": "*Series: From Visual Flows to AI-Orchestrated Automation · Part 03*\n\nA chatbot rarely becomes difficult because of its first `if`\n\nstatement.\n\nThe problem starts later.\n\nA customer wants to track an order. Another asks about a refund. Someone enters an invalid order ID. The API times out. Another user phrases the same question in a completely different way. Someone else just wants to talk to a human.\n\nSoon, what started as a simple decision tree is handling language, validation, business rules, external data, exceptions, and customer experience at the same time.\n\nIn the previous part of this series, **“Where Developer Hours Disappear in Messaging Automation,”** I looked at why repetitive conversation logic can consume more engineering time than expected.\n\nWorking around messaging automation at BotSailor has made the next question equally interesting to me:\n\n**Where should chatbot logic actually live?**\n\nThe answer is not necessarily more conditions.\n\nAnd it is not “let AI handle everything” either.\n\nThe first approach is familiar:\n\n```\nif (intent === \"track_order\") {\n  // track the order\n} else if (intent === \"refund\") {\n  // handle refund\n} else if (intent === \"support\") {\n  // contact support\n} else {\n  // fallback\n}\n```\n\nNothing is inherently wrong with this.\n\nThe problem appears when one decision tree gradually becomes responsible for too many things: identifying intent, validating data, calling APIs, enforcing business rules, handling failures, and choosing what the customer sees next.\n\nThe opposite approach sounds more modern: send everything to an LLM and let the model decide what happens.\n\nThat solves some language problems, but introduces another architectural problem.\n\nA probabilistic language model should not automatically become the source of truth for an order status, payment state, refund eligibility, or another deterministic business decision.\n\n[OWASP's guidance on Excessive Agency](https://genai.owasp.org/) highlights a related risk: giving LLM systems too much functionality, permission, or autonomy can allow unexpected model behavior to produce unintended actions.\n\nInstead of choosing between **if-else everywhere** and **AI everywhere**, I think the better question is:\n\nWhat responsibility belongs to each layer?\n\nFor many conversational workflows, this model is more useful:\n\n```\nUser language\n     ↓\nStructured input\n     ↓\nValidation\n     ↓\nBackend/API truth\n     ↓\nDeterministic decision\n     ↓\nConversation response\n     ↓\nHuman fallback\n```\n\nEach layer has a different job.\n\nAI can interpret uncertain language.\n\nStructured input can collect the exact values the system needs.\n\nThe backend can remain responsible for business truth.\n\nDeterministic conditions can control predictable conversation paths.\n\nHumans can handle cases that still require judgment.\n\nThat separation matters more than whether the orchestration is written in JavaScript or represented visually.\n\nSuppose a customer enters:\n\n```\nBS-1042\n```\n\nThe chatbot should not ask an AI model:\n\n“Do you think this order has shipped?”\n\nIt should ask the system that actually owns the order data.\n\nA simplified JavaScript request might look like this:\n\n``` js\nasync function getOrderStatus(orderId) {\n  const response = await fetch(\n    `https://api.example.com/orders/${orderId}`,\n    {\n      headers: {\n        Authorization: `Bearer ${process.env.API_TOKEN}`\n      }\n    }\n  );\n\n  if (!response.ok) {\n    throw new Error(\"Order lookup failed\");\n  }\n\n  return response.json();\n}\n```\n\nThe service might return:\n\n```\n{\n  \"order_id\": \"BS-1042\",\n  \"status\": \"shipped\",\n  \"tracking_url\": \"https://example.com/track/BS-1042\"\n}\n```\n\nNow we have something more useful than an AI guess: **structured information from the system of record.**\n\nThe next decision can remain deterministic:\n\n```\nswitch (order.status) {\n  case \"shipped\":\n    return \"show_tracking\";\n\n  case \"pending\":\n    return \"show_pending_message\";\n\n  default:\n    return \"human_handoff\";\n}\n```\n\nThis is where visual automation becomes interesting.\n\nIn BotSailor, the same pattern can be represented using a **User Input Flow**, an **HTTP API** call, response mapping into custom fields, a **Condition**, and a human fallback.\n\nConceptually:\n\n```\nTrack Order\n     ↓\nCollect order_id\n     ↓\nHTTP API\n     ↓\nMap order_status\n     ↓\nCondition\n   /    |     \\\n  /     |      \\\nShipped Pending Unknown\n   ↓      ↓       ↓\nTracking Waiting Human\nmessage  message  handoff\n```\n\nThe API still owns the truth.\n\nThe visual workflow owns the conversation around that truth.\n\nA Condition can route the conversation based on the mapped value, while User Input Flow can collect and validate customer information before the API request.\n\nAI Reply can then be used where language is genuinely uncertain instead of asking AI to replace deterministic business rules.\n\nThis is not really a competition between code and no-code.\n\nIt is an **ownership decision**.\n\nConsider this rule:\n\n```\nif (order.status === \"shipped\") {\n  sendTrackingLink();\n}\n```\n\nShould it stay in backend code?\n\nSometimes, absolutely.\n\nIf a rule controls pricing, refunds, fraud detection, permissions, financial transactions, or audited policy, I would rather have the application enforce it.\n\nBut a presentation decision such as:\n\n```\nstatus = shipped\n      ↓\nshow tracking message\n```\n\ncan reasonably live in the conversation orchestration layer, particularly when the underlying `status`\n\nstill comes from an authoritative backend.\n\nA useful boundary might look like this:\n\n| Responsibility | Better Home |\n|---|---|\n| Interpret fuzzy language | AI |\n| Validate an order ID | Structured input |\n| Retrieve order status | API/backend |\n| Choose conversation path | Deterministic workflow |\n| Approve a refund | Business logic/backend |\n| Handle unusual exception | Human |\n\nThe important question is not:\n\n**“Can this be moved into a visual builder?”**\n\nIt is:\n\n**“Should the conversation layer own this decision?”**\n\nNow suppose the order API returns:\n\n```\n503 Service Unavailable\n```\n\nThe workflow should not silently ask AI to generate a plausible order status.\n\nIt should fail predictably:\n\n```\nAPI failure\n    ↓\nNo verified order status\n    ↓\nExplain temporary problem\n    ↓\nRetry or human handoff\n```\n\nBefore calling this workflow production-ready, I would test at least:\n\n```\n✓ Valid order ID\n✓ Invalid order ID\n✓ Order not found\n✓ API timeout\n✓ Malformed response\n✓ Unexpected status\n✓ Human handoff\n```\n\nThe happy path proves that the demo works.\n\n**Failure paths tell you whether the architecture works.**\n\nMoving beyond if-else does not mean removing deterministic logic.\n\nIt means stopping one layer from becoming responsible for everything.\n\nThe mental model I keep coming back to is:\n\nUse AI to interpret language, APIs to provide truth, deterministic logic to control predictable paths, and humans to handle exceptions.\n\nOnce those boundaries are clear, a chatbot becomes easier to reason about whether its orchestration lives in JavaScript, BotSailor, or another workflow system.\n\nAnd that creates the next engineering problem in this series.\n\n_If APIs are going to provide the truth, **how do we make the API layer reliable enough for a live conversation?**\n\nThat takes us into authentication, payload mapping, response handling, and reusable HTTP API wrappers._\n\n**Where would you draw the boundary in your own stack? What stays in backend code, and what would you allow the conversation workflow to decide?**\n\n**AI Assistance Disclosure:** AI was used to support the structure and editing of this article and to help prepare illustrative code examples. The ideas and practical perspective are informed by the author's work around BotSailor and messaging automation. The final article and examples were reviewed before publication.", "url": "https://wpnews.pro/news/if-else-or-boundaries", "canonical_source": "https://dev.to/siddharthaghosh/if-else-or-boundaries-3ojf", "published_at": "2026-08-20 10:50:58+00:00", "updated_at": "2026-08-20 11:15:52.464537+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "ai-agents", "ai-safety", "developer-tools"], "entities": ["BotSailor", "OWASP"], "alternates": {"html": "https://wpnews.pro/news/if-else-or-boundaries", "markdown": "https://wpnews.pro/news/if-else-or-boundaries.md", "text": "https://wpnews.pro/news/if-else-or-boundaries.txt", "jsonld": "https://wpnews.pro/news/if-else-or-boundaries.jsonld"}}