<?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: Evan Childers</title>
    <description>The latest articles on DEV Community by Evan Childers (@epchilders).</description>
    <link>https://dev.to/epchilders</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3306900%2F98b3ad1b-34a3-4ca0-bed4-1f4e3f0411aa.jpg</url>
      <title>DEV Community: Evan Childers</title>
      <link>https://dev.to/epchilders</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/epchilders"/>
    <language>en</language>
    <item>
      <title>Building SolSistr: Features and Motivation</title>
      <dc:creator>Evan Childers</dc:creator>
      <pubDate>Sun, 13 Jul 2025 18:25:16 +0000</pubDate>
      <link>https://dev.to/epchilders/building-solsistr-features-and-motivation-4ipe</link>
      <guid>https://dev.to/epchilders/building-solsistr-features-and-motivation-4ipe</guid>
      <description>&lt;p&gt;Recently, I announced SolSistr, a platform that I have been building since October 2024 that organizes sorority recruitment data into a unified system, enabling chapters to manage and enhance their recruitment process through the power of AI. &lt;/p&gt;

&lt;p&gt;While I shared the &lt;strong&gt;business motivation and launch story on &lt;a href="https://www.linkedin.com/in/evan-childers-41325b240/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;, I wanted to write a technical reflection for those interested in the engineering side of building a SaaS from scratch.&lt;/p&gt;

&lt;p&gt;In the following series of posts, I will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tech stack overview:&lt;/strong&gt; Why I chose these tools and frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main Features:&lt;/strong&gt; Integral pieces of the puzzle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pitfalls I encountered:&lt;/strong&gt; What broke, what was harder than expected, and what I would do differently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lessons learned:&lt;/strong&gt; For anyone looking to build and ship their own SaaS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope to document the major wins and setbacks I’ve encountered during this development journey and share advice for others on a similar path.&lt;/p&gt;




&lt;h2&gt;
  
  
  An Overview of How Sorority Recruitment Works
&lt;/h2&gt;

&lt;p&gt;Sorority recruitment is a multi-day process in which chapters meet with Potential New Members (PNMs) across several rounds/days in order to narrow down a pool of people to extend bids (offers to join the sorority) to those who are determined to best align with a chapter's values and culture. Each round typically involves structured social events where members and PNMs engage in conversations to give the members a better idea on where the PNMs' values and character lie while giving the PNMs' a sense of the chapter’s culture and community.&lt;/p&gt;

&lt;p&gt;To manage these interactions, chapters use &lt;strong&gt;bump groups&lt;/strong&gt;, or small teams of members who "bump" each other in and out of conversations with PNMs during a party, which is a slot in which PNMs are assigned to talk to a certain sorority. This allows the PNM to speak with several members in a short time while ensuring their conversations are flowing naturally and don't become stale. For example, a member may talk to a PNM for a few minutes before seamlessly passing them to another member and picking up another PNM that has been passed to them, allowing the chapter gather multiple opinions on each PNM that comes through.&lt;/p&gt;

&lt;p&gt;PNMs are matched to these bump groups based in shared interest, hometown, intended major or other points of connection that they share with member's of the bump group that can help create an initial state of relatability in the conversation. Throughout recruitment, chapters track conversations by rating their interactions to whittle their pool of PNMs as they approach bid day.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Wanted to Solve
&lt;/h2&gt;

&lt;p&gt;During the 2024 sorority recruitment cycle, my girlfriend took on the role of recruitment analyst for her sorority. While I knew recruitment was a complicated and stressful process for everyone involved, I had no clue how things truly were. She described pulling data from multiple spreadsheets to cross-check PNM information, manually assigning bump groups, and consolidating notes from dozens of chapter members after each party. Even small updates like changing a PNM’s hometown required digging through scattered tabs and group chats, creating constant opportunities for errors and frustration during an already high stress week. &lt;/p&gt;

