<?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>Forem: Will Cheung</title>
    <description>The latest articles on Forem by Will Cheung (@willcheung).</description>
    <link>https://forem.com/willcheung</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%2F364607%2F9649ed80-cae9-4ce2-9685-a23679cbbe15.png</url>
      <title>Forem: Will Cheung</title>
      <link>https://forem.com/willcheung</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/willcheung"/>
    <language>en</language>
    <item>
      <title>I Built and Deployed a Micro-SaaS in 3 Hours — Here’s What I Learned</title>
      <dc:creator>Will Cheung</dc:creator>
      <pubDate>Sun, 31 Aug 2025 20:33:55 +0000</pubDate>
      <link>https://forem.com/willcheung/i-built-and-deployed-a-micro-saas-in-3-hours-heres-what-i-learned-cd6</link>
      <guid>https://forem.com/willcheung/i-built-and-deployed-a-micro-saas-in-3-hours-heres-what-i-learned-cd6</guid>
      <description>&lt;p&gt;I’m not a software engineer, but I’m a relatively technical tinker. In earlier posts, I built apps using Replit, Firebase Studio, and Gemini Canvas. Gemini Canvas is pure magic for spinning up an idea in 30 seconds with zero code, but it doesn’t do well for more complex apps that need a database. Firebase Studio is great if you want a powerful, scalable setup — but it can feel a bit unwieldy for a hobbyist like me (I’ll do a deeper dive on that later).&lt;/p&gt;

&lt;p&gt;That’s where Replit shines. It’s a sort of Swiss Army knife — a perfect middle ground for quickly going from idea to deployed app. In this article, I’ll go deeper into the pros and cons of going production-ready on Replit.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note #1: This isn’t a step-by-step guide. Other articles do that much better — this is more about my experience using Replit to ship something real.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note#2: I’m using the paid version of Replit, which gives me a better model with better prompt reasoning and code generation.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why going to production requires more than just code
&lt;/h2&gt;

&lt;p&gt;If you’re new to all this, here are a few basics that separate a toy app from one that’s production-ready:&lt;/p&gt;

&lt;p&gt;✅ Database — Storing user data and app data.&lt;br&gt;
✅ Custom domain — Giving your app a real branded URL.&lt;br&gt;
✅ Hosting — Moving from “local preview” to “actually live on the internet.”&lt;br&gt;
✅ Deployment — Transitioning from sandbox to a real, stable environment.&lt;br&gt;
✅ Monitoring — Checking errors and usage after going live.&lt;/p&gt;

&lt;p&gt;This typically requires 5+ tools. And the cool thing about Replit? It takes care of all of this out of the box.&lt;/p&gt;

&lt;p&gt;People often talk about no-code tools making it easy to ship SaaS apps without writing a single line of code — that’s mostly true. Going to production, for example, is another story.&lt;/p&gt;

&lt;p&gt;Even though Replit Agent wrote most of the code, I still had to code review and occasionally debug. But the speed production code is generated is mind-blowing. That’s what allowed me to build something meaningful in hours instead of days or weeks.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;Let me introduce &lt;a href="https://calautobot.com/" rel="noopener noreferrer"&gt;Calendar Autobot&lt;/a&gt;, your AI assistant to transform any emails and images into calendar events. You can literally forward an email with a flight itinerary or kids camp schedule, and the bot will add the event to your Google Calendar. A few caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I might add a SMS feature, making it easier to send images&lt;/li&gt;
&lt;li&gt;Adding email support was extra work that took me beyond the 3-hour goal.&lt;/li&gt;
&lt;li&gt;Vibe coded &lt;a href="https://chromewebstore.google.com/detail/calendar-autobot-ai/lmempdbkammjlmomcgjglolnmebeedok" rel="noopener noreferrer"&gt;Chrome Extension&lt;/a&gt; as well&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.amazonaws.com%2Fuploads%2Farticles%2Ftm6e53lltgdhy8d9a0d5.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.amazonaws.com%2Fuploads%2Farticles%2Ftm6e53lltgdhy8d9a0d5.jpg" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Impressive debugging saves the day
&lt;/h2&gt;

