<?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: Irina Bodean</title>
    <description>The latest articles on DEV Community by Irina Bodean (@irinabodean).</description>
    <link>https://dev.to/irinabodean</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%2F4175751%2Ffb1f5c5e-3fa1-447d-8379-d51ba82b903f.png</url>
      <title>DEV Community: Irina Bodean</title>
      <link>https://dev.to/irinabodean</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/irinabodean"/>
    <language>en</language>
    <item>
      <title>How a Medical AI Agent Stays Grounded: RAG + Knowledge Graph</title>
      <dc:creator>Irina Bodean</dc:creator>
      <pubDate>Tue, 03 Nov 2026 09:11:21 +0000</pubDate>
      <link>https://dev.to/irinabodean/how-a-medical-ai-agent-stays-grounded-rag-knowledge-graph-2k64</link>
      <guid>https://dev.to/irinabodean/how-a-medical-ai-agent-stays-grounded-rag-knowledge-graph-2k64</guid>
      <description>&lt;p&gt;Originally published on the &lt;a href="https://corlab.tech/blog/built-a-medical-ai-agent-in-just-3-days" rel="noopener noreferrer"&gt;CorLab Tech blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A doctor wants to know whether two medicines on a patient's list interact, and which source says so. A general chatbot answers fluently, but it cannot show where the answer came from. In medicine, the source is the part that matters.&lt;/p&gt;

&lt;p&gt;This post walks through a medical AI agent that Mihai Dascal, CorLab's co-founder and CTO, built in three days, with input from the CorLab engineering team.&lt;/p&gt;

&lt;p&gt;The agent answers from two sources only: official medicine leaflets and a drug interaction graph. Every answer traces back to one of them. The sections below show how this RAG knowledge graph setup keeps answers grounded, where it broke, and what a three-day build does not prove.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI agent vs chatbot: why the difference matters here
&lt;/h2&gt;

&lt;p&gt;The idea came from Mihai's wife, a practicing doctor. Her complaint was simple: AI chat tools sounded confident, but their answers were not evidence-based. She wanted an assistant that answers only from verified data and says so when the data is silent.&lt;/p&gt;

&lt;p&gt;A healthcare chatbot usually follows a script or answers from what its model learned in training. An agent works differently. It has tools it can call, such as a document search or a database query, and a loop that picks the right tool for each question. It answers only from what the tool returns.&lt;/p&gt;

&lt;p&gt;That difference makes an agent mostly a data project. The work goes into preparing sources, deciding which questions each source may answer, and testing that every question reaches the right source.&lt;/p&gt;

&lt;p&gt;Mihai kept this agent deliberately narrow. It answers medication questions and interaction questions, and it takes no actions in other systems. The World Health Organization's guidance on large multi-modal models asks developers for exactly this kind of well-defined task. A narrow scope does not remove the risk of wrong answers, but it makes wrong answers easier to find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of medical knowledge, two stores
&lt;/h2&gt;

&lt;p&gt;Mihai spent day 1 on the data model. The stack came together quickly: LangChain and LangGraph for the agent logic, Qdrant as the vector store, and Neo4j as the knowledge graph. Both databases ran on an Azure virtual machine.&lt;/p&gt;

&lt;p&gt;The two stores hold different kinds of knowledge on purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leaflets go into vector search.&lt;/strong&gt; Mihai scraped the official medicine leaflets (summaries of product characteristics) from the national medicines agency's website, parsed them, and stored them as embeddings in Qdrant. Leaflet answers sit in paragraphs, and a clinician's wording rarely matches the leaflet's wording. Vector search finds text by meaning, so it bridges that gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactions go into the graph.&lt;/strong&gt; The interaction data came from an Excel file from Mihai's earlier medical research. His scripts parsed the file and loaded each interaction into Neo4j as a relationship between two drugs. An interaction is a link, not a passage. A question about two medicines becomes a lookup of that link, which returns a record or returns nothing.&lt;/p&gt;

