<?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: Sam シ</title>
    <description>The latest articles on DEV Community by Sam シ (@samharri).</description>
    <link>https://dev.to/samharri</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%2F2275665%2Fdff0a4da-a4fe-409a-838a-4d38614f8398.png</url>
      <title>DEV Community: Sam シ</title>
      <link>https://dev.to/samharri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samharri"/>
    <language>en</language>
    <item>
      <title>Build Internal Tools Better: Using Neon, StackAuth, and Vercel</title>
      <dc:creator>Sam シ</dc:creator>
      <pubDate>Thu, 10 Jul 2025 15:33:24 +0000</pubDate>
      <link>https://dev.to/neon-postgres/build-internal-tools-better-using-neon-stackauth-and-vercel-419a</link>
      <guid>https://dev.to/neon-postgres/build-internal-tools-better-using-neon-stackauth-and-vercel-419a</guid>
      <description>&lt;p&gt;Almost every tech company, from small startups to Fortune 500 enterprises relies on internal tools. Larger organizations often have dedicated services and structured procedures in place, but for many others, internal tools are... messy. They often take the form of an insecure setup hosted behind a VPN or worse, an overly complex solution cobbled together from a surprising number of often times costly AWS services.&lt;/p&gt;

&lt;p&gt;But if you’re like me, you just want to share tools easily, securely, cheaply, and without feeling like you’re managing a full-fledged production application (that's already your 9 to 5).&lt;/p&gt;

&lt;p&gt;That's what drove me to create &lt;a href="https://github.com/sam-harri/internal_tooling_neon_stack" rel="noopener noreferrer"&gt;this template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built using Neon, StackAuth, and Vercel's free plans, it provides a secure and scalable foundation, enabling you to quickly launch your next internal tool in minutes: &lt;a href="https://github.com/sam-harri/internal_tooling_neon_stack" rel="noopener noreferrer"&gt;https://github.com/sam-harri/internal_tooling_neon_stack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It comes preconfigured with a Postgres database, API routes, authentication, authorization, and a built-in admin panel – everything you need to get an application running fast.&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%2F05d5g8aw3ajs2w7d8c91.gif" 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%2F05d5g8aw3ajs2w7d8c91.gif" alt="create your own internal tools with neon" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with the Template
&lt;/h2&gt;

&lt;p&gt;Using the template, you can get your internal tool up and running in minutes, and all you need are Neon, Stack Auth, and Vercel accounts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone https://github.com/sam-harri/internal_tooling_neon_stack.git&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then open that new project in your editor of choice, and install the dependencies: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, set up the environment variables by copying over the .env.example to a new .env file. Fill these in with the connection string from your Neon database project in the console, and the Stack Auth keys from the Auth tab.&lt;/p&gt;

&lt;p&gt;Since the template comes with some admin logic already, you’ll need to sync your database by applying the schema:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx drizzle-kit push&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up Roles and Claim Your Project
&lt;/h2&gt;

&lt;p&gt;Head to the Auth tab in the Neon console and claim the project with your Stack Auth account. From there, create two project roles: &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt;, where &lt;code&gt;user&lt;/code&gt; is contained within &lt;code&gt;admin&lt;/code&gt;. Your Project Permissions section should look like this afterward:&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%2Fiaic2u8sjg57kszizcy3.gif" 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%2Fiaic2u8sjg57kszizcy3.gif" alt="set up roles" width="1600" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that’s done, boot up the project locally, log in, and head over to &lt;code&gt;/setup&lt;/code&gt; to claim your admin privileges.&lt;/p&gt;

&lt;p&gt;You can then deploy your app with Vercel. Don’t forget to add your Vercel-provided domain to the allowed domains in Neon’s Auth tab, or your custom domain if you're using one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication and Access Control with StackAuth
&lt;/h2&gt;

&lt;p&gt;You obviously don't want just anyone to be able to access your internal tools. Luckily, Neon comes with an auth solution supporting social and password sign-on, role-based access control, and much more.&lt;/p&gt;

&lt;p&gt;In this template, only authorized users can access the tools. The application admin can whitelist domains, allowing anyone with an email from a specific domain (e.g. &lt;code&gt;yourcompany.tld&lt;/code&gt;) and a verified email to sign up and get access. Likewise, you can grant and revoke access to specific external email addresses, perfect for contractors or partners. Emails can also be explicitly blocked even if their domain is whitelisted.&lt;/p&gt;

&lt;p&gt;Managing access is straightforward via the built-in admin panel at &lt;code&gt;/tools/admin&lt;/code&gt;. Admins can assign or revoke privileges to other users, giving you complete control over your application's access policies. These rules are meant to cover most of the common scenarios you might face, but since it’s a template, you can easily update it to add features like regex validation or subdomain whitelisting.&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%2Fw6kxkfj7jdc7ahw6zs6x.gif" 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%2Fw6kxkfj7jdc7ahw6zs6x.gif" alt="set auth" width="1600" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Database and Logic with Neon and Next.js
&lt;/h2&gt;

&lt;p&gt;Internal tools inherently require business logic and data storage. Neon provides a free serverless Postgres database, along with all the most popular Postgres extensions, and offers a serverless database driver that’s perfect for single-shot queries in Next.js serverless functions.&lt;/p&gt;

&lt;p&gt;For business logic, you can use Next’s server actions and API routes without the overhead of managing and deploying a separate API—ideal for simple internal tools. Vercel makes hosting your Next.js app painless, even allowing you to attach a custom domain so that you can host it at something like yourtool.yourcompany.tld.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the App for Your Team
&lt;/h2&gt;

&lt;p&gt;Customization is designed to be simple. Just fill out your company-specific details in &lt;code&gt;config/app.ts&lt;/code&gt;, then add your custom tools to the &lt;code&gt;app/tools&lt;/code&gt; directory and declare them within the configuration file. To get started, play around with &lt;code&gt;app/tools/tool1/page.tsx&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sam-harri/internal_tooling_neon_stack" rel="noopener noreferrer"&gt;Clone the template&lt;/a&gt;, connect your Neon and StackAuth accounts, and deploy to Vercel. Your next internal tool is just a few commands away!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Coding With Cursor and Windsurf Side by Side</title>
      <dc:creator>Sam シ</dc:creator>
      <pubDate>Thu, 27 Mar 2025 14:27:18 +0000</pubDate>
      <link>https://dev.to/neon-postgres/coding-with-cursor-and-windsurf-side-by-side-4gkd</link>
      <guid>https://dev.to/neon-postgres/coding-with-cursor-and-windsurf-side-by-side-4gkd</guid>
      <description>&lt;p&gt;Getting a taste of vibe coding&lt;/p&gt;

&lt;p&gt;Since the release of GitHub Copilot, AI integration in IDEs has evolved dramatically. Now, two IDEs are being talked about more than any other: Windsurf and Cursor. Both tools are forks of VSCode, offer a similar set of features, and use Anthropic’s Claude model under the hood. &lt;/p&gt;

&lt;p&gt;I vibe coded with both IDEs and took some notes, using my experience developing the Neon Python driver as a guiding example.&lt;/p&gt;

&lt;h2&gt;
  
  
  What feels the same?
&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%2F3fjwujgxu70djsbsofjs.gif" 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%2F3fjwujgxu70djsbsofjs.gif" alt="cursor vs windsurf window comparison" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the surface, Windsurf and Cursor feel strikingly similar. So much so that at first glance, you might think you’re simply looking at two VSCode windows. This means you get the familiar editor you already know and love, with the extensive extension marketplace and setting customization still available. &lt;/p&gt;

&lt;p&gt;Both editors provide the same general interface with the chat window on the right, and the same feature set you’ve come to expect from AI-powered IDEs, like better tab completions, multi file refactoring, agent-based workflows, and context management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tab Completions: Cursor’s Tab vs. Windsurf’s Tabs
&lt;/h2&gt;

&lt;p&gt;I find tab completions one of the most important features of any AI-powered IDE, and it’s where I noticed the biggest productivity gains when developing the Neon Python driver compared to my old setup. &lt;/p&gt;

&lt;p&gt;While both Windsurf and Cursor offer far better autocomplete experiences than GitHub Copilot, Cursor’s tab completions are particularly impressive. Specifically, I’d often make a small change at the top of a file that would need further adjustments below, and Cursor consistently predicted exactly what I needed next—I could just &lt;code&gt;tab-tab-tab&lt;/code&gt; my way through the file. &lt;/p&gt;

&lt;p&gt;For a long time, Cursor held the edge in tab completions when compared to Windsurf’s SuperComplete. But Windsurf is shipping fast. As I was writing this article, they released Windsurf Wave 5, which introduced the Windsurf Tab, and the gap has narrowed. Previously, Cursor felt more aware of the codebase for tabs and provided more useful completions, but Windsurf autocompletions now benefit from the same context engine that powers its AI Agent Cascade, along with a larger and more powerful model.&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%2Ffv4qpg2izrq4mte3n8kh.gif" 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%2Ffv4qpg2izrq4mte3n8kh.gif" alt="cursor vs windsurf tab comparison" width="653" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Context management: Attaching the relevant information
&lt;/h2&gt;

&lt;p&gt;Context management, or the way IDEs handle relevant information to inform their AI suggestions, is another important differentiator. Cursor offers robust support for custom rules that can be conditionally applied based on file type, directory, and other factors. For instance, I easily defined a set of Python rules with strict type hinting, NumPy-style docstrings, and other linting preferences. Throughout my development, Cursor adhered perfectly to these rules, saving me a lot of time in docstring writing and code-style preference refactoring. Windsurf also has built-in rule management, with support for both global rules and rules local to the project, but not for rule file pattern matching or semantic description attachment.&lt;/p&gt;

&lt;p&gt;Yet Windsurf excels in one crucial area: context indexing. Its automatic indexing and understanding of the project structure felt superior. For example, when I made changes to the core logic of the application, Windsurf would reach out to the tests directory and update the relevant unit and integration tests. Cursor is also capable of this, but it requires prompting to get it there, which gets tedious and can easily be forgotten. On the other hand, it offers a wider variety of options for attaching content including most recent changes, git history, lint errors, and most importantly custom docs. Here, at the beginning of my project I was able to create a custom documentation context for Psycopg which I could easily reference. &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%2Fhmoqtstxyclpgrzrzmce.gif" 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%2Fhmoqtstxyclpgrzrzmce.gif" alt="windsurf vs cursor context indexing comparison" width="488" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Windsurf offers only a pre-set documentation library without the means to extend it, though you can attach the link of the documentation as web context to achieve a very similar result. Despite this limitation in customizable context attachments, Windsurf’s automatic context management is really a standout feature and as AI models continue to evolve, this strength will only become more pronounced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent mode: Cursor’s Composer vs Windsurf’s Cascade
&lt;/h2&gt;

&lt;p&gt;Both Windsurf’s Cascade and Cursor’s Composer offer agentic workflows, which allow LLMs to intelligently interact with your codebase, automate repetitive tasks, and implement entire features. &lt;/p&gt;

&lt;p&gt;In practice, I found that both still struggled with more complex coding scenarios. For instance, when implementing Python-to-Postgres type conversions in the Neon driver, both agents had difficulty leveraging Psycopg effectively, because the conversion needed to happen without establishing an actual database connection. Even after I devised a clear implementation plan and explicitly provided it in the prompt instructions, neither IDE could successfully execute without significant manual prompting. &lt;/p&gt;

&lt;p&gt;All hope is not lost, as both agents shined in many other tasks like tests and frontend development. In the case of testing, they generated robust, comprehensive unit and integration tests, accelerating development by at least a few hours without any detailed prompting. For this alone, the agents are well worth having integrated in the IDE. What’s more, to see how each of these fared in more line-heavy development, I tried both out in a React project of mine. After asking for the same component, they each generate working solutions, though Windsurf clearly had a broader understanding of my codebase, utilising some of my custom hooks and API endpoints many directories away. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Both Cursor and Windsurf significantly boosted my productivity when porting the SQL-over-HTTP driver to Python. They both have strengths in different areas —to me, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    &lt;strong&gt;Cursor&lt;/strong&gt; excelled in tab completions, and offered tighter, more intuitive control, providing better confidence in the AI coding workflow. &lt;/li&gt;
&lt;li&gt;    &lt;strong&gt;Windsurf&lt;/strong&gt; offered better and automatic context indexing, leading to a more “magical” feeling agentic workflow. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This vibe coding experiment not only provided insights into these two IDEs but also unveiled a new way to use Neon. You can now leverage Neon more effectively in Python serverless functions (e.g., AWS Lambdas or Azure Functions) by using SQL over HTTP for efficient single-shot queries and transactions. &lt;/p&gt;

&lt;p&gt;Check out the repository &lt;a href="https://github.com/sam-harri/neon-pyserverless" rel="noopener noreferrer"&gt;here&lt;/a&gt;!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Looking at How Replit Agent Handles Databases</title>
      <dc:creator>Sam シ</dc:creator>
      <pubDate>Wed, 13 Nov 2024 16:16:59 +0000</pubDate>
      <link>https://dev.to/neon-postgres/looking-at-how-replit-agent-handles-databases-4259</link>
      <guid>https://dev.to/neon-postgres/looking-at-how-replit-agent-handles-databases-4259</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.replit.com/replitai/agent" rel="noopener noreferrer"&gt;Replit Agent&lt;/a&gt; is a powerful tool within the &lt;a href="https://replit.com/" rel="noopener noreferrer"&gt;Replit&lt;/a&gt; development environment that allows you to offload coding tasks using natural language and AI. By interacting with the Replit Agent, you can generate code, create applications, and modify existing features simply by asking for what you need. &lt;/p&gt;

&lt;p&gt;This automation becomes particularly valuable when you’re working on full-stack applications that require database changes, where it helps maintain consistency between the code and the database schema. Essentially, you can have the Replit Agent implement end-to-end features within your application, changing the API, Database, and Frontend all at once. Getting Replit Agents to handle database migrations&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the tools
&lt;/h2&gt;

&lt;p&gt;To manage your app’s data, Replit provisions a database, a fully managed serverless Postgres database (powered by Neon) that scales on demand. The Agent builds the rest of the app in &lt;code&gt;Flask&lt;/code&gt;, a python microframework for web applications which supports API endpoints and HTML templating.&lt;/p&gt;

&lt;p&gt;With a little prompting, you can get the Agent to use Alembic, a database migration tool that works with SQLAlchemy. SQLAlchemy acts as an Object Relational Mapper (ORM), which helps convert your Python objects and code into the schema used by your Postgres database. Alembic tracks changes to your SQLAlchemy models and automatically generates migration scripts for database schema updates.&lt;/p&gt;

&lt;p&gt;In this article, we will explore how this process works by creating a simple TODO application where features are added incrementally using only natural language prompts to the Replit Agent. You’ll see how you can get Alembic migrations to be automatically generated and how changes are reflected in your Neon database. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To follow along, you need access to the Replit Agent, which is available on the Replit Core or Team subscriptions.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the project
&lt;/h2&gt;

&lt;p&gt;To begin, head over to your Replit home dashboard and create a new &lt;code&gt;Repl&lt;/code&gt;. Instead of setting everything up manually, simply describe the project you want in the Replit Agent textbox. To follow this article, ask the agent to:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create me a TODO application API with CRUD functionalities and a simple frontend to view the tasks&lt;/code&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%2Fhoki10vji2b3u68vh0bl.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%2Fhoki10vji2b3u68vh0bl.png" alt="replit" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you describe the project, Replit Agent will generate a plan that includes the core features and may also suggest additional features or enhancements that could be useful. You can review these suggestions and make adjustments if needed. After you approve the plan, the agent will provision a Neon PostgreSQL database, generate the SQLAlchemy schema for your application, set up the Flask API endpoints, and create the HTML and JavaScript for the frontend.&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%2Fdphk60jb1dvxlh5by2it.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%2Fdphk60jb1dvxlh5by2it.png" alt="dev environment" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within a minute or two, the Replit Agent will finish setting up the fully functional application. At this point, it will prompt you to test everything to make sure it’s working as expected. You should try adding a simple task to the app and confirm that the basic functionality is correct. Once you verify that everything is in order, the Agent will create a commit and a checkpoint for the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a due date feature
&lt;/h2&gt;

&lt;p&gt;With the basic TODO app up and running, you can extend its functionality by allowing users to optionally specify a due date for each task to help with setting deadlines or reminders. To add this feature, simply ask the Replit Agent:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;can you add another text box so that the user can optionally add a due date to the task&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The Replit Agent understands that this feature requires changes to both the frontend (adding a date input) and the backend (modifying the database schema to store the due date). Specifically, a new column needs to be added to the tasks table to store the due_date.&lt;/p&gt;

&lt;p&gt;Here’s how the Agent handles the database update process:&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating the SQLAlchemy model update
&lt;/h3&gt;

&lt;p&gt;The agent modifies the Task model to include a new due_date field. The updated model now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    class Task(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(100), nullable=False)
        description = db.Column(db.Text, nullable=True)
        completed = db.Column(db.Boolean, default=False)
        due_date = db.Column(db.DateTime, nullable=True)


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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating and applying alembic migrations
&lt;/h3&gt;

&lt;p&gt;Since this change alters the database schema, the Replit Agent generates a migration script using Alembic. The migration will add the due_date column to the tasks table in the database. Here’s the up migration script that adds the due_date column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  """Add due_date to Task model

    Revision ID: 0f4af0e04d02
    Revises:
    Create Date: 2024-10-15 17:40:22.891574

    """
    from alembic import op
    import sqlalchemy as sa


    # revision identifiers, used by Alembic.
    revision = '0f4af0e04d02'
    down_revision = None
    branch_labels = None
    depends_on = None


    def upgrade():
        # ### commands auto generated by Alembic - please adjust! ###
        with op.batch_alter_table('task', schema=None) as batch_op:
            batch_op.add_column(sa.Column('due_date', sa.DateTime(), nullable=True))

        # ### end Alembic commands ###


    def downgrade():
        # ### commands auto generated by Alembic - please adjust! ###
        with op.batch_alter_table('task', schema=None) as batch_op:
            batch_op.drop_column('due_date')

        # ### end Alembic commands ###

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

&lt;/div&gt;



&lt;p&gt;Notice that it provides both an upgrade and downgrade migration such that it can also handle rollbacks to previous states. &lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend update
&lt;/h3&gt;

&lt;p&gt;The Agent also updates the frontend to include a new text box where users can input a due date when creating or updating a task, as seen here:&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%2F78893p82srpsq9tr88la.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%2F78893p82srpsq9tr88la.png" alt="to do app" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you confirm the update, the &lt;code&gt;due_date&lt;/code&gt; field is now part of both the UI and the database, and the agent ensures the changes are committed properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding more features: Priority field
&lt;/h2&gt;

&lt;p&gt;You can continue adding features to your TODO app in a similar way. For example, to help users manage the importance of their tasks, you might want to add a priority field to each task. Like before, asking Replit Agent to implement this will trigger another schema change, and it will generate and apply the relevant Alembic migrations automatically:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;add a priority flag to the task creation that can be low, medium, or high and that shows up on the tasks list&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Once the agent has processed the request, it will update both the backend and frontend. The priority field will now appear in the task creation form, and tasks will display their priority (low, medium, or high) in the tasks list:&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%2Fwhwbp3uj41xs26gb5bqu.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%2Fwhwbp3uj41xs26gb5bqu.png" alt="todo app" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, you have introduced two schema changes to the database: one for the &lt;code&gt;due_date&lt;/code&gt; field and one for the &lt;code&gt;priority&lt;/code&gt; field. With each change, Alembic generated a separate migration file. These migration files represent individual checkpoints in the evolution of your database schema.&lt;/p&gt;

&lt;p&gt;When you look into the &lt;code&gt;migrations/versions&lt;/code&gt; folder of your project, you’ll now see two distinct migration files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;     &lt;code&gt;{commit-id}_add_due_date_to_task.py – This migration was created when the due_date field was added.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;    &lt;code&gt;{commit-id}_add_priority_to_task.py – This migration was created when the&lt;/code&gt;priority&lt;code&gt;field was added.&lt;/code&gt;
&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%2Fzkfoeu5xs7u255xp2ng9.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%2Fzkfoeu5xs7u255xp2ng9.png" alt="example" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These migration files are important because they allow you to easily rollback your database schema to any previous state. For example, if you want to undo the addition of the priority field but keep the &lt;code&gt;due_date&lt;/code&gt;, you can apply a rollback to the previous migration using Alembic’s rollback functionality.&lt;/p&gt;

&lt;p&gt;By having separate migrations for each change, you can always track the evolution of your schema and revert specific changes when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolling back
&lt;/h2&gt;

&lt;p&gt;There may come a time when you’re not happy with how a feature turned out, or you simply don’t need it anymore. This is where Replit’s rollback feature, powered by Alembic, becomes especially useful. Not only can you revert the application to a previous state, but you can also undo the database changes, ensuring everything stays consistent.&lt;/p&gt;

&lt;p&gt;In this case, let’s say you no longer need the priority field or you want to remove it for some reason. Rolling back the application and database to the state before the priority field was added is straightforward.&lt;/p&gt;

&lt;p&gt;Replit Agent will automatically run the downgrade migration script, and the priority field will be removed from the database. Additionally, the app’s frontend will be updated, and the feature will no longer appear in the task creation form or task list.&lt;/p&gt;

&lt;p&gt;Here’s the UI after the rollback, which now looks just like it did before the priority field was added:&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%2Fgrq4yifakgwnkp8vmsl4.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%2Fgrq4yifakgwnkp8vmsl4.png" alt="to do app" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To confirm that the database has been successfully rolled back, you can use the Postgres tool in the Replit development environment to inspect the tasks table. As shown below, the priority column is no longer present:&lt;/p&gt;

&lt;p&gt;This means the database schema has been fully reverted to its earlier state, and the feature has been removed not just from the code, but also from the database. Because Alembic tracks every migration in the project, you can roll back your schema to any previous migration—whether that’s the most recent change or a much earlier one. The migration files in the migrations folder act like checkpoints in your database’s timeline. Each migration has a unique revision ID, which allows you to target a specific state when rolling back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Replit Agents can use Alembic and Neon PostgreSQL to handle database changes efficiently. When you request a new feature that affects the database, you can get the agent to generate a migration files with two functions—one for applying changes and another for rolling them back. This makes it easy to update or revert your database schema as 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%2Fs15ioxixa8e2xy31gzcg.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%2Fs15ioxixa8e2xy31gzcg.png" alt="example" width="800" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By creating these files with each change, Replit ensures that your database stays in sync with your application. Whether you’re adding new fields, removing features, or undoing changes, the agent manages the migrations so you don’t have to worry about keeping the database consistent. This allows you to focus on building your application while the underlying structure is handled for you.&lt;/p&gt;

&lt;p&gt;Neon is the serverless Postgres database backing Replit Agents. &lt;a href="https://console.neon.tech/realms/prod-realm/protocol/openid-connect/registrations?client_id=neon-console&amp;amp;redirect_uri=https%3A%2F%2Fconsole.neon.tech%2Fauth%2Fkeycloak%2Fcallback&amp;amp;response_type=code&amp;amp;scope=openid+profile+email&amp;amp;state=O0vePLuUjTzGPk1PdOEDXA%3D%3D%2C%2C%2C" rel="noopener noreferrer"&gt;Start using it for free&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>ai</category>
      <category>database</category>
    </item>
  </channel>
</rss>
