<?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: Abhishek.M. Shivanagoudar</title>
    <description>The latest articles on DEV Community by Abhishek.M. Shivanagoudar (@abhishekm_shivanagoudar).</description>
    <link>https://dev.to/abhishekm_shivanagoudar</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%2F4154361%2F263c8675-2b9a-4a2e-a63f-8192020537b3.jpg</url>
      <title>DEV Community: Abhishek.M. Shivanagoudar</title>
      <link>https://dev.to/abhishekm_shivanagoudar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhishekm_shivanagoudar"/>
    <language>en</language>
    <item>
      <title>"My Mergetober story: building a BambooHR connector for cognee"</title>
      <dc:creator>Abhishek.M. Shivanagoudar</dc:creator>
      <pubDate>Wed, 07 Oct 2026 22:27:58 +0000</pubDate>
      <link>https://dev.to/abhishekm_shivanagoudar/my-mergetober-story-building-a-bamboohr-connector-for-cognee-4d1n</link>
      <guid>https://dev.to/abhishekm_shivanagoudar/my-mergetober-story-building-a-bamboohr-connector-for-cognee-4d1n</guid>
      <description>&lt;p&gt;This Mergetober I wanted to do more than fix a typo. I wanted to build something real, get it reviewed, and actually understand every line of it. I ended up building a &lt;strong&gt;BambooHR connector for cognee&lt;/strong&gt;, and this post is the story of how it went, mistakes included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the issue
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/topoteretes/cognee" rel="noopener noreferrer"&gt;cognee&lt;/a&gt; is an open source project that turns your data into a knowledge graph AI agents can search, basically long-term memory for agents. It uses "connectors" to pull data in from tools like Notion, Gmail and Google Drive.&lt;/p&gt;

&lt;p&gt;While browsing the issues I found &lt;a href="https://github.com/topoteretes/cognee/issues/4795" rel="noopener noreferrer"&gt;#4795&lt;/a&gt;: add a connector for &lt;strong&gt;BambooHR&lt;/strong&gt;, an HR platform. The idea was simple: if your company's employee directory and handbooks are in BambooHR, your AI agent should be able to answer "who works in Sales?" or "how many days of leave do we get?"&lt;/p&gt;

&lt;p&gt;The issue suggested copying the existing Notion connector. Before commenting, I read through BambooHR's API docs and noticed something important: BambooHR has a &lt;strong&gt;change feed&lt;/strong&gt;, an endpoint that tells you exactly which employees were added, updated or deleted since a given time.&lt;/p&gt;

&lt;p&gt;The Notion connector re-downloads everything every run and &lt;em&gt;replaces&lt;/em&gt; the old data, because Notion has no such feed. If I copied that approach but only fetched changes, every employee who didn't change would get wiped from memory. So in my comment I proposed using the change feed with a &lt;strong&gt;merge&lt;/strong&gt; strategy instead, like the Gmail and Google Drive connectors do. I also asked what should happen to employees who leave the company.&lt;/p&gt;

&lt;p&gt;That comment got me assigned. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up (and immediately messing up)
&lt;/h2&gt;

&lt;p&gt;My first hour was mostly Git mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I forked &lt;strong&gt;cognee&lt;/strong&gt; instead of &lt;strong&gt;cognee-community&lt;/strong&gt;. The issue lives in the main repo, but connectors actually go in the community repo.&lt;/li&gt;
&lt;li&gt;I ran &lt;code&gt;git init&lt;/code&gt; in the parent folder instead of inside the cloned repo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not my proudest moment, but it's the kind of thing you only do once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a BambooHR trial account
&lt;/h2&gt;