&lt;p&gt;Keeping the stores apart has a second benefit: the source of each answer is always obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing is part of grounding
&lt;/h2&gt;

&lt;p&gt;The agent runs on two LangChain tools. &lt;em&gt;process_medication_query&lt;/em&gt; searches the leaflets, and &lt;em&gt;process_interaction_query&lt;/em&gt; queries the graph. Both tools accept full questions, so a clinician can ask the way they would ask a colleague. This is how the build registers them, as published on the CorLab blog (long strings wrapped for reading):&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%2Fmxpmnq4fly9zyefun8mj.jpg" 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%2Fmxpmnq4fly9zyefun8mj.jpg" alt="LangChain tool definitions for the medical AI agent's medication and interaction queries" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The descriptions do the routing. The agent reads them to decide which tool fits a question, so "two or more drugs" sends interaction questions to the graph. The capitalized instruction matters just as much: if the agent passed only a drug name, the workflow would lose the context it needs to find the right answer.&lt;/p&gt;

&lt;p&gt;Before adding any medical data, Mihai tested tool switching on something harmless: financial questions such as the price of Bitcoin. A Swagger API let him test the logic before any interface existed. If the agent could route a price question to the right tool, it could route a medication question too.&lt;/p&gt;

&lt;p&gt;Routing deserves that early attention. A side-effect question belongs to the leaflets, and a question that names two drugs belongs to the graph. When routing fails, a correct source answers the wrong question. That failure is as risky as a hallucination, and it looks just as convincing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Neo4j RAG: when the model writes the query
&lt;/h2&gt;

&lt;p&gt;On day 2, vector search over the leaflets worked smoothly. The graph did not.&lt;/p&gt;

&lt;p&gt;In the first version, the model wrote Cypher queries freely. Any small schema change or naming inconsistency broke the logic, and the schema was still changing. After two frustrating hours of debugging, Mihai dropped that approach and switched to LangChain's &lt;em&gt;GraphCypherQAChain&lt;/em&gt;, which turns a question into a Cypher query against the graph's actual schema.&lt;/p&gt;

&lt;p&gt;Two details matter in 2026. The chain now lives in the &lt;em&gt;langchain-neo4j&lt;/em&gt; package, and it will not run until the developer sets &lt;em&gt;allow_dangerous_requests&lt;/em&gt;. The &lt;a href="https://reference.langchain.com/python/langchain-neo4j/chains/graph_qa/cypher/GraphCypherQAChain" rel="noopener noreferrer"&gt;LangChain reference&lt;/a&gt; also recommends credentials scoped to only the permissions the chain needs. For a medical graph, that means a read-only user. A model that can write queries can also write harmful ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the medical AI agent stays grounded
&lt;/h2&gt;

&lt;p&gt;In this build, grounding is mostly a data design decision, not a prompt trick.&lt;/p&gt;

&lt;p&gt;The graph can say no. An interaction lookup returns a record or nothing, and an empty result lets the agent report that the data holds no record. The leaflets work the same way: the agent answers from retrieved passages, and when retrieval finds nothing relevant, it says so. Prompts and guardrails help, but only on top of sources that can come back empty.&lt;/p&gt;

&lt;p&gt;The evidence supports this approach, with limits. A 2025 meta-analysis of 20 studies in the Journal of the American Medical Informatics Association found that retrieval-augmented generation improved model performance on biomedical tasks, with a pooled odds ratio of 1.35.&lt;/p&gt;

&lt;p&gt;The same review cites a preliminary study in which GPT-4 Turbo extracted table data with only 16 percent accuracy. That is a real concern for leaflets full of dosing tables.&lt;/p&gt;

&lt;p&gt;The model is the easiest part to swap. Because the agent reaches its data only through tools, a newer model can replace the original without any change to the graph or the leaflets. Model names date quickly. Tool boundaries and database permissions do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 3: the interface and going live
&lt;/h2&gt;