&lt;p&gt;Every app has bugs — yes, especially the ones built by AI. And debugging isn’t just about fixing the problem; it’s about playing detective: reading logs, tracing the code path, and narrowing in on what went wrong. Sometimes a typo can take (a human) hours to find!&lt;/p&gt;

&lt;p&gt;What surprised me is how well Replit handled all that on its own. Without me having to point it in the right direction, it flagged issues I hadn’t even noticed — just by parsing the logs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pro tip: I had Replit add a bunch of logging early on, which turned out to be a huge help for its AI when it came time to debug. Highly recommend that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What’s the catch?
&lt;/h2&gt;

&lt;p&gt;There were several places where the Replit Agent/Assistant got stuck. Replit always gives a preview of the code it is about to write, and sometimes the logic doesn’t make sense. When they’re stuck they do one of the two things:&lt;/p&gt;

&lt;p&gt;❌ The AI sometimes bloats the codebase — creating new code instead of fixing existing logic.&lt;br&gt;
❌ It can also spin in circles, especially around tricky third-party integrations like Google OAuth or Mailgun.&lt;/p&gt;

&lt;p&gt;When that happened, I had to jump in — pasting documentation links and writing hyper-specific prompts to steer the AI in the right direction. Even so, that’s faster than writing everything myself. What took me a few hours could easily have taken me 3–4 weeks solo.&lt;/p&gt;
&lt;h3&gt;
  
  
  Other considerations that slows down development
&lt;/h3&gt;

&lt;p&gt;💡 What happens when users don’t give OAuth consent?&lt;br&gt;
💡 What if users forward random spam emails?&lt;br&gt;
💡 What happens when there are many events in a single email? (note: this is purely prompt engineering, not coding)&lt;/p&gt;
&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt;&lt;br&gt;
Python, Flask, PostgreSQL&lt;br&gt;
Integrations: OpenAI, Google OAuth, Mailgun&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where Replit saved the most time:&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Bootstrapping:&lt;/strong&gt; A single prompt gave me an app 70% of the way. I used Google AI Studio to generate the prompt, so even prompting was made simple! &lt;/p&gt;

&lt;p&gt;Here’s my starter prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using Python Flask and Google AI Python SDK, create a backend API service that transforms text into calendar events. It takes some text, either from email or document, and extracts date information from them. Text can be a travel itinerary, school event, or any date/time related events. From the extracted data, integrate with Google calendar and create corresponding events. For frontend, use any popular framework.
A sample prompt I would feed into this API would be:
Given the following text, extract all event information. For each event, identify:
The event name.
The event description that gives context to this calendar event.
The start date (in YYYY-MM-DD format).
The start time (in HH:MM 24-hour format, if specified).
The end date (in YYYY-MM-DD format, if specified or different from start date).
The end time (in HH:MM 24-hour format, if specified).
The location (if specified).
If a date is relative (e.g., “next Monday,” “tomorrow”), assume the current date is [INSERT CURRENT DATE HERE, e.g., 2023–10–27] for resolving it.
Provide the output as a JSON list, where each object in the list represents an event with keys: “event_description”, “start_date”, “start_time”, “end_date”, “end_time”, “location”. If a piece of information is not found, use null or an empty string for its value.
Text: “”” [PASTE THE EMAIL/DOCUMENT TEXT HERE] “””
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Refactoring code:&lt;/strong&gt; At one point, routes.py got too big to manage, so I have to get it to rewrite it.&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%2Fbroasgv9439jw9pzrth2.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.amazonaws.com%2Fuploads%2Farticles%2Fbroasgv9439jw9pzrth2.jpg" alt=" " width="800" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Marketing and Branding:&lt;/strong&gt; Replit generated a logo and marketing page for me. I don’t even have to write marketing copy!&lt;br&gt;
✅ &lt;strong&gt;DB Migration:&lt;/strong&gt; Replit automatically creates mini scripts to run for database migrations&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%2Fwvu756divyl5soekzque.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.amazonaws.com%2Fuploads%2Farticles%2Fwvu756divyl5soekzque.jpg" alt=" " width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Deployment to production:&lt;/strong&gt; Going live was literally one click. This alone would have taken me a week to figure out.&lt;br&gt;
✅ &lt;strong&gt;Error fixing:&lt;/strong&gt; Fixing errors that pop up every now and then. Simply prompt: fix error in console and it just does it. This is slightly different than debugging, which is still somewhat manual and takes up most of my time (more details below).&lt;br&gt;
✅ &lt;strong&gt;Collaboration:&lt;/strong&gt; While I didn’t use this feature, it’s touted as one of the differentiators compared to other vibe coding tools&lt;br&gt;
✅ &lt;strong&gt;Source control:&lt;/strong&gt; Versioning and simple source control built-in&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I spent the most time wrestling Replit
&lt;/h2&gt;