&lt;p&gt;I didn't have access to a real company's BambooHR (and wouldn't want to test on one anyway), so I signed up for a &lt;strong&gt;free BambooHR trial&lt;/strong&gt;. It comes preloaded with around 118 fake employees and about 20 company files like handbooks and policies, which turned out to be perfect for testing.&lt;/p&gt;

&lt;p&gt;I spent a while just calling the API by hand to see what it actually returns. That's where I learned two things that shaped the whole design:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The first sync doesn't need a separate endpoint.&lt;/strong&gt; If you ask the change feed for everything since &lt;code&gt;1970-01-01&lt;/code&gt;, every employee comes back as "Inserted". One code path handles both the first sync and every sync after it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BambooHR returns only the employee id unless you ask for specific fields.&lt;/strong&gt; That meant I could make the connector private by default: it only ever requests safe fields like name, job title and department. Salary, SSN, date of birth and home address are never even fetched.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building it
&lt;/h2&gt;

&lt;p&gt;The connector syncs two things: the &lt;strong&gt;employee directory&lt;/strong&gt; and &lt;strong&gt;company files&lt;/strong&gt; (PDF, TXT, MD, CSV).&lt;/p&gt;

&lt;p&gt;For employees, it uses the change feed and stores BambooHR's own timestamp as a cursor for the next run. That cursor is only saved once the whole run succeeds, so a crash halfway doesn't skip anything. Deleted employees are removed, and terminated employees (which BambooHR marks as &lt;code&gt;Inactive&lt;/code&gt;) are forgotten by default too.&lt;/p&gt;

&lt;p&gt;Files were trickier because BambooHR has no change feed for them. So each run lists all files, compares them with the previous run to spot deletions, and lets cognee skip files whose content hasn't changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with Ollama on Windows
&lt;/h2&gt;

&lt;p&gt;I didn't want to pay for API calls while experimenting, so I ran everything locally with &lt;strong&gt;Ollama&lt;/strong&gt;, using &lt;code&gt;llama3.1:8b&lt;/code&gt; as the LLM and &lt;code&gt;nomic-embed-text&lt;/code&gt; for embeddings. This is where most of my debugging time went.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama models "downloaded" but didn't exist.&lt;/strong&gt; &lt;code&gt;ollama pull&lt;/code&gt; said success, yet &lt;code&gt;ollama list&lt;/code&gt; was empty. It turned out Ollama 0.40.0 stores model manifests as symlinks, and Windows 11 refused to follow them. Replacing the two symlinks with real copies of the files fixed it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows' 260-character path limit.&lt;/strong&gt; cognee's vector store creates deeply nested folders, and my long project path pushed it over the limit. Moving the data to a short folder fixed it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model returned the schema instead of the data.&lt;/strong&gt; cognee asks the LLM for JSON in a fixed shape, and the small 8B model sometimes replied with the &lt;em&gt;description&lt;/em&gt; of the shape instead. Adding this to &lt;code&gt;.env&lt;/code&gt; solved it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LLM_INSTRUCTOR_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"json_schema_mode"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it all worked, I loaded a few mock employees and a leave policy and asked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Who works in the Sales department?"&lt;/strong&gt; → it named the right person.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"How many days of paid leave do employees get?"&lt;/strong&gt; → &lt;em&gt;"every employee gets 24 days of paid leave per year."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I deleted that Sales employee upstream, synced again, and watched cognee's document count drop from 4 to 3. Seeing it forget someone was honestly the most satisfying moment of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing against the real trial
&lt;/h2&gt;

&lt;p&gt;Next I ran the connector against my BambooHR trial four times, making changes in between:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Employees fetched&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;th&gt;Stored&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 (first sync)&lt;/td&gt;
&lt;td&gt;108&lt;/td&gt;
&lt;td&gt;Deleted and inactive sample employees skipped&lt;/td&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Picked up my edits&lt;/td&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;One deleted, one terminated&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Caught an employee I terminated &lt;em&gt;while run 3 was still running&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Run 4 was a nice confirmation that saving the cursor only at the end really works: nothing slipped through.&lt;/p&gt;

&lt;p&gt;The trial also surprised me. One of the sample files was a &lt;strong&gt;Benefits CSV with per-employee data&lt;/strong&gt; in it. My field allowlist protects the employee directory, but it can't protect against a file like that. So I added a &lt;code&gt;file_categories&lt;/code&gt; option to let you choose exactly which file folders get synced. Another sample PDF turned out to be genuinely corrupt, so I made sure broken files are skipped with a warning instead of crashing the run.&lt;/p&gt;

&lt;p&gt;Alongside the live tests I wrote &lt;strong&gt;33 unit tests&lt;/strong&gt; with a mocked API, so reviewers can run them without a BambooHR account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opening the PR
&lt;/h2&gt;

&lt;p&gt;I opened &lt;a href="https://github.com/topoteretes/cognee-community/pull/282" rel="noopener noreferrer"&gt;the PR&lt;/a&gt;… and CI failed immediately. The repo requires a &lt;strong&gt;DCO sign-off&lt;/strong&gt; on every commit, which I'd never heard of. The fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;--signoff&lt;/span&gt; upstream/main
git push &lt;span class="nt"&gt;--force-with-lease&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;If your team uses BambooHR, here's all it takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;packages/connector/bamboohr &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv &lt;span class="nb"&gt;sync
export &lt;/span&gt;&lt;span class="nv"&gt;BAMBOOHR_COMPANY_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;acme   &lt;span class="c"&gt;# from https://acme.bamboohr.com&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BAMBOOHR_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...           &lt;span class="c"&gt;# BambooHR → your name → API Keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cognee_community_connector_bamboohr&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bamboohr_source&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;bamboohr_source&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;dataset_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bamboohr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;write_disposition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# important: cognee's default is replace
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;query_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Who works in the Sales department?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;query_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GRAPH_COMPLETION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;datasets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bamboohr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;remember&lt;/code&gt; again anytime and only the changes are synced. You can also pass &lt;code&gt;include_inactive=True&lt;/code&gt; to keep former employees, or &lt;code&gt;file_categories=["Company Files"]&lt;/code&gt; to limit which files are synced.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read the API before writing code.&lt;/strong&gt; One look at BambooHR's change feed changed the whole design, and it's what got me the issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A trial account beats mocks.&lt;/strong&gt; The broken PDF, the Benefits CSV and the inclusive timestamps only showed up with real-ish data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local LLMs are great for learning,&lt;/strong&gt; as long as you're ready to debug your OS along the way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is easier to design in than to bolt on.&lt;/strong&gt; Never requesting sensitive fields is safer than filtering them out later.&lt;/li&gt;
&lt;/ul&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%2F3fcjkfibv58n0t15cdy6.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%2F3fcjkfibv58n0t15cdy6.png" alt=" " width="799" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the cognee maintainers for the guidance on the issue, and to the WeMakeDevs community for Mergetober pushing me to ship something real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR: &lt;a href="https://github.com/topoteretes/cognee-community/pull/282" rel="noopener noreferrer"&gt;https://github.com/topoteretes/cognee-community/pull/282&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Issue: &lt;a href="https://github.com/topoteretes/cognee/issues/4795" rel="noopener noreferrer"&gt;https://github.com/topoteretes/cognee/issues/4795&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;cognee: &lt;a href="https://github.com/topoteretes/cognee" rel="noopener noreferrer"&gt;https://github.com/topoteretes/cognee&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Mergetober #WeMakeDevs #OpenSource
&lt;/h1&gt;

</description>
      <category>mergetober</category>
      <category>wemakedevs</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building CampusEvac | How We Won Best UI at First Commit</title>
      <dc:creator>Abhishek.M. Shivanagoudar</dc:creator>
      <pubDate>Thu, 01 Oct 2026 10:07:30 +0000</pubDate>
      <link>https://dev.to/abhishekm_shivanagoudar/building-campusevac-how-we-won-best-ui-at-first-commit-5deg</link>
      <guid>https://dev.to/abhishekm_shivanagoudar/building-campusevac-how-we-won-best-ui-at-first-commit-5deg</guid>
      <description>&lt;p&gt;&lt;em&gt;Two perspectives, one shared emergency, and the engineering behind a real-time evacuation simulation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A fire evacuation is not just a problem of finding the nearest exit. It is also a problem of understanding what is happening across a building, communicating information, and coordinating people who see the same emergency from different perspectives.&lt;/p&gt;

&lt;p&gt;We wanted to explore that problem through a browser-based simulation.&lt;/p&gt;

&lt;p&gt;CampusEvac was our attempt to turn an evacuation drill into an interactive, real-time experience. One participant navigates the building as an evacuee, while another observes the scenario as a warden. Both interact with the same simulation, but neither sees it in quite the same way.&lt;/p&gt;

&lt;p&gt;We built CampusEvac during First Commit, a WeMakeDevs hackathon, and our project was recognised in the &lt;strong&gt;Best UI track&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is a look at the problem we chose, the architecture behind the application, the design decisions that shaped the experience, and what building a real-time product taught us.&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%2F0g45l6lu5ex1hi9d3kj5.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%2F0g45l6lu5ex1hi9d3kj5.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build an Evacuation Simulator?
&lt;/h2&gt;

&lt;p&gt;Emergency preparedness involves more than documenting procedures. People need opportunities to understand their environment, practice decisions, and become familiar with how an evacuation might unfold.&lt;/p&gt;

&lt;p&gt;Physical drills serve an important purpose, but organising them requires coordination and a suitable environment. We wanted to explore how a browser-based simulation could provide another way to practise and understand evacuation scenarios.&lt;/p&gt;

&lt;p&gt;That led us to a question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if two people could participate in the same evacuation drill from completely different roles, directly in their browsers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer became CampusEvac.&lt;/p&gt;

&lt;p&gt;Instead of creating a static building map or a conventional monitoring dashboard, we designed a two-player experience with a shared environment and role-specific interfaces.&lt;/p&gt;

&lt;p&gt;The project had three central requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A navigable building environment for the evacuee.&lt;/li&gt;
&lt;li&gt;An overhead monitoring interface for the warden.&lt;/li&gt;
&lt;li&gt;Real-time communication so that both participants could interact with the same evolving simulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These requirements shaped both our interface design and our technical architecture.&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%2Fxvlwcgcsmgfcopil023r.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%2Fxvlwcgcsmgfcopil023r.png" alt=" " width="800" height="401"&gt;&lt;/a&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%2F6i65ge76ookzfpjo71ih.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%2F6i65ge76ookzfpjo71ih.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Roles, Two Different Interfaces
&lt;/h2&gt;

&lt;p&gt;One of the most important decisions was to avoid treating the evacuee and warden as two versions of the same user.&lt;/p&gt;

&lt;p&gt;They have different responsibilities, different information needs, and different ways of understanding the building.&lt;/p&gt;

&lt;h3&gt;
  
  
  The evacuee: navigating the situation
&lt;/h3&gt;

&lt;p&gt;The evacuee interacts with the environment from a first-person perspective.&lt;/p&gt;

&lt;p&gt;The interface needs to keep the building and navigation experience central, allowing the participant to understand their surroundings and make decisions without unnecessary interface clutter.&lt;/p&gt;

&lt;h3&gt;
  
  
  The warden: understanding the bigger picture
&lt;/h3&gt;

&lt;p&gt;The warden sees the building from an overhead perspective.&lt;/p&gt;

&lt;p&gt;Rather than navigating from ground level, the warden needs to understand the broader scenario and observe what is happening across the environment.&lt;/p&gt;

&lt;p&gt;That calls for a different information hierarchy, a different layout, and controls designed around monitoring rather than first-person movement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the distinction matters
&lt;/h3&gt;

&lt;p&gt;A role-based application should not simply change a heading or swap a few buttons when the user changes roles.&lt;/p&gt;

&lt;p&gt;The interface should reflect what that person needs to accomplish.&lt;/p&gt;

&lt;p&gt;For CampusEvac, we treated the two perspectives as complementary parts of the same experience. The evacuee focuses on navigating the environment; the warden focuses on observing the wider situation.&lt;/p&gt;

&lt;p&gt;This principle extends to many other applications, from collaborative tools to logistics dashboards and real-time monitoring systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real-Time Problem
&lt;/h2&gt;

&lt;p&gt;Two browser windows showing the same building do not automatically create a shared simulation.&lt;/p&gt;

&lt;p&gt;When participants interact independently, the application needs a way to communicate changes, manage connections, and keep the experience coherent.&lt;/p&gt;

&lt;p&gt;For CampusEvac, we used AWS AppSync Events for real-time communication and Amazon DynamoDB for application state.&lt;/p&gt;

&lt;p&gt;At a high level, the architecture consists of three main parts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Browser application&lt;/td&gt;
&lt;td&gt;Provides the evacuee and warden interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS AppSync Events&lt;/td&gt;
&lt;td&gt;Enables real-time communication between connected participants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon DynamoDB&lt;/td&gt;
&lt;td&gt;Stores application state persistently&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction between real-time communication and persistent state is important.&lt;/p&gt;

&lt;p&gt;A message can communicate that something has changed, but a message and a durable record of application state are not the same thing. A robust implementation needs to define which information is persisted, how updates are propagated, and what happens when a participant disconnects and returns.&lt;/p&gt;

&lt;p&gt;These questions become especially relevant in collaborative applications, where the experience depends on multiple clients interacting with shared state.&lt;/p&gt;

&lt;p&gt;Our architecture gave us a foundation for combining a browser-based simulation with AWS-managed real-time communication and persistence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why UI Was More Than Visual Polish
&lt;/h2&gt;

&lt;p&gt;The Best UI track gave us a reason to look closely at what makes an interface effective in the first place.&lt;/p&gt;

&lt;p&gt;It is tempting to equate good UI with gradients, animations, typography, and polished components. Those elements can contribute to the experience, but they do not replace clear interaction design.&lt;/p&gt;

&lt;p&gt;CampusEvac has an additional challenge: the interface needs to communicate a situation that changes over time.&lt;/p&gt;

&lt;p&gt;The evacuee needs an interface that supports navigation. The warden needs an interface that supports observation. Both need to understand that they are participating in the same simulation.&lt;/p&gt;

&lt;p&gt;That makes several design considerations particularly important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role clarity.&lt;/strong&gt; Participants should be able to understand their responsibilities from the interface they are using.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Information hierarchy.&lt;/strong&gt; The most relevant information should be easy to distinguish from secondary controls and status indicators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contextual feedback.&lt;/strong&gt; Changes in a shared environment need to be communicated clearly enough that participants can understand what has happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functional consistency.&lt;/strong&gt; The interface should remain understandable while the application is running, not merely in a static screenshot.&lt;/p&gt;

&lt;p&gt;The central idea is that UI quality depends on how effectively an interface supports its intended task.&lt;/p&gt;

&lt;p&gt;A visually polished interface is useful. A visually polished interface that also communicates the system's behaviour is more useful still.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Under Hackathon Constraints
&lt;/h2&gt;

&lt;p&gt;Hackathons require teams to make decisions quickly. There is limited time to explore a problem, choose a stack, implement the main experience, integrate services, test the application, and prepare a demonstration.&lt;/p&gt;

&lt;p&gt;For CampusEvac, we concentrated on the core interaction: two roles, two perspectives, and one shared simulation.&lt;/p&gt;

&lt;p&gt;We also incorporated AI coding agents into our development workflow.&lt;/p&gt;

&lt;p&gt;These tools helped accelerate implementation, but generating code was only one part of the process. The application still needed architectural decisions, integration work, and testing across its different components.&lt;/p&gt;

&lt;p&gt;A component can render correctly on its own while the complete application behaves incorrectly when two participants interact simultaneously.&lt;/p&gt;

&lt;p&gt;That distinction matters particularly in real-time systems. Testing an individual interface is not a substitute for testing the relationship between connected clients.&lt;/p&gt;

&lt;p&gt;Our experience reinforced a useful principle: AI-assisted development can reduce implementation effort, but it does not remove the need to understand the system being built.&lt;/p&gt;

&lt;p&gt;The quality of the result still depends on defining the problem clearly, making sound decisions, and verifying that the pieces work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Would Improve Next
&lt;/h2&gt;

&lt;p&gt;CampusEvac gave us a working foundation, but a simulation of this kind has several areas worth exploring further.&lt;/p&gt;

&lt;p&gt;Some possible directions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Larger building scenarios:&lt;/strong&gt; More detailed environments that allow for varied evacuation routes and layouts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clearer monitoring interfaces:&lt;/strong&gt; Better ways to communicate the overall state of a drill without overwhelming the warden with information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection handling:&lt;/strong&gt; More robust behaviour when a participant leaves a session and later returns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More extensive real-time testing:&lt;/strong&gt; Testing concurrent interactions, changing connection states, and the consistency of shared information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are potential improvements, not claims that every feature is already implemented.&lt;/p&gt;

&lt;p&gt;The broader challenge is to make the simulation useful beyond a successful demonstration: predictable behaviour, understandable interfaces, and a system that remains coherent when real users interact with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result: Best UI at First Commit
&lt;/h2&gt;

&lt;p&gt;CampusEvac was recognised in the Best UI track at WeMakeDevs' First Commit hackathon.&lt;/p&gt;

&lt;p&gt;For us, the project brought together several areas of engineering: interface design, browser-based interaction, real-time communication, persistent state, and AI-assisted development.&lt;/p&gt;

&lt;p&gt;The recognition was a milestone, but the process was equally valuable. It gave us an opportunity to think about how design decisions affect a product's functionality and how architectural choices influence the experience users ultimately see.&lt;/p&gt;

&lt;p&gt;I'm grateful to WeMakeDevs for the opportunity to participate, and to my teammates Afreen and Akshay for building CampusEvac with me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try CampusEvac
&lt;/h2&gt;

&lt;p&gt;The best way to understand the project is to explore the two perspectives and see how they fit together.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://main.d3skpx6qxc85hh.amplifyapp.com/" rel="noopener noreferrer"&gt;Explore CampusEvac&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="//youtube.com/watch?v=FhgTfCz0ou8&amp;amp;feature=youtu.be"&gt;Watch the walkthrough&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;wemakedevs:&lt;/strong&gt; &lt;a href="https://www.wemakedevs.org/aws/first-commit/projects" rel="noopener noreferrer"&gt;showcase&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CampusEvac started with a simple idea: make an evacuation drill interactive and shared. Building it taught us how much work goes into connecting the interface, the underlying system, and the experience of the people using it.&lt;/p&gt;

&lt;p&gt;That is what made this project worth building.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by Abhishek M. Shivanagoudar, Afreen, and Akshay for WeMakeDevs First Commit.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wemakedevs</category>
      <category>hackathon</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