&lt;p&gt;Day 3 gave the agent a face. Mihai started from an open-source Next.js and React project under the MIT license that already used WebSockets. He generated a Python backend to connect the WebSocket to the agent, then debugged authentication and message broadcasting by hand until they synced.&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%2Ffl87hfg175el674skuqh.jpg" 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%2Ffl87hfg175el674skuqh.jpg" alt="Start screen of CorLab's medical AI agent, labeled Medical Information Agent, listing its integrated tools for medication data, drug interactions, clinical protocols, operational procedures and PubMed search, with a chat box at the bottom" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supabase handled authentication, chat history, feedback collection, and admin tools. The code went to GitHub and then onto the Azure VM behind NGINX, with SSL from Certbot.&lt;/p&gt;

&lt;p&gt;As a final test, Mihai added two new document types: &lt;strong&gt;clinical guidelines&lt;/strong&gt; and &lt;strong&gt;operational protocols&lt;/strong&gt;. The agent parsed them and answered questions about them right away. By the end of day 3, the agent was running on the Azure VM as a live demo, answering medication questions from the leaflets and interaction questions from the graph.&lt;/p&gt;

&lt;p&gt;A single VM is fine for a prototype and little else. Production needs redundancy, separate hosting for the databases and the app, a staging environment, and monitoring that alerts someone when a database or model API stops responding. Running agents on a client's own data, and supporting them once they are live, is the core of &lt;a href="https://corlab.tech/industries/artificial-intelligence" rel="noopener noreferrer"&gt;CorLab's AI software development&lt;/a&gt; work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What three days prove, and what they do not
&lt;/h2&gt;

&lt;p&gt;Three days proved that the architecture works end to end: vector search, graph queries, tool routing, and a live interface. They did not prove clinical accuracy, security, or regulatory readiness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An evaluation set comes first.&lt;/strong&gt; So far, only Mihai has checked the prototype. Clinicians have not yet tested it against questions with agreed answers. Validation starts with clinicians writing realistic questions, including some the sources cannot answer, and agreeing on the correct response to each.&lt;/p&gt;

&lt;p&gt;Clinicians then score the agent on accuracy, on citing the right source, and on declining when it should. That last score is easy to skip, and a confident wrong answer is where harm starts. The evaluation set must run again whenever the data or the model changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data needs licensing and versioning.&lt;/strong&gt; The interaction records came from a research spreadsheet, and Mihai scraped the leaflets only once. Production needs licensed, versioned sources and a process that reloads a leaflet whenever the agency updates it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security needs more than prototype auth.&lt;/strong&gt; Patient data calls for role-based access, an audit log of every question and answer, and read-only database accounts. If the agent handles protected health information for a covered entity, HIPAA applies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulation shapes the interface.&lt;/strong&gt; The FDA reissued its Clinical Decision Support Software guidance on January 29, 2026. One of its criteria is that clinicians can independently review the basis for a recommendation.&lt;/p&gt;

&lt;p&gt;The WHO also warns about automation bias: people miss errors they would otherwise catch because software produced them. Showing the source beside every answer, and flagging any answer without one, is what makes citations work in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The hard parts of this build were never the language model. They sat where the model met a system with strict rules: a graph schema, a WebSocket protocol, and a clinician's need for a traceable answer. AI-generated code handled most of the plumbing, and it still needed debugging.&lt;/p&gt;

&lt;p&gt;For teams building a medical AI agent, the order of effort is clear. Sources that can say no come first. Tested routing comes next, then an evaluation set that clinicians trust. The prompt comes last.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Irina writes about healthcare, logistics and AI software at CorLab Tech.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>healthcare</category>
      <category>database</category>
    </item>
    <item>
      <title>Offline-First Driver Apps: Sync, Conflicts and Lost GPS</title>
      <dc:creator>Irina Bodean</dc:creator>
      <pubDate>Thu, 29 Oct 2026 20:55:40 +0000</pubDate>
      <link>https://dev.to/irinabodean/offline-first-driver-apps-sync-conflicts-and-lost-gps-1afm</link>
      <guid>https://dev.to/irinabodean/offline-first-driver-apps-sync-conflicts-and-lost-gps-1afm</guid>
      <description>&lt;p&gt;Most delivery driver app development starts in an office with good Wi-Fi. The app loads fast, every tap reaches the server, and the demo looks great. Then a driver backs into a loading dock with concrete walls, and the app starts spinning.&lt;/p&gt;