&lt;p&gt;🔍 &lt;strong&gt;Google OAuth:&lt;/strong&gt; It initially generated working code, even though very messy, inefficient auth workflow. I had to correct this by prompting specific fixes, but it just resulted in more bloated code and unnecessary API calls. Eventually, I pasted the official docs and prompting the AI carefully, and it worked as expected.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pro tip: I’m surprised how great Replit’s AI is at reading external documentation. If you’re technical and know that AI is generating garbage code, just feed it dev docs like this one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🪲 &lt;strong&gt;Debugging:&lt;/strong&gt; If something went wrong and Replit couldn’t figure it out after several tries, I was back in the driver’s seat — manually reading logs and helping the AI understand what to do.&lt;br&gt;
Pro tip: Disable “automatically publish changes” and review Replit’s code, especially for big changes. It will save a lot of time later on if something doesn’t go as planned because you’re in the loop.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Production control:&lt;/strong&gt; One-click deployment is great, but if something breaks, logging is limited (oftentimes crashes) and prod database setup is non-existant.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pro tip: Always use a separate production database, and I’m seriously considering using external logging analytics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is Replit perfect?&lt;/strong&gt; No.&lt;br&gt;
&lt;strong&gt;Is it amazing for hobbyists?&lt;/strong&gt; Absolutely.&lt;/p&gt;

&lt;p&gt;If you value speed, ease of use, and getting something real into the world over fine-tuning every deployment detail, Replit is one of the most productive tools you’ll ever use. It’s a terrific all-in-one platform for side projects or small SaaS apps where moving fast is more important than absolute control.&lt;/p&gt;

&lt;p&gt;And that’s exactly why I’ll keep using it — especially when my goal is shipping something fun without overthinking it.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://x.com/willcheung" rel="noopener noreferrer"&gt;X (https://x.com/willcheung)&lt;/a&gt; for more insights like this.&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>replit</category>
      <category>learning</category>
      <category>ai</category>
    </item>
    <item>
      <title>Launched &amp; Lessons Learned: SMS order-ahead platform with Twilio &amp; Square API</title>
      <dc:creator>Will Cheung</dc:creator>
      <pubDate>Sun, 11 Oct 2020 16:31:22 +0000</pubDate>
      <link>https://forem.com/willcheung/launched-lessons-learned-sms-order-ahead-platform-with-twilio-square-api-34l4</link>
      <guid>https://forem.com/willcheung/launched-lessons-learned-sms-order-ahead-platform-with-twilio-square-api-34l4</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/willcheung/twilio-hackathon-todo-list-textbot-user-flow-with-studio-flow-4188"&gt;first post&lt;/a&gt;, I wrote about my ambitious project to create a SMS order-ahead platform &amp;amp; CRM for small business. The goal was to help coffee shops &amp;amp; restaurant owners to digitally accept new orders (like a Starbucks app) without the 30% commission charged by on-demand delivery businesses. Not to mention, I believe that digital (specifically text/chat) is the future of customer communication. So, I'm happy to announce that I've launched the text-ordering bot, &lt;a href="https://getTextbot.com" rel="noopener noreferrer"&gt;Textbot&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built a to-do list first, and how it evolved
&lt;/h2&gt;

&lt;p&gt;The reason why I chose to create a to-do list is to find the most-common-denominator feature that could power potential sms bot ideas. SMS ordering-bot was just one of my initial ideas. I also had thought about SMS queue-management system for restaurants and simply to keep a list of inventory via text messages, without the bulky app installation &amp;amp; registration. In addition, I even thought about exposing the SMS queue-management platform API, so anyone can use it to build whatever list they want.&lt;/p&gt;