&lt;p&gt;Despite all this effort, chapter members often went into conversations with little more than basic cue cards listing a PNM’s hometown and intended major. Matching PNMs with bump groups was often based on vague similarities like “they're both from Tennessee” rather than any meaningful or structured criteria. The entire system was put together with good intentions, but, in my opinion, &lt;strong&gt;it lacked the tools needed to support thoughtful, scalable recruitment decisions.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;At the time of this post, SolSistr is still in the late stages of development so some of the features are subject to change, but the key components I wanted to focus on are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Profile Management: Allows chapters to view and rate profiles in a structured format.&lt;/li&gt;
&lt;li&gt;Member Dashboard: An organized overview of all of the PNM's a sorority member is set to speak with in a given round, including their similarity to each PNM, AI generated conversation starters and required action reminders to comment on their profile after having spoken to them.&lt;/li&gt;
&lt;li&gt;Bump Group Management: A custom built drag-and-drop component where users can organize members/PNMs into different bump groups.&lt;/li&gt;
&lt;li&gt;PNM rating form: A way for sorority members to jot down their thoughts on a PNM while it is fresh on their mind&lt;/li&gt;
&lt;li&gt;Admin Tools:  A centralized hub for sorority administrators to view PNM ratings, set each round's schedule and invite &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Profile Management
&lt;/h2&gt;

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

&lt;p&gt;This page displays the general view of a user profile, with some subtle differences between PNM and member accounts. All users can upload gallery images and list their interests, while PNMs have an additional comments section under their profiles. Sorority admins can view all comments left on a PNM’s profile, whereas chapter members can only see the comments they personally submitted. This design ensures that individual opinions remain independent, preventing overall comment sentiment from influencing a member’s perspective when rating a PNM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Member Dashboard
&lt;/h2&gt;

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

&lt;p&gt;This is the member dashboard where chapter members can view the bump group that they have been assigned to along with its PNM assignments. These assignments are separated into party views so members can organize their talking assignments by what time of the day they will occur and in which order. There is also a user required action task bar which reminds members if they have been assigned a PNM to talk to and have yet to leave a comment under their profile in the current round.&lt;/p&gt;

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

&lt;p&gt;Here is a detailed view of a PNM profile within the party assignments dropdown on the member dashboard. This component sends an API call to the Flask backend to break down the exact reasons behind the similarity score, providing a clear breakdown of interest, major, and hometown similarity contributions. Additionally, it leverages the OpenAI API (using GPT-4o) to generate personalized conversation starters and provide a natural language overview explaining the main factors contributing to the similarity score, providing chapter members flash-cards to prepare for meaningful interactions during recruitment.&lt;/p&gt;

&lt;h2&gt;
  
  
  PNM Rating Form
&lt;/h2&gt;

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

&lt;p&gt;This image shows the PNM rating form that appears in the comments section of a PNM’s profile for chapter members. This form allows members to quickly record their impressions and notes immediately after an interaction, getting rid of the need to remember details and manually enter them into a spreadsheet later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Admin Tools
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmuz83qs37uyk0w16oq4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmuz83qs37uyk0w16oq4n.png" alt="Club Configuration" width="800" height="435"&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.amazonaws.com%2Fuploads%2Farticles%2Fmp5o77xyo5xdsun6a4lo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp5o77xyo5xdsun6a4lo.png" alt="Club Configuration" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The images above show the club configuration page, a dashboard available to chapter admins for managing recruitment operations. From this page, admins can control the total number of rounds (and progress to the next round when needed), manage sorority join requests, share a club join link or generate a QR code, and access a centralized area to assign PNMs to specific parties. They can also mark PNMs as cut or active and view all current comments under each PNM’s profile in one place. This page also allows the admin to export all of their club data to an organized excel spreadsheet in order to fit into their current workflow if needed.&lt;/p&gt;

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