&lt;p&gt;Drivers lose signal far more often than office teams expect: underground garages, warehouses with metal roofs, rural roads between towers. If the app treats the network as always on, the driver ends up waiting, retrying, or writing delivery notes on paper.&lt;/p&gt;

&lt;p&gt;An offline-first driver app flips that assumption. The phone is where the work happens, and the server catches up when it can. This post walks through the parts that make it work: local storage, a sync queue, conflict resolution, GPS gaps and proof of delivery. The technical input comes from the CorLab engineering team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why drivers are offline more than you think
&lt;/h2&gt;

&lt;p&gt;For a driver, "offline" rarely means airplane mode. It usually means a bad connection: a request that hangs for 30 seconds and then fails. That is worse than no signal at all, because the app keeps waiting instead of moving on.&lt;/p&gt;

&lt;p&gt;Think about a typical stop. The driver walks into a building and hands over a package in a hallway with no coverage. That is exactly when the app must record the delivery, take a photo and collect a signature. If any step needs the server, the driver is stuck.&lt;/p&gt;

&lt;p&gt;So design for three states, not two: online, offline, and "technically online but useless." Use short request timeouts, and treat a slow network the same way you treat no network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build on a local-first data model
&lt;/h2&gt;

&lt;p&gt;In an offline-first mobile app, screens read from the local database, never straight from the API. The server is a sync partner, not the source of every tap.&lt;/p&gt;

&lt;p&gt;In React Native, the usual choice is SQLite, through a library like expo-sqlite or op-sqlite, or a layer on top of it such as WatermelonDB. Local-first storage gives you real queries, transactions, and data that survives an app restart. AsyncStorage is fine for settings, but not for a full day's route with 120 stops.&lt;/p&gt;

&lt;p&gt;A practical model for a driver app has three groups of tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Route data&lt;/strong&gt; pulled from the server: stops, addresses, time windows, package lists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver actions&lt;/strong&gt; created on the device: arrived, delivered, failed attempt, notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An outbox&lt;/strong&gt;: changes waiting to be sent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how it plays out. The dispatcher assigns a route at 6 a.m., and the app downloads all of it while the driver is still at the depot on good Wi-Fi. From then on, marking a stop as delivered is one local transaction: it updates the stop and adds a record to the outbox. The screen updates instantly, whenever the server hears about it.&lt;/p&gt;

&lt;p&gt;Two rules keep this model honest. First, give every record an ID generated on the device (a UUID), so it can exist before the server knows about it. Second, store a local sequence number next to each device timestamp. Phone clocks drift, and drivers cross time zones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sync queue and idempotent writes
&lt;/h2&gt;

&lt;p&gt;The outbox becomes your sync queue. A background worker takes pending items in order, sends them, and removes each one only after the server confirms it.&lt;/p&gt;

&lt;p&gt;The hard part is not sending. It is sending twice. On a weak connection, a request can reach the server and get saved, and then the response gets lost on the way back. The app sees a failure and retries. Without protection, the server now has two "delivered" events for the same stop.&lt;/p&gt;

&lt;p&gt;The fix is idempotent writes. Each queued action carries an idempotency key created when the action happened, not when it was sent. The server remembers the keys it has seen and returns the original result for any repeat.&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%2Ftw45q3gzgq22muqn4xpk.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%2Ftw45q3gzgq22muqn4xpk.png" alt=" " width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retry with backoff matters as much as the key. Picture 300 drivers coming out of the same tunnel, with every app retrying at once. Exponential backoff plus a little random jitter spreads that spike out. Also sort your errors: retry timeouts and 5xx responses, but show a 4xx validation error to the driver instead of retrying it forever.&lt;/p&gt;