&lt;p&gt;The idea quickly turned into a full-blown ordering and payment system when I started talking to food trucks and independent coffee shops about their pain points. Not only do they want to offer digital ordering to their customers, they don't want to be charged 30% by DoorDash or invest time to setup a new order-ahead app. The biggest revelation was probably the time that these businesses took to invest in creating their menus in these new order-ahead apps. &lt;/p&gt;

&lt;h3&gt;
  
  
  Pain points of these SMBs:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;30% commission fee is outrageous, but necessary to survive. Margin is razor thin.&lt;/li&gt;
&lt;li&gt;Willing to pay a little money to offer existing customers a way to order-ahead. Some opt for traditional call-in-to-order. Others invest in 3rd party apps/websites.&lt;/li&gt;
&lt;li&gt;For those who invest in 3rd party apps, creating a menu is the biggest pain points. They have to take professional pictures, post it with a description, create categories/add-ons, and then publish the menu. &lt;/li&gt;
&lt;li&gt;Menu management is also difficult with these apps, especially for sold-out items.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Which means our MVP needs to:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Be frictionless for consumers to order from these businesses. This means no app installation, no registration, and can start ordering right away.&lt;/li&gt;
&lt;li&gt;Be frictionless for store owners to get started &amp;amp; setup their menus&lt;/li&gt;
&lt;li&gt;Offer online payment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discovering the most important player in the ecosystem
&lt;/h2&gt;

&lt;p&gt;As I paid more attention to how restaurants and coffee shops take orders and allows digital orders, I started to notice a POS (point of sale) system that most "modern" businesses use - &lt;a href="https://squareup.com/us/en" rel="noopener noreferrer"&gt;Square&lt;/a&gt;. (The reason why I put quotes around "modern" is because most businesses are cash-only or non-tech savvy. More on this later.) Since I want to offer online payment where these SMBs can use their existing POS to accept payment (and the store owners are more tech-savvy), then Square is a clear winner. &lt;/p&gt;

&lt;p&gt;I actually didn't integrate with Square from the beginning. Instead, I used Stripe because I had previous experience with their API and they have a very robust marketplace product called &lt;a href="https://stripe.com/connect" rel="noopener noreferrer"&gt;Stripe Connect&lt;/a&gt; that is being used by Uber, DoorDash, etc. There were several offerings, one of which is white-label solution (like Uber, DoorDash, etc.), and the other method onboards customers via Stripe's own onboarding process. As a single person team, I didn't want to be liable of payment fraud or ordering errors, plus the ease of integration is much lighter, I chose to onboard store owners via Stripe and defer those responsibilities to the store owners themselves. &lt;/p&gt;

&lt;p&gt;Integration was a breeze, and I mainly used Stripe integration as a live demo to store owners. Behind the scenes, I knew Square is the POS that I need to integrate with. As I started Square integration, I even participated in &lt;a href="https://square.devpost.com/" rel="noopener noreferrer"&gt;Square Hackathon&lt;/a&gt;! Their API and documentation isn't as rich as Stripe, but it wasn't hard to hack together a prototype to sign up a few beta customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Square + Twilio = Great Product
&lt;/h2&gt;

&lt;p&gt;I worked with a few beta customers to &lt;a href="https://www.gettextbot.com/how-it-works" rel="noopener noreferrer"&gt;streamline the ordering process&lt;/a&gt;. The product is beautiful, figuratively, since SMS actually has no UI. But the customer flow is exactly how I imagined the future to become.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer flow:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Customers text to order their coffee, food, farmer's market items, etc. Customers checkouts via our Square checkout integration. (You can try a live demo - text "order @textbot" to 215-8TEXTME) &lt;br&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%2Fi%2Fiz63jr0fc23bl62e2xtb.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.amazonaws.com%2Fi%2Fiz63jr0fc23bl62e2xtb.jpg" alt="Alt Text" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We send the order directly into businesses' POS or Square account so vendors don't need to use another tool; we operate in the background. They can use our order management system if they don't have Square.&lt;br&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%2Fi%2Fojh5yr8665yf7xncv46j.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.amazonaws.com%2Fi%2Fojh5yr8665yf7xncv46j.jpg" alt="Alt Text" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the order is ready, tab "Done" inside Square POS. The webhook will send an event so we can text customers that the order is ready.&lt;br&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%2Fi%2Flwxrysjpsvldgtan8r9s.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.amazonaws.com%2Fi%2Flwxrysjpsvldgtan8r9s.jpg" alt="Alt Text" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Store owner flow:
&lt;/h3&gt;