&lt;p&gt;Here you can see the PNM Matcher screen, which allows chapter admins to assign PNMs to specific bump groups and parties with a custom-built drag-and-drop interface. On the Club Configuration screen, Admins can specify which party each PNM will be visiting in, enabling the Auto-Assign algorithm to automatically match and balance bump group assignments across parties. If no party is specified for a PNM, that user remains available for assignment to any bump group in any party. I also created a sister page to this one that allows the user to define the bump group members in the same drag-and-drop interface.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Stay tuned for my next post regarding the tech-stack breakdown and backend logic for SolSistr. -Evan&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>Building SolSistr: Technical Review</title>
      <dc:creator>Evan Childers</dc:creator>
      <pubDate>Sun, 13 Jul 2025 18:24:50 +0000</pubDate>
      <link>https://dev.to/epchilders/building-solsistr-technical-review-4k25</link>
      <guid>https://dev.to/epchilders/building-solsistr-technical-review-4k25</guid>
      <description></description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>Building SolSistr: Pitfalls and Lessons Learned</title>
      <dc:creator>Evan Childers</dc:creator>
      <pubDate>Sun, 13 Jul 2025 18:24:45 +0000</pubDate>
      <link>https://dev.to/epchilders/building-solsistr-pitfalls-and-lessons-learned-58ml</link>
      <guid>https://dev.to/epchilders/building-solsistr-pitfalls-and-lessons-learned-58ml</guid>
      <description>&lt;p&gt;Recently, I announced SolSistr, a platform that I have been building since October 2024 that organizes sorority recruitment data into a unified system, enabling chapters to manage and enhance their recruitment process through the power of AI. &lt;/p&gt;

&lt;p&gt;While I shared the &lt;strong&gt;business motivation and launch story on &lt;a href="https://www.linkedin.com/in/evan-childers-41325b240/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;, I wanted to write a technical reflection for those interested in the engineering side of building a SaaS from scratch.&lt;/p&gt;

&lt;p&gt;In the following series of posts, I will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tech stack overview:&lt;/strong&gt; Why I chose these tools and frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main Features:&lt;/strong&gt; Integral pieces of the puzzle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pitfalls I encountered:&lt;/strong&gt; What broke, what was harder than expected, and what I would do differently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lessons learned:&lt;/strong&gt; For anyone looking to build and ship their own SaaS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope to document the major wins and setbacks I’ve encountered during this development journey and share advice for others on a similar path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pitfalls
&lt;/h2&gt;

&lt;p&gt;Although I’d like to say this was a cakewalk, I encountered several challenges and unexpected hurdles that tested both the system’s design and my determination to see it through.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hometown CSV loading/caching trouble
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it was&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a part of the user's profile customization, I added a feature that allowed for the selection of a user's hometown based on a preloaded csv of &lt;a href="https://github.com/kelvins/US-Cities-Database/blob/main/csv/us_cities.csv" rel="noopener noreferrer"&gt;US Cities&lt;/a&gt;. While having such a comprehensive list available on the client seemed convenient, fetching a 36,000-line CSV on every page load proved to be extremely taxing. Page load times stretched into the double digits in seconds, and I even reached the monthly rate limit on the free subscription of Neon Console in a matter of days, making it clear that a more efficient solution was necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first thought was to create a client context to store the list after the initial page load and cache it in local storage. This would allow the app to fetch the CSV only once and reuse the data across sessions, reducing unnecessary downloads and improving load times. While this seemed like a solid solution in theory, it didn’t play well with my development workflow. During testing, I was frequently deleting my profile, signing out, and going through the entire login flow repeatedly. Since I was using NextAuth.js, this process would require me to clear my cache each time I logged out, which meant the cached US Cities CSV was wiped before the expiry I had set. As a result, I started running into errors on reload because the app expected the list to be present when it wasn’t.&lt;/p&gt;

&lt;p&gt;After some research, I decided to dynamically query the US Cities table only when needed. Instead of storing the full CSV, user profiles now store the ID of their selected hometown. Whenever a page needs to display multiple user profiles, it performs a bulk fetch for only the required hometowns on page load, rather than loading the entire list.&lt;/p&gt;