&lt;p&gt;For background sync, Android has WorkManager and iOS has BGTaskScheduler. iOS makes no promises about when a background task runs, so also flush the queue when the app comes to the foreground and when connectivity returns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolving conflicts
&lt;/h2&gt;

&lt;p&gt;Offline means two people can change the same thing without seeing each other. A dispatcher reassigns stop 14 to another driver while the first driver, offline in a warehouse, marks it delivered. When the phone reconnects, which version wins?&lt;/p&gt;

&lt;p&gt;Last-write-wins is the simple answer: the newest timestamp overwrites the rest. It works for low-stakes fields like a driver's note. It is risky for delivery status, because "newest" depends on phone clocks, and the losing change silently disappears.&lt;/p&gt;

&lt;p&gt;For most logistics data, server-authoritative rules work better. The phone sends events ("driver marked stop 14 delivered at 10:42"), not final states, and the server decides what they mean. In the example above, the server sees that the stop was reassigned, but a delivery event with a photo and a signature is strong evidence. A sensible rule is that physical evidence beats office edits: record the delivery, cancel the second driver's task, and flag the conflict for the dispatcher.&lt;/p&gt;

&lt;p&gt;A few habits make conflict resolution manageable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Decide rules per field, not per record.&lt;/strong&gt; A note and a delivery status need different policies.&lt;br&gt;
&lt;strong&gt;- Send events and keep history.&lt;/strong&gt; An append-only log makes conflicts visible and easy to audit.&lt;br&gt;
&lt;strong&gt;- Tell the driver.&lt;/strong&gt; If the server rejects an action, show a clear message after the next sync, not a silent rollback.&lt;/p&gt;

&lt;p&gt;These trade-offs come up in almost every project in &lt;a href="https://corlab.tech/industries/logistics" rel="noopener noreferrer"&gt;custom logistics software development&lt;/a&gt;, because the office and the road always work from slightly different versions of the truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  GPS gaps and tracking
&lt;/h2&gt;

&lt;p&gt;Real-time shipment tracking is the part customers see, so GPS gaps get noticed quickly. And they will happen: tunnels, dense city streets, phones in a glovebox, and operating systems that stop background apps to save power.&lt;/p&gt;

&lt;p&gt;Treat collecting location and sending location as two separate jobs. Write each point to SQLite with its timestamp and accuracy, then upload in batches through the same queue. When the phone is offline, the track is still complete later, just delayed.&lt;/p&gt;

&lt;p&gt;On Android, continuous tracking requires a foreground service with a visible notification. On iOS, you need "Always" location permission and the background location mode, and the user can turn either off. Plan for the case where permission is missing.&lt;/p&gt;

&lt;p&gt;Battery drain is the complaint you will hear most. High-accuracy GPS every second will empty a phone before the shift ends. Adjust instead: frequent, accurate updates while the vehicle moves toward a stop, fewer updates on the highway, and a pause when motion detection says the vehicle is parked.&lt;/p&gt;

&lt;p&gt;Gaps need honest handling too. Say a driver spends 15 minutes in an underground loading area. Do not draw a straight line across the gap as if the truck drove it. Freeze the marker at the entrance with a "signal lost" label, and keep the customer's ETA based on the planned route. Also filter out low-accuracy points, so a reading with a 500-meter error does not make the truck jump across town.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project example: the map on the other end
&lt;/h2&gt;

&lt;p&gt;Every buffered GPS point ends up on a map in the office.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://corlab.tech/projects/fusedash-ai" rel="noopener noreferrer"&gt;Fusedash AI&lt;/a&gt;, a data visualization product built by the CorLab engineering team, works on this side of the problem. It shows data on interactive maps with styled layers and real-time data feeds, and logistics is one of its listed map use cases. Users zoom, filter and explore regions instead of reading tables.&lt;/p&gt;

&lt;p&gt;The lesson for a tracking view is simple: a map fed by real-time data must also show when the data stopped. A dispatcher gets more from a frozen marker labeled "last update 12 minutes ago" than from a smooth line drawn across a tunnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of delivery media
&lt;/h2&gt;