&lt;p&gt;This is much more difficult, since there are multiple humps that store owners need to go through to get started. I'm familiar with this SaaS sales funnel, so I got started like the following.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign up using Square SSO, or sign up with email without Square.&lt;/li&gt;
&lt;li&gt;Setup a business profile, much like an Instagram handle. I'm imagining a world where Textbot can order food from anywhere.&lt;/li&gt;
&lt;li&gt;Setup menu, and we made it &lt;em&gt;really&lt;/em&gt; easy. For example, no pictures are required for SMS. &lt;/li&gt;
&lt;/ol&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%2Fi%2Fa9990p7o4pbt2vbwzrea.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%2Fi%2Fa9990p7o4pbt2vbwzrea.png" alt="Alt Text" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Go to market experiments and concerns
&lt;/h2&gt;

&lt;p&gt;Signing up customers was really, really difficult. Imagine a door-to-door salesman selling magazine subscriptions in the past - it's like that. But because we provide a poster for store vendors to tape on their window about how to order-ahead with Textbot, I thought we had a chance for word-of-mouth marketing. &lt;/p&gt;

&lt;p&gt;So I identified a cluster of coffee shops, casual restaurants, and farmers market, and just went door to door. It was painful but enlightening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most customers are not tech-savvy. They asked a lot of basic questions about how order-ahead generally works. I used analogies like Starbucks app to order ahead coffee, but every sale takes a lot of time and many touch points.&lt;/li&gt;
&lt;li&gt;SMS ordering is a pretty new concept, so this definitely adds to the "customer education" sales process&lt;/li&gt;
&lt;li&gt;Some don't use Square.&lt;/li&gt;
&lt;li&gt;Digital marketing conversion isn't great. It may be because I haven't fine-tuned the keywords, but it could also be because most store owners are not technical.&lt;/li&gt;
&lt;li&gt;It is definitely not scalable, at least not something I can do part-time after work.&lt;/li&gt;
&lt;li&gt;I started targeting farmers markets, where I can target a cluster of store owners. Most accept cash / Venmo and not many use Square.&lt;/li&gt;
&lt;li&gt;In my last startup, I found a lot of success being a partner in marketplaces for Chrome extension. I applied to become a partner at Stripe and Square. Stripe doesn't drive much qualified traffic, as expected, and Square doesn't list Textbot as a partner until we hit 100 active customers.&lt;/li&gt;
&lt;li&gt;This one is big: even when customers sign up, they still have to do their own marketing to encourage customers to order via SMS. We made it easy so store owners can print out a poster to tape on their entrance - to tell their customers how to order via text, and even added QR code. But most store owners see this as retaining existing customers, but not as getting new ones. In SaaS world: Sales &amp;gt; Customer Success&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Even though there's an explosion of order-ahead and digital payment for brick &amp;amp; mortar businesses, targeting them isn't very scalable. Without a scalable digital channel, I actually have to spend time in the field getting the first 100 customers, instead of just investing money in marketing. Moreover, I'm sure this was done years ago (perhaps via SMS or Facebook Messenger) and never took off. Was the market not ready? I wonder what were the learnings then?&lt;/p&gt;

&lt;p&gt;However, the recent explosion of e-commerce might present an opportunity to bring Textbot to that market instead. It's a crowded, albeit big, market. Perhaps Textbot will become a look-alike, or it will be the next big chat-based CRM. We will see what happens.&lt;/p&gt;

&lt;p&gt;What would you do?&lt;/p&gt;