&lt;p&gt;Additionally, for selecting a hometown, I built an autocomplete component that filters hometowns based on what the user types. To mitigate lag, I implemented a function that waits until the user has entered at least three characters before dynamically fetching a filtered list of matching cities from the database. This approach significantly reduced load times while still providing a seamless user experience during hometown selection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fetchCitiesGet = async (searchTerm: string) =&amp;gt; {

    if (searchTerm.length &amp;lt; 2) {
      setCities([]);
      return;
    }

    setLoading(true);
    try {
      const response = await fetch(`/api/get-cities?q=${encodeURIComponent(searchTerm)}`);

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const contentType = response.headers.get('content-type');
      if (!contentType || !contentType.includes('application/json')) {
        throw new Error(`Expected JSON but got ${contentType}`);
      }

      const fetchedCities = await response.json();
      setCities(fetchedCities);
    } catch (error) {
      console.error('Error fetching cities:', error);
      setCities([]);
    } finally {
      setLoading(false);
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Chat component (scope creep)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it was&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.amazonaws.com%2Fuploads%2Farticles%2F05kso0xevdd7hi59gkmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05kso0xevdd7hi59gkmn.png" alt="Chat app" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially, I envisioned the need for a Chat feature within the app to allow for seamless communication all in one place.  Despite this sounding valuable on paper, I found myself more interested in the technical aspects of its integration than in the necessity of the component itself. I went as far as designing a UI template and researching websocket integration to enable real-time messaging between chapter members and admins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this feature might still be an option/wanted in the future, I realized that adding a fully functional chat system at my current skill level would expand the project's scope without directly advancing its core purpose. Since the beginning of this project, my dad (goated SWE) kept telling me &lt;strong&gt;“You need to focus on the nuts and bolts—what are the bare necessities this thing needs to work?”&lt;/strong&gt;. Building a system like this would require message persistence, notification toasts, delivery guarantees—a ton of additional infrastructure that would divide my attention from the core goals of the project, especially while taking classes at the same time. It became clear that adding a full chat system wasn’t just a feature, but it was practically a separate product, and taking it on would risk delaying the parts of the project that mattered most.&lt;/p&gt;

&lt;h3&gt;
  
  
  University selection / university domain search
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it was&lt;/strong&gt;&lt;br&gt;
Initially, I handled university selection very similarly to the way I handled US City selection. I had found a US University CSV online and I loaded each of them into a searchable auto complete component in which the user could modify on their profile. While this CSV was a lot smaller than the list of US Cities, there was still one major concern with this process: How often do users change which university they go to? &lt;/p&gt;

&lt;p&gt;More importantly, a fundamental concern for the app as a whole was verifying that users are actually college students. We needed to prevent random individuals from creating accounts and interfering with the recruitment process at schools they had no intention of attending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;br&gt;
My solution to this was grafting another dataset, a list of &lt;a href="https://github.com/endSly/world-universities-csv" rel="noopener noreferrer"&gt;World Universities CSV&lt;/a&gt;, and filter it down to universities located in the US. I then attached each university’s list of email domains to my dataset, allowing the system to verify a user’s enrollment by checking that their provided email matched a valid university domain. Additionally, this verifies that no other existing accounts have been verified with the same university email, ensuring that each account is attached to one and only one university email. I ended up choosing &lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;, an email-sending API that allowed me to send verification links with an attached activation token to the user’s school email. Once the user clicked the link, their account would be verified and activated automatically.&lt;/p&gt;
&lt;h3&gt;
  
  
  Open AI Prompting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it was&lt;/strong&gt;&lt;br&gt;
In the member dashboard, I knew I wanted to provide AI-generated suggested conversation starters to help members prepare for interactions with PNMs. However, I quickly discovered I had no idea what kinds of roadblocks I would encounter when trying to get the AI to output exactly what I needed. There were a couple of funny outputs I ran into during testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;br&gt;
To begin, I aimed to prioritize conversation starters that highlighted strong similarities between the two users’ profiles. However, this approach sometimes caused the AI to tie together unrelated topics in ways that didn’t make sense, often resulting in nonsensical or awkward language. For example, when comparing User A and User B where User A had a strong interest in weightlifting and User B had a strong interest in hiking, it output:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
Hey [User B Name], I see you are into hiking and I am into weightlifting, maybe one day we can meet up and hike a mountain and do a dumbbell workout at the top!!&lt;br&gt;
&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;While it could technically be argued that the similarity here is “physical activity,” this is an oddly specific and unrealistic suggestion that would likely lead to an awkward conversation rather than a natural icebreaker.&lt;/p&gt;

&lt;p&gt;Another issue that I kept running into was the use of language that seemed unnatural to use in casual conversation, outputs such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
[User B Name], I see you are into gaming, have any spectacularly epic wins recently?&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
and&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
I see you are into cooking and watching movies, what is your favorite movie-inspired cuisine that you have made so far?&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;While these results are kind of funny, they use phrasing that feels overly formal or exaggerated, with words like “spectacularly epic” or oddly specific phrases like “movie-inspired cuisine” that most people wouldn’t naturally say. This type of language risks making conversations feel forced or awkward, which is the opposite of what I wanted for the members using the app.&lt;/p&gt;

&lt;p&gt;Through my tests, I found a set of prompt parameters that consistently led to clearer, more natural conversation starters while avoiding awkward phrasing and irrelevant topic matching. Some of the prompt parameters that helped were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- **Do not force fake connections** — if there’s no match on a trait, don’t pretend there is.
- **Instead of trying to come up with  a conversation starter relating the two profiles**, focus on {UserB}'s profile and individual interests, and how you can start a conversation with them.
- **Avoid niche references** (e.g., obscure pop culture, overly technical topics).

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Word2Vec Model and Deployment Concerns
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it was&lt;/strong&gt;&lt;br&gt;
Originally during development, I was generating the user's profile vectors through a locally downloaded Word2Vec model that I loaded at project runtime. Despite the convenience of have a free and reliable text embeddings model downloaded locally and loaded with the gensim library, the size of the model became a concern once I started going through the motions of deploying my Flask app. When downloading all dependencies during deployment, the build would often fail because the app would try to start before the 3GB Word2Vec model was fully loaded into memory. This created a race condition where health checks would timeout, causing the build to terminate the process prematurely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;br&gt;
Initially, I thought to implement a lazy load feature for the model ensure the build atleast succeeds and the application could start without immediate dependency on the Word2Vec model. This approach succeeded with passing the build health checks but often times triggering the lazy load function would cause a loading time of upwards of 60 seconds as the user waited for entire model to be loaded into memory. This performance bottleneck led me to explore external API options for generating profile vectors, ultimately discovering OpenAI's text embedding model. This solution offered two key advantages over the local Word2Vec implementation. First, OpenAI's embedding model is approximately five times larger than the standard Word2Vec model, resulting in more accurate vector representations. Second, by passing off the embedding generation to an external service, I got rid of the loading process entirely, dramatically improving response times for users. My primary concern with this approach was the potential cost of using OpenAI's API. However, after testing with sample data, I found that generating 30,000 profile vectors cost approximately $0.02, making the expense negligible compared to the substantial improvement in user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding Remarks
&lt;/h2&gt;

&lt;p&gt;Overall, I am extremely proud of what I’ve been able to build over the past year. After tens of thousands of lines of code and likely thousands of hours of work, I now have something that’s not only close to being fully usable but also capable of solving real problems that people I know genuinely struggle with. Throughout this process, I’ve seen a tremendous improvement in my ability to plan sprints, assess complex problems, and develop code-driven solutions for abstract challenges. Beyond just writing code, this project has taught me how to iterate efficiently and balance building features with maintaining a clear focus on the core goals of the product and business. I’m excited to see how this project continues to grow and to carry these skills into whatever I build next.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;br&gt;
-Evan&lt;/p&gt;

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