&lt;p&gt;Proof of delivery photos and signatures are the heaviest data in the app and often the most important. A missing photo can turn into a disputed delivery.&lt;/p&gt;

&lt;p&gt;Keep the record and the file apart. Save the photo to the app's file storage, create the delivery record in SQLite with a reference to the local path, and queue them as separate jobs. The small event syncs quickly, the image uploads when it can, and the server accepts the delivery with a "media pending" status in the meantime.&lt;/p&gt;

&lt;p&gt;Compress on the device. A 4 MB camera photo can usually shrink to a few hundred KB and still show a door number clearly. Add a file hash so the server can spot duplicate uploads, and use resumable uploads on weak connections. Never delete the local file until the server confirms it.&lt;/p&gt;

&lt;p&gt;In practice, a driver can work through 40 parcels in a business park with weak signal, and the photos upload later on Wi-Fi.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test offline on purpose
&lt;/h2&gt;

&lt;p&gt;Offline bugs never show up on office Wi-Fi, so make offline testing part of every release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turn on airplane mode halfway through a delivery flow.&lt;/li&gt;
&lt;li&gt;Throttle the network with the Android emulator settings or Network Link Conditioner on iOS. A slow profile with packet loss finds more bugs than full offline.&lt;/li&gt;
&lt;li&gt;Kill the app in the middle of a sync and restart it. The queue should resume with no duplicates.&lt;/li&gt;
&lt;li&gt;Change the device clock and time zone.&lt;/li&gt;
&lt;li&gt;Run a full 100-stop route on a real phone and check the battery at the end.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;An offline-first driver app is less about one library and more about one decision: the phone owns the work, and the server catches up. The rest follows from that: local storage, a sync queue with idempotent writes, clear conflict rules, honest GPS gaps, and media that uploads on its own schedule.&lt;/p&gt;

&lt;p&gt;Start with the data model and the queue. They are the hardest parts to add later.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Irina writes about healthcare, logistics and AI software at CorLab Tech.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>mobile</category>
      <category>programming</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Your First HIPAA App: 8 Things Developers Learn the Hard Way</title>
      <dc:creator>Irina Bodean</dc:creator>
      <pubDate>Fri, 16 Oct 2026 06:11:55 +0000</pubDate>
      <link>https://dev.to/irinabodean/your-first-hipaa-app-8-things-developers-learn-the-hard-way-2dk4</link>
      <guid>https://dev.to/irinabodean/your-first-hipaa-app-8-things-developers-learn-the-hard-way-2dk4</guid>
      <description>&lt;p&gt;Your first healthcare project usually starts with a familiar feature list: forms, a dashboard, messaging, search. You have built all of these before. Then someone says "HIPAA" on the kickoff call, and the familiar list stops feeling familiar.&lt;/p&gt;

&lt;p&gt;HIPAA is the US law that protects health information. For developers, most of it lands in two places. The Privacy Rule limits who can see and share patient data. The Security Rule sets the safeguards your system needs around electronic patient data. There is no official HIPAA certification for software. Compliance is something you build and document, not a badge you earn.&lt;/p&gt;

&lt;p&gt;It applies when your app handles health data for a healthcare provider, a health plan, or a vendor working for them. A standalone fitness tracker may fall outside it.&lt;/p&gt;

&lt;p&gt;A lot of it stays invisible until it bites you. If you are about to build a HIPAA compliant app for the first time, here are eight lessons developers tend to learn the hard way. The examples come from &lt;a href="https://corlab.tech/projects/referral-md" rel="noopener noreferrer"&gt;ReferralMD&lt;/a&gt;, a referral platform for US doctors and specialists that the CorLab engineering team worked on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;One note:&lt;/strong&gt;&lt;/em&gt; this is HIPAA for developers, not legal advice. Your compliance officer makes the final calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. PHI is more than medical records
&lt;/h2&gt;

&lt;p&gt;Protected health information (PHI) is health information that can be tied to a specific person. Once it is stored or sent electronically, it becomes ePHI. Most developers picture diagnoses and lab results. Those count, but so do many things that look harmless on their own.&lt;/p&gt;