</description>
      <category>twiliohackathon</category>
      <category>ruby</category>
      <category>sms</category>
      <category>square</category>
    </item>
    <item>
      <title>Twilio Hackathon: Todo-list Textbot - User Flow with Studio Flow</title>
      <dc:creator>Will Cheung</dc:creator>
      <pubDate>Sat, 18 Apr 2020 19:42:27 +0000</pubDate>
      <link>https://forem.com/willcheung/twilio-hackathon-todo-list-textbot-user-flow-with-studio-flow-4188</link>
      <guid>https://forem.com/willcheung/twilio-hackathon-todo-list-textbot-user-flow-with-studio-flow-4188</guid>
      <description>&lt;p&gt;Hey there. Since my &lt;a href="https://dev.to/willcheung/twilio-hackathon-shared-to-do-grocery-lists-between-friends-family-and-businesses-343a"&gt;first post on Twilio Hackathon&lt;/a&gt;, I've made some good progress. In this post, I'm going to go over user flow design and how I used Studio Flow to stitch my to-do list textbot together.&lt;/p&gt;

&lt;h2&gt;
  
  
  User flow challenges
&lt;/h2&gt;

&lt;p&gt;The biggest challenge of the textbot is having multiple trigger words to take users into different paths. After watching some &lt;a href="https://www.twilio.com/studio" rel="noopener noreferrer"&gt;Studio Flow&lt;/a&gt; tutorials, I embarked on the user journey. I started with a simple path to sign-up users. It quickly got complicated as I added more commands for the bot.&lt;/p&gt;

&lt;p&gt;As an overview, these are the commands that I planned for this hackathon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"hi" - sign up&lt;/li&gt;
&lt;li&gt;"add item" - add an item to list. If list doesn't exist, create one.&lt;/li&gt;
&lt;li&gt;"remove item" - remove item by name or number&lt;/li&gt;
&lt;li&gt;"show items" - show all items in list&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  User Flow v1
&lt;/h3&gt;

&lt;p&gt;To give you an idea how I thought through the process, here's a first version.&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%2Fi%2F64crh6fok74hlt34lpfc.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%2Fi%2F64crh6fok74hlt34lpfc.png" alt="Textbot user flow v1" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each green box is one Studio Flow, and each Flow calls the corresponding API to manage the list. This design decouples the "bot" and the backend, so the Rails app is simply an API that handles requests and backend. Simple enough. I went to work and discovered a few challenges/learnings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;While technically, I can just create a single big Flow that covers all the conditions/branching, I chose a design where each Flow is decoupled and modularized. No monolithic design!&lt;/li&gt;
&lt;li&gt;To connect the Flows, I wrongly assumed that a single phone number can associate to multiple Flows. I had thought that when a user texts the phone number, every Flow will evaluate the trigger words and go down the corresponding Flow when triggers match. Thinking back now, it makes total sense that one phone number can only trigger one Flow. Here's more &lt;a href="https://support.twilio.com/hc/en-us/articles/360016475194-How-do-Twilio-Studio-Flows-Relate-to-Twilio-Phone-Numbers-" rel="noopener noreferrer"&gt;details&lt;/a&gt; about this topic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learnings:&lt;/strong&gt; I needed something that parses all the commands, and then trigger "sub-Flows".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  User flow v2
&lt;/h3&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%2Fi%2Fd308155ndbarpsbk4snf.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%2Fi%2Fd308155ndbarpsbk4snf.png" alt="Textbot user flow v2" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here, a Flow process commands, and triggers other nested Flows based on string condition. A Flow can be triggered via &lt;a href="https://www.twilio.com/docs/studio/rest-api" rel="noopener noreferrer"&gt;Studio Flow REST API&lt;/a&gt; and from another Flow, which helps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Single point of entry for SMS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; I wasn't satisfied with the &lt;a href="https://www.twilio.com/docs/studio/widget-library/split-based-on" rel="noopener noreferrer"&gt;split-based-on widget&lt;/a&gt;. While string parsing with Regex is enough to parse most text, but building more than a "Yes"/"No" condition, it quickly gets complicated and becomes difficult to manage (like this &lt;a href="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/studio-flow.width-1600.png" rel="noopener noreferrer"&gt;example&lt;/a&gt;). A &lt;code&gt;case&lt;/code&gt; statement is much easier to code and manage, especially when there are nested conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learnings:&lt;/strong&gt; While everything kind of works, I found that it was difficult to overcome the lack of control, the unwieldiness of the Flows, and the limitation on debugging. I decided to bring the "command parsing" into code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  User flow v3
&lt;/h3&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%2Fi%2F8jowkxm417br23dogezk.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%2Fi%2F8jowkxm417br23dogezk.png" alt="Textbot user flow v3" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blue box is the Rails app, green boxes are Flows. Here, I'm leveraging a Flow to listen to SMS to trigger and sign up. I could have combined both sign-up and trigger into my app, but the main reason they're in a Flow is because I love using &lt;a href="https://www.twilio.com/docs/studio/widget-library/send-wait-reply" rel="noopener noreferrer"&gt;Send &amp;amp; Wait Reply Widget&lt;/a&gt;, which will take some work to build manually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caveat:&lt;/strong&gt; While using Send &amp;amp; Wait Reply Widget, if the Flow is triggered via Studio Flow REST API, the "wait" part doesn't respond to the user's text. Not sure if it's a bug or by design, but that's the main reason why this Flow is triggered by text in this design, not by my app.&lt;/li&gt;
&lt;li&gt;Leveraging my app to process all the commands. This enables me to use all kinds of string manipulation and &lt;code&gt;case&lt;/code&gt; statements, and allows me to use Studio Flow REST API to tigger corresponding Flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion - Think like Lego Master Builder
&lt;/h2&gt;

&lt;p&gt;Studio Flow is awesome. It's great for anyone, technical or not, to quickly build a chatbot. With the rise of &lt;a href="https://en.wikipedia.org/wiki/No-code_development_platform" rel="noopener noreferrer"&gt;low/no-code development&lt;/a&gt;, it's definitely going to gain more traction. The big plus here is allowing non-coders to build a simple bot, without bothering developers to build it. :)&lt;/p&gt;

&lt;p&gt;However, Flow isn't like software development. If your bot features will grow and evolve over time, I recommend writing software rather than building Flows. Since I started using Studio Flow, I've learned to think of Flows as drag &amp;amp; drop AWS Lambda functions. They're very good at building point solutions, and need to be broken down into smaller and more manageable pieces. &lt;/p&gt;

&lt;p&gt;As an example, I've been looking for ways to programmatically check in the Flows in source control, creating dev/prod Flows, and "migrating" them into production mode. Twilio's &lt;a href="https://www.twilio.com/docs/studio/rest-api/v2" rel="noopener noreferrer"&gt;Studio REST API v2&lt;/a&gt; will be interesting and might help close some of these gaps. During your development process, consider trade-offs between building software and creating Flows that cater towards your use cases.&lt;/p&gt;

</description>
      <category>twiliohackathon</category>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Twilio Hackathon: Shared (to-do, grocery) lists between friends, family, and businesses</title>
      <dc:creator>Will Cheung</dc:creator>
      <pubDate>Sat, 11 Apr 2020 22:40:49 +0000</pubDate>
      <link>https://forem.com/willcheung/twilio-hackathon-shared-to-do-grocery-lists-between-friends-family-and-businesses-343a</link>
      <guid>https://forem.com/willcheung/twilio-hackathon-shared-to-do-grocery-lists-between-friends-family-and-businesses-343a</guid>
      <description>&lt;p&gt;My first post on dev.to, and fist ever hackathon! &lt;/p&gt;

&lt;p&gt;I'm a tinkerer and builder, and I went as far as founding + raising money for a startup via one project. Moreover, I believe the future of business communications is evolving from websites (2000s), to mobile apps (2010s), and to text/chat (2020s). This is my first time playing with Twilio API in depth and I'm excited to dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm going to build
&lt;/h2&gt;

&lt;p&gt;I started out brainstorming an ambitious project of text/chat for local businesses to interact with consumers, so businesses don't have to invest in costly websites/mobile apps to go digital (think restaurants take-outs, coffee shops order-aheads). Especially in covid-19 world, local businesses are even more dependent on take-outs via phone, deliveries, and "non-physical" channels.&lt;/p&gt;