&lt;p&gt;An appointment time is PHI when it sits next to a patient's name. So is an email address in a referral form, an IP address in a patient portal log, or a photo of a face. HIPAA's de-identification rules list 18 identifiers, from names and dates to device serial numbers. If any of them is attached to health data, treat the record as PHI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The practical lesson:&lt;/strong&gt; map your data before you design the database. Mark every field that is PHI, or could become PHI when combined with another field. That map decides which tables need extra protection, which services may touch them, and what must never leave your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. You need a BAA with every vendor that touches PHI
&lt;/h2&gt;

&lt;p&gt;A business associate agreement (BAA) is a contract that obliges a vendor to protect PHI it creates, receives, stores or transmits for you. No BAA, no PHI.&lt;/p&gt;

&lt;p&gt;Developers usually remember the database host. The surprises are elsewhere: the email service that sends appointment reminders, the SMS gateway, the logging service, the error tracker that captures request bodies, the analytics script on the patient portal, the AI API you call to summarize notes. The same goes for AI coding assistants and no-code builders if production data can pass through them.&lt;/p&gt;

&lt;p&gt;This includes your own team. If your developers can reach production data, your company needs a BAA with the client too. If they work only with synthetic data and never touch production, you may not.&lt;/p&gt;

&lt;p&gt;Also check the fine print. Many cloud providers sign a BAA, but it covers only a listed set of their services, not everything in the console. Before you add any third-party tool, ask one question: could PHI ever reach it, even by accident? If yes, the vendor signs a BAA or the data does not go there. Keep that vendor list in the repo, next to your architecture docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Logs, chat and error tools leak PHI
&lt;/h2&gt;

&lt;p&gt;Your main database is usually the best-protected place in the system. The leaks happen around it.&lt;/p&gt;

&lt;p&gt;The ReferralMD case page notes that about 60% of referrals go unseen, the gap the platform was built to close. Part of the answer is a real-time chat, so doctors and specialists can discuss a referral without phone tag. Clinicians write the way clinicians talk: names, conditions, medications, all in one message. The message store can be locked down, and PHI can still escape through request logs that capture message bodies, WebSocket debug output, or an error report that ships the full payload to a third-party tracker. You can see the chat and the rest of the workflow in the ReferralMD case study.&lt;/p&gt;

&lt;p&gt;The fix is to redact by default, at the logger, so no one has to remember it at every call site:&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%2Fpt3eqfux5cnxc77nr86a.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%2Fpt3eqfux5cnxc77nr86a.png" alt="JavaScript example: a pino logger configured to redact req.body, patient.name, patient.dob and message.text, replacing each value with [REDACTED] before it is logged." width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then apply the same rule to your error tracker and analytics. Scrub payloads before they leave your servers, and test it by searching your logs for a fake patient name.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Encryption without access control is not enough
&lt;/h2&gt;

&lt;p&gt;Encryption at rest and in transit is the first thing every team adds, and it should be. It protects you from a stolen disk or an intercepted connection. It does nothing about a user who logs in legitimately and opens records they have no reason to see.&lt;/p&gt;

&lt;p&gt;That is why the Security Rule also asks for unique user IDs, automatic logoff and strong authentication, which in practice means multi-factor login. On top of that, HIPAA's minimum necessary standard says people should get only the PHI they need for the task in front of them. In code, that means access scoped by relationship, not just by role. A specialist should see the patients referred to them, not every patient in the system.&lt;/p&gt;

&lt;p&gt;There is a real design tension here. ReferralMD turns scattered patient data into a one-page visual summary, with live links to X-rays and MRI scans, and puts everything needed for a referral decision within two clicks. Speed matters to busy clinicians. But a fast screen also has to be a narrow one: decide what appears on it, for whom, before you optimize how quickly it loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Audit trail: who saw what, and when
&lt;/h2&gt;

&lt;p&gt;Sooner or later someone will ask: who opened this patient's record last Tuesday? If your system cannot answer, you have a compliance problem.&lt;/p&gt;