&lt;p&gt;However, the project has been reduced in scope to a simple to-do or grocery list for this hackathon. This will serve as a platform for anything that requires a list, like menu items, order list, grocery list, etc. &lt;/p&gt;

&lt;h3&gt;
  
  
  MVP features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sign up&lt;/li&gt;
&lt;li&gt;Add items&lt;/li&gt;
&lt;li&gt;Remove items&lt;/li&gt;
&lt;li&gt;Show items&lt;/li&gt;
&lt;li&gt;Manage multiple lists&lt;/li&gt;
&lt;li&gt;&lt;del&gt;Share/collaborate list with friends/family (if time allows)&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;(Edit 4/29):&lt;/em&gt; Invite via vcard / MMS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Category Submission:
&lt;/h3&gt;

&lt;p&gt;Exciting X-Factors&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Link
&lt;/h2&gt;

&lt;p&gt;Text "hi" to 616.344.5566&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit (4/13)&lt;/em&gt;: Deployed to AWS EBS and "hi" in the cloud! Do note that EBS is a bit janky with Rails 6, so I wrestled with it a bit. Might go with a container next time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit (4/18)&lt;/em&gt;: All commands on screens below should work. Looking forward to some feedback!&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%2Fi%2F9nstfu9ci1c9701fke9k.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%2Fi%2F9nstfu9ci1c9701fke9k.png" alt="textbot-demo" width="800" height="854"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Link to Code
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Edit (4/20)&lt;/em&gt;: Code includes Rails 6 app and Twilio Studio Flow exports. Setup instructions in &lt;code&gt;README&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/willcheung" rel="noopener noreferrer"&gt;
        willcheung
      &lt;/a&gt; / &lt;a href="https://github.com/willcheung/textbot-rails" rel="noopener noreferrer"&gt;
        textbot-rails
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;

&lt;p&gt;My current stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twilio SMS API&lt;/li&gt;
&lt;li&gt;Twilio Studio Flow + API&lt;/li&gt;
&lt;li&gt;Rails 6&lt;/li&gt;
&lt;li&gt;Postgres 12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had planned to use AWS Lambda instead of Rails, but struggled to get Ruby running on Lambda and talking to RDS. Having built my last startup in Rails, I'm able to get a Rails app running 100x faster than Lambda :). Also, I get flexibility to create a UI if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources/Info
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Edit (4/14)&lt;/em&gt;:&lt;br&gt;
I ran into a few limitations (features?) with Studio Flow. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Studio Flow only allows one flow tied to one (to many) phone number(s). This means I cannot "modularize" and create multiple flows based on trigger keywords. For example, I'd like "Add" to trigger one flow, and "Remove" to trigger another.&lt;/li&gt;
&lt;li&gt;I also discovered the value of &lt;a href="https://www.twilio.com/docs/studio/widget-library/send-wait-reply" rel="noopener noreferrer"&gt;Send &amp;amp; Wait for Reply&lt;/a&gt; widget in Studio. While I attempted to ditch Studio altogether, I thought &lt;del&gt;it was quite a bit of work to build capabilities that saves a state of a request and wait for x hours to pick up the workflow.&lt;/del&gt; &lt;em&gt;Edit (4/23): Pro tip: State can be maintained by using &lt;a href="https://www.twilio.com/blog/2014/07/the-definitive-guide-to-sms-conversation-tracking.html" rel="noopener noreferrer"&gt;cookies&lt;/a&gt;!&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Because of the first limitation, I either have to build a single Flow that has 5+ branches (more when I want to expand on trigger keywords), or I have to handle the trigger keywords parsing myself. Good thing is that I can trigger a Flow via REST API, so I went with handling my own keyword-trigger management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Edit (4/18):&lt;/em&gt; Just posted a &lt;a href="https://dev.to/willcheung/twilio-hackathon-todo-list-textbot-user-flow-with-studio-flow-4188"&gt;second post&lt;/a&gt; with more details on managing Flows. &lt;/p&gt;

</description>
      <category>twiliohackathon</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