&lt;p&gt;The Security Rule requires audit controls that record activity in systems holding ePHI. Audit logging is not the same as application logging. Most teams log writes and forget reads. Log both. A useful audit event has the user, the patient record (by internal ID), the action, the time, the source and the outcome.&lt;/p&gt;

&lt;p&gt;Three rules keep the trail useful. Log access events, not the PHI itself. Make the audit store append-only, so no one, including admins, can quietly edit history. And agree on retention with your compliance team early, because audit data grows fast and is painful to backfill.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Test data must be synthetic
&lt;/h2&gt;

&lt;p&gt;The fastest way to get realistic test data is to copy production into staging. It is also the fastest way to put PHI into an environment with weaker controls, more people with access and no BAA coverage for half the tools.&lt;/p&gt;

&lt;p&gt;You have two options. De-identify real data under HIPAA's rules, which is harder than deleting the name column. Or generate synthetic patients with a tool like Synthea, which produces realistic records, including in FHIR format. For most teams, synthetic is simpler and safer. Ban production copies outside production, and make that a written rule, not a habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. FHIR, HL7 and fax take longer than the app
&lt;/h2&gt;

&lt;p&gt;First-time teams estimate the app and forget the EHR integration work. The integrations usually take longer.&lt;/p&gt;

&lt;p&gt;FHIR R4 is the modern standard: a REST API with JSON resources like Patient, Practitioner and ServiceRequest. Certified EHRs in the US must offer a FHIR API, which sounds like good news. In practice, every EHR vendor has its own registration process, sandbox, app review and quirks. HL7 v2 messages are still everywhere in hospitals, and they look nothing like FHIR.&lt;/p&gt;

&lt;p&gt;Then there is the integration nobody puts in the estimate: fax. Many US practices still send referrals by fax. ReferralMD's dashboard accepts electronic referrals, including ones that started as a fax. A fax is PHI arriving as an image, so it needs secure intake, matching to the right patient and a human check when matching fails.&lt;/p&gt;

&lt;p&gt;Integration is often the biggest line item in custom healthcare software development. Scope each connection as its own workstream, with its own timeline and its own test data.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Have a breach plan before launch
&lt;/h2&gt;

&lt;p&gt;Under the Breach Notification Rule, the clock starts at discovery, and that is a bad moment to learn who to call.&lt;/p&gt;

&lt;p&gt;The deadlines are concrete. Affected individuals must be notified without unreasonable delay, and no later than 60 days after discovery. Breaches affecting 500 or more people also go to HHS within that window, and to the media when 500 or more residents of one state or jurisdiction are affected. If you build software for a healthcare provider, your contract likely sets an even shorter deadline for telling them.&lt;/p&gt;

&lt;p&gt;The developer's part of the plan is practical. Know who gets the first call. Have alerts that would actually notice unusual access. And make sure your audit trail from lesson 5 can answer the first question anyone asks: whose data was exposed? Run a one-hour tabletop exercise before launch. It costs little and shows every gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these lessons have in common
&lt;/h2&gt;

&lt;p&gt;Almost none of these lessons is about clever code. They are about decisions made early: which data counts as PHI, which vendors may touch it, who sees what, what gets logged, where test data comes from, and who answers the phone on a bad day.&lt;/p&gt;

&lt;p&gt;That is the real difference between healthcare app development and the apps you have built before. The features look the same. The constraints sit underneath them, and they are much cheaper to design in than to retrofit. Most of HIPAA compliant app development comes down to these early decisions.&lt;/p&gt;

&lt;p&gt;If you take one thing from this post, make it the data map from lesson 1. Almost every other lesson starts there.&lt;/p&gt;

&lt;p&gt;What surprised you on your first healthcare project? Share it in the comments.&lt;/p&gt;

&lt;p&gt;Thanks to the &lt;a href="https://corlab.tech/" rel="noopener noreferrer"&gt;CorLab Tech&lt;/a&gt; engineering team for the technical input behind these lessons.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
