From d90493a8eddda445c9f9d1af31812f5cefc31418 Mon Sep 17 00:00:00 2001 From: Dhanush Reddy <29dhanushreddy@gmail.com> Date: Mon, 28 Apr 2025 13:17:40 +0530 Subject: [PATCH 1/3] docs: add best practices guide --- .../best-practises-using-ai-with-neon.md | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 content/docs/guides/best-practises-using-ai-with-neon.md diff --git a/content/docs/guides/best-practises-using-ai-with-neon.md b/content/docs/guides/best-practises-using-ai-with-neon.md new file mode 100644 index 0000000000..47fa2b4433 --- /dev/null +++ b/content/docs/guides/best-practises-using-ai-with-neon.md @@ -0,0 +1,141 @@ +--- +title: Best Practices for using AI tools with Neon +subtitle: A comprehensive guide to using AI tools with Neon and how to make the most of them +enableTableOfContents: true +updatedOn: '2025-04-28T00:00:00.000Z' +--- + +Generating code with AI using natural language, often called "vibe coding," provides significant speed advantages for development, especially during prototyping. You describe what you need, and the AI generates the code. + +However, the reliability needed for production applications, particularly when managing databases, user data, and authentication, requires more than just speed. AI code generation, while powerful, can sometimes overlook critical details, security considerations, or database best practices. Incorrect SQL, inefficient queries, or insecure patterns can arise if the AI lacks sufficient context or guidance. + +Consider this guide a set of best practices for using AI tools with Neon. These practices are designed to help you leverage the power of AI while ensuring that your code is maintainable, secure, and efficient. + + +## Treat your AI like a teammate: Not a mind reader + +You wouldn't onboard a new developer to your team by just giving them vague instructions and expecting perfect code. You would provide documentation, explain the project's architecture, point out existing patterns, and guide them towards the right libraries and tools. + +Similarly, when you work with AI, you need to provide context. The AI is a powerful tool, but it doesn't have the same understanding of your project as you do. It doesn't know your database schema, your business logic, or your specific requirements unless you tell it. This is where the "mind reader" illusion can lead to problems. You might expect the AI to generate code that fits perfectly into your existing structure, but without context, it often produces generic or incorrect code. + +But by shifting your approach, you can achieve far better results. The first step is providing context. Just as you would brief a new teammate, you need to give the AI the necessary background. Informing the AI about your specific stack, existing patterns, and constraints is crucial for it to generate relevant and useful code. + +For instance, simply telling the AI you are using Drizzle ORM fundamentally changes how it should approach schema generation or query building. Instead of the vague "Create a table for users," a contextual prompt looks more like: "Using Drizzle ORM, define a schema for a users table with fields for `id` (auto-incrementing primary key), `name` (text, not null), and `email` (text, unique, not null)." + +**The Vibe Coding Best Practice**: Treat your AI as a teammate, not a mind reader. Provide it with the context it needs to generate relevant and useful code. This includes your database schema, existing patterns, and specific requirements. The more context you provide, the better the AI can assist you. + +## Give your AI Neon-specific rules + +While large language models possess vast knowledge drawn from diverse sources, their default suggestions for database interactions might lean towards common, generic patterns. When working with a platform like Neon, relying solely on this general knowledge can lead to code that, while functional, isn't optimally leveraging Neon's features or adhering to its specific best practices (like using the Neon serverless driver in edge environments). The output can be less predictable and may require refinement. + +This is where providing targeted guidance becomes invaluable. [Neon AI Rules](https://github.com/neondatabase-labs/ai-rules) serve as this specific direction. They are concise `.mdc` (markdown context) files that encapsulate best practices, patterns, and optimizations tailored for Neon. They don't necessarily introduce entirely new concepts to the AI, but rather ensure it prioritizes and correctly applies Neon-specific libraries, patterns, and optimizations that are crucial for efficient and maintainable code. + +Think of it like providing a developer with the project's specific style guide. They likely know many ways to solve a problem, but the guide ensures they use the preferred, optimized way. By including Neon AI Rules, you significantly increase the likelihood that the AI consistently generates code aligned with Neon's architecture and best practices from the start. + +Here are examples of how Neon AI Rules help steer the AI: + +* **Authentication (`neon-auth.mdc`):** Provides the AI with rules for effectively utilizing [Neon Auth](/blog/neon-auth-is-here-get-authentication-in-a-couple-of-clicks), and integrating authentication via Stack Auth. + - [AI Rules: Neon Auth (`neon-auth.mdc`)](/docs/ai/ai-rules-neon-auth) +* **Serverless driver usage (`neon-serverless.mdc`):** Offers the AI instructions and contextual examples for employing the Neon Serverless Driver. + - [AI Rules: Neon Serverless Driver (`neon-serverless.mdc`)](/docs/ai/ai-rules-neon-serverless) +* **Drizzle ORM Integration (`neon-drizzle.mdc`):** Directs the AI's approach to implementing Drizzle ORM with Neon databases. + - [AI Rules: Neon with Drizzle (`neon-drizzle.mdc`)](/docs/ai/ai-rules-neon-drizzle) + +**The Vibe Coding Best Practice**: Use Neon AI Rules to provide the AI with specific guidance on how to work with Neon. These rules guide the AI towards producing functional, predictable, maintainable, and optimized code, substantially increasing the likelihood that the generated code will work as intended and streamline your development efforts. + +## Create sandboxes on demand: Isolate AI experiments with Neon Branching + +One of the biggest anxieties when moving fast – especially with code you didn't write yourself – is breaking something important. Imagine your AI suggests a significant refactor or a new feature that touches multiple parts of your database schema. How do you test it safely without jeopardizing your main development environment or, worse, production data? In traditional workflows, this often means laboriously setting up separate staging databases, and copying data – a process that grinds the rapid iteration promised by vibe coding to a halt. + +This is where **[Neon Branching](/branching)** becomes a superpower for responsible AI-assisted development. Forget slow, cumbersome staging environments. Neon leverages its unique copy-on-write architecture to let you create fully independent, isolated copies of your *entire* database, including all its data, in literally seconds. + +**How Branching Transforms Vibe Coding testing:** + +Let's say your AI suggests a complex change, like adding a feature that requires altering tables and backfilling data: + +1. **Before applying:** Instead of running the AI's code against your main database branch, you instantly [create a dedicated branch](/docs/manage/branches#create-a-branch) (e.g., `ai_feature_test`) for this experiment. +2. **Isolate the experiment:** Point your application to the new `ai_feature_test` branch's connection string. +3. **Run the AI code:** Now, let the AI apply its generated migrations and run its proposed code against this isolated branch. +4. **Test rigorously:** Thoroughly test the feature on the `ai_feature_test` branch. Does it work? Did it introduce any data inconsistencies? Did the migration apply correctly? +5. **Iterate or merge:** + * **If it breaks:** No problem! The mess is contained within the `ai_feature_test` branch. You can simply [delete the branch](/docs/manage/branches#delete-a-branch) and try a different approach with the AI. + * **If it works:** Fantastic! You now have much higher confidence. You can plan to apply the *tested* migration scripts to your main branch and merge the corresponding application code. + +**The Vibe Coding Best Practice:** Make branching an integral part of your AI-assisted workflow. **Never** let AI apply untested database modifications directly to your production branches. Always create a dedicated Neon branch for testing AI-generated schema changes, data migrations, or complex features. This provides the isolation needed to experiment safely, validate thoroughly, and easily discard unsuccessful attempts, keeping your core database stable while you harness the speed of AI. + +## Automate migration safety checks with the Neon MCP Server + +While AI assistants excel at generating code, including database migration scripts, applying these directly requires careful validation. Neon's instant branching makes it easy to manually create isolated environments for testing these AI-generated migrations, a crucial best practice we've already discussed. However, the **[Neon MCP Server](/docs/ai/mcp-server)** takes this a step further by **automating this test-on-a-branch workflow directly within your AI tool** (like Cursor, Claude, etc.), making it a seamless part of your development process. + +The Neon MCP Server acts as an intelligent bridge, allowing AI tools (like [Claude Desktop](/guides/neon-mcp-server), [Cursor](/guides/cursor-mcp-neon), etc.) that support the Model Context Protocol to interact with Neon using natural language *and* specialized, safety-oriented "tools". For database migrations, the key tools are `prepare_database_migration` and `complete_database_migration`. + +Here's how it enhances the process when you ask the AI to perform a schema change (e.g., "Add an `updated_at` column to the `orders` table"): + +1. **Automated branch creation (`prepare_database_migration`):** Instead of you manually creating a branch, the AI uses the `prepare_database_migration` tool. This triggers the Neon MCP server to automatically **leverage Neon's branching capability to create a fresh, temporary branch**. +2. **Targeted execution & verification:** The Neon MCP server then directs the execution of the AI-generated migration SQL **specifically against this newly created temporary branch**. The AI can then follow up (or be prompted to) by running descriptive commands or test queries *against the same temporary branch* to verify the schema change and ensure its correctness, all without affecting your main database. +3. **Controlled commit (`complete_database_migration`):** Once the AI (and you, reviewing its output) confirms the migration was successful in the isolated branch, you can approve the final step. The AI uses the `complete_database_migration` tool. The Neon MCP server then manages the process of applying the confirmed changes to your actual target branch and cleans up the temporary branch used for testing. + +This automated workflow harnesses the power and safety of Neon's branching but makes it a seamless, integrated part of the AI-driven development process. The AI effectively uses a dedicated, temporary Neon branch as its sandboxed testing ground for every migration, orchestrated via the Neon MCP server. + +**The Vibe Coding Best Practice:** For AI-driven database migrations, utilize tools connected via the Neon MCP server. This setup ensures that the best practice of testing migrations on an isolated branch is **automatically applied**, integrating safety checks directly into the conversational workflow and reducing the chance of applying unverified schema changes generated by the AI. + +## Neon RLS: Database-level security for AI-generated applications + +So, you're leveraging AI to accelerate development, building features and fetching data efficiently. But a critical challenge remains: ensuring users only see the data they're supposed to. How do you implement authorization reliably, especially when AI is generating significant portions of your application logic? + +The common approach, and often the default an AI might suggest, involves embedding authorization checks directly into your application's data-fetching code. You end up with logic sprinkled throughout your backend: + +```javascript +// Inside an API endpoint or server function... +const userId = getCurrentUserIdFromSession(); // App logic to get user ID +const query = `SELECT * FROM projects WHERE id = $1 AND owner_user_id = $2`; // <-- Auth check mixed with query +const { rows } = await db.query(query, [projectId, userId]); +// ... application logic to handle results or lack thereof +``` + +Now, consider scaling this with AI assistance. Can you be certain the AI will consistently remember to add the correct `AND owner_user_id = $2` clause (or more complex permission logic) every single time it generates code that accesses sensitive data? What happens when requirements change? Relying on just application-level checks, especially when generated or modified by AI, creates a significant risk surface. One missed `WHERE` clause can lead to critical data leaks. + +Instead of scattering authorization logic across your application, a more robust approach is to define access rules directly within the database. Postgres Row-Level Security (RLS) provides precisely this mechanism. RLS allows you to create policies on your database tables that dictate which rows are visible or modifiable based on the context of the user making the query. + +**Making RLS practical: Neon RLS and JWT integration** + +While standard Postgres RLS is powerful, integrating it smoothly into modern web applications (which typically rely on JWTs for authentication) can involve managing complex session states or database roles. [Neon RLS](/blog/introducing-neon-authorize) simplifies this through built-in integration with popular authentication providers (like Clerk, Auth0, Stack Auth, and any asymmetric JWKS provider), automatically handling JWT verification. + +**How it works:** When Neon RLS is enabled and configured with your provider, Neon can automatically validate the JWT sent with each database connection request. It extracts relevant user information (like `sub` for user ID, or custom claims for roles/permissions) and makes it available within the database session for your RLS policies to use. + +**Why Neon RLS is a Vibe Coding Best Practice:** + +* **Less room for error:** Security is enforced by the database itself *before* any data is returned to the application. Even if your AI generates application code with flawed logic, RLS acts as a fundamental backstop, preventing unauthorized data access. +* **Centralized authorization logic:** All your authorization rules are defined in one place (the database migrations), making them easier to manage and update. This is especially important when working with AI, as it reduces the risk of inconsistent or incomplete authorization checks in your application code. + +## Sequence your moves: Coordinating migrations and deployments + +When developing rapidly, especially with AI generating migration scripts or application code, it's easy to apply changes haphazardly. However, deploying database schema modifications, data migrations, and application code updates requires careful coordination. Simply asking your AI to "Add a `last_login` timestamp to the `users` table and update the profile page to show it" can lead to deployment chaos if the steps aren't executed in the correct order. The common pitfall is deploying application code that relies on a database change *before* that change is live, or applying a restrictive schema change (like making a column `NOT NULL`) before the application consistently provides data for it, resulting in runtime errors and frantic rollbacks. + +Instead, treat database migrations and related application deployments like a carefully orchestrated multi-stage process. Each stage must complete successfully before the next begins. A robust sequence often starts with applying **backward-compatible schema changes**, such as adding a new `NULLABLE` column. This ensures existing application code continues functioning. Next, you deploy the **application code that is aware** of the new schema but doesn't require it yet, handling both old and new states gracefully and, importantly, starting to write to the new column. + +Following this, you might perform any necessary **data backfills**, ensuring existing records conform to the new structure. Only then do you deploy application code that **relies** on the new schema element being present. Finally, if needed, you can apply **forward-incompatible schema changes**, like making the new column `NOT NULL` or removing old schema elements, now that the application no longer uses the previous state. + +**The Vibe Coding Best Practice:** Adopt a phased rollout strategy for database and application changes. Guide your AI through backward-compatible schema updates, code deployments aware of (but not reliant on) changes, backfills, code reliance deployments, and finally, forward-incompatible schema cleanup. This structured approach minimizes deployment risk and ensures that your application remains stable throughout the process. + +## Rewind time: Safe rollbacks with Neon PITR + +Despite careful sequencing and testing, sometimes a problematic change—perhaps a subtly flawed migration script suggested by AI or a complex manual error—slips into production, causing application errors or data corruption. In these high-pressure moments, you need a fast, reliable way to revert *both* the application code and the database state. Traditional approaches like complex rollback migrations or restoring from slow, potentially outdated `pg_dump` backups often mean extended downtime and unacceptable data loss. + +Neon's unique architecture, retaining a history of all database changes (the WAL), provides a powerful safety net: **[Point-in-Time Restore (PITR)](/blog/announcing-point-in-time-restore)**. Instead of complex manual reversals, you can treat the database's recent history as a timeline you can instantly rewind. If a bad migration impacts your production database, first identify the issue and determine the precise time *just before* the faulty migration was applied, perhaps using deployment logs or Neon's [Time Travel](/docs/guides/time-travel-assist) feature to query past states directly in the console. Then, execute the PITR operation from the Neon Console's **Restore** section, selecting the target timestamp. Neon completes the restore in **just a couple of seconds**, bringing the database state cleanly back to the desired point before the error occurred. + +Crucially, this database rollback must be paired with **rolling back your application code** to the previous working version that corresponds to the restored database schema. Once both are reverted, verify the application's functionality and data integrity thoroughly. This streamlined process, facilitated by Neon's instantaneous PITR, transforms rollbacks from a high-risk, slow procedure into a fast, predictable recovery mechanism. + +**The Vibe Coding Best Practice:** Leverage Neon's Point-in-Time Restore as your primary database rollback strategy. When a deployment goes wrong, use PITR to quickly revert the database state to a point just before the error, coordinated with deploying the corresponding application code version. This provides a critical safety net, giving you the confidence to iterate quickly with AI tools, knowing you have a robust and fast fallback for unforeseen issues without resorting to risky manual rollbacks or slow backup restores. + +## Conclusion + +Using AI tools to generate code can significantly speed up development, but it requires careful consideration of best practices to ensure reliability, security, and maintainability. By treating AI as a teammate, providing context, using Neon AI Rules, leveraging Neon Branching for safe testing, automating migration safety checks with the Neon MCP server, implementing database-level security with Neon RLS, coordinating migrations and deployments, and utilizing Point-in-Time Restore for rollbacks, you can utilize the power of AI while maintaining a robust and secure development process. + +## Resources + +- [Neon Auth is Here: Get Authentication in a Couple of Clicks](https://neon.tech/blog/neon-auth-is-here-get-authentication-in-a-couple-of-clicks) +- [Instant Branching for Postgres](https://neon.tech/branching) +- [Neon's MCP Server is Here](https://neon.tech/blog/let-claude-manage-your-neon-databases-our-mcp-server-is-here) +- [Announcing Point-in-Time Restore](https://neon.tech/blog/announcing-point-in-time-restore) +- [Neon RLS](https://neon.tech/blog/introducing-neon-authorize) From d29d406b7605c577526141953b5f1c3c5baa7831 Mon Sep 17 00:00:00 2001 From: Dhanush Reddy <29dhanushreddy@gmail.com> Date: Tue, 13 May 2025 00:28:49 +0530 Subject: [PATCH 2/3] docs: address comments --- .../best-practises-using-ai-with-neon.md | 483 +++++++++++++++--- 1 file changed, 414 insertions(+), 69 deletions(-) diff --git a/content/docs/guides/best-practises-using-ai-with-neon.md b/content/docs/guides/best-practises-using-ai-with-neon.md index 47fa2b4433..46ad33a359 100644 --- a/content/docs/guides/best-practises-using-ai-with-neon.md +++ b/content/docs/guides/best-practises-using-ai-with-neon.md @@ -1,5 +1,5 @@ --- -title: Best Practices for using AI tools with Neon +title: Best practices for using AI tools with Neon subtitle: A comprehensive guide to using AI tools with Neon and how to make the most of them enableTableOfContents: true updatedOn: '2025-04-28T00:00:00.000Z' @@ -12,121 +12,466 @@ However, the reliability needed for production applications, particularly when m Consider this guide a set of best practices for using AI tools with Neon. These practices are designed to help you leverage the power of AI while ensuring that your code is maintainable, secure, and efficient. -## Treat your AI like a teammate: Not a mind reader +## Provide clear context: Treat AI as a teammate -You wouldn't onboard a new developer to your team by just giving them vague instructions and expecting perfect code. You would provide documentation, explain the project's architecture, point out existing patterns, and guide them towards the right libraries and tools. +AI tools require specific context to generate useful code. Without context, the AI may produce generic or incorrect code that doesn't fit your project's needs. -Similarly, when you work with AI, you need to provide context. The AI is a powerful tool, but it doesn't have the same understanding of your project as you do. It doesn't know your database schema, your business logic, or your specific requirements unless you tell it. This is where the "mind reader" illusion can lead to problems. You might expect the AI to generate code that fits perfectly into your existing structure, but without context, it often produces generic or incorrect code. +### Problem: Vague prompts lead to generic code -But by shifting your approach, you can achieve far better results. The first step is providing context. Just as you would brief a new teammate, you need to give the AI the necessary background. Informing the AI about your specific stack, existing patterns, and constraints is crucial for it to generate relevant and useful code. +**Example scenario:** You want to create a `users` table in your Neon Postgres database using Drizzle ORM. -For instance, simply telling the AI you are using Drizzle ORM fundamentally changes how it should approach schema generation or query building. Instead of the vague "Create a table for users," a contextual prompt looks more like: "Using Drizzle ORM, define a schema for a users table with fields for `id` (auto-incrementing primary key), `name` (text, not null), and `email` (text, unique, not null)." +If you ask an AI to perform a task without sufficient background: +* **Your prompt (lacking context):** + ``` + Create a table for users. + ``` +* **Potential AI output (Generic SQL - may not suit your project):** + ```sql + CREATE TABLE users ( + id SERIAL PRIMARY KEY, + username VARCHAR(255), + email VARCHAR(255) + ); + ``` + The AI doesn't know you're using Postgres, MySQL, or Drizzle ORM, Prisma. It also lacks specific requirements like what columns you need, constraints, or data types. -**The Vibe Coding Best Practice**: Treat your AI as a teammate, not a mind reader. Provide it with the context it needs to generate relevant and useful code. This includes your database schema, existing patterns, and specific requirements. The more context you provide, the better the AI can assist you. +### Solution: Provide specific project context in your prompts -## Give your AI Neon-specific rules +Guide the AI by including details about your stack, libraries, existing schema, and specific requirements. -While large language models possess vast knowledge drawn from diverse sources, their default suggestions for database interactions might lean towards common, generic patterns. When working with a platform like Neon, relying solely on this general knowledge can lead to code that, while functional, isn't optimally leveraging Neon's features or adhering to its specific best practices (like using the Neon serverless driver in edge environments). The output can be less predictable and may require refinement. +* **Your prompt:** + ``` + Using Drizzle ORM with Postgres, define a schema for a 'users' table. + It needs: + - 'id': an auto-incrementing integer, serving as the primary key. + - 'name': text, not null. + - 'email': text, unique, and not null. + - 'createdAt': a timestamp with time zone, defaulting to the current timestamp. + ``` +* **Expected AI output:** + ```typescript + import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core'; -This is where providing targeted guidance becomes invaluable. [Neon AI Rules](https://github.com/neondatabase-labs/ai-rules) serve as this specific direction. They are concise `.mdc` (markdown context) files that encapsulate best practices, patterns, and optimizations tailored for Neon. They don't necessarily introduce entirely new concepts to the AI, but rather ensure it prioritizes and correctly applies Neon-specific libraries, patterns, and optimizations that are crucial for efficient and maintainable code. + export const users = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), + }); + ``` -Think of it like providing a developer with the project's specific style guide. They likely know many ways to solve a problem, but the guide ensures they use the preferred, optimized way. By including Neon AI Rules, you significantly increase the likelihood that the AI consistently generates code aligned with Neon's architecture and best practices from the start. +### Key context points to provide your AI -Here are examples of how Neon AI Rules help steer the AI: +* **Libraries/ORMs:** e.g., "using Drizzle ORM," "node-postgres", "Prisma." etc. +* **Business logic/constraints:** e.g., "Ensure email addresses are stored in lowercase," "Usernames must be at least 3 characters." -* **Authentication (`neon-auth.mdc`):** Provides the AI with rules for effectively utilizing [Neon Auth](/blog/neon-auth-is-here-get-authentication-in-a-couple-of-clicks), and integrating authentication via Stack Auth. - - [AI Rules: Neon Auth (`neon-auth.mdc`)](/docs/ai/ai-rules-neon-auth) -* **Serverless driver usage (`neon-serverless.mdc`):** Offers the AI instructions and contextual examples for employing the Neon Serverless Driver. - - [AI Rules: Neon Serverless Driver (`neon-serverless.mdc`)](/docs/ai/ai-rules-neon-serverless) -* **Drizzle ORM Integration (`neon-drizzle.mdc`):** Directs the AI's approach to implementing Drizzle ORM with Neon databases. - - [AI Rules: Neon with Drizzle (`neon-drizzle.mdc`)](/docs/ai/ai-rules-neon-drizzle) +By treating your AI as a knowledgeable teammate who needs a clear brief, you'll get more accurate, relevant, and immediately usable code. -**The Vibe Coding Best Practice**: Use Neon AI Rules to provide the AI with specific guidance on how to work with Neon. These rules guide the AI towards producing functional, predictable, maintainable, and optimized code, substantially increasing the likelihood that the generated code will work as intended and streamline your development efforts. +## Leverage Neon AI rules for optimized code -## Create sandboxes on demand: Isolate AI experiments with Neon Branching +While large language models possess vast knowledge drawn from diverse sources, their default suggestions for database interactions might lean towards common, generic patterns. When working with a platform like Neon, relying solely on this general knowledge can lead to code that, while functional, isn't optimally leveraging Neon's features. -One of the biggest anxieties when moving fast – especially with code you didn't write yourself – is breaking something important. Imagine your AI suggests a significant refactor or a new feature that touches multiple parts of your database schema. How do you test it safely without jeopardizing your main development environment or, worse, production data? In traditional workflows, this often means laboriously setting up separate staging databases, and copying data – a process that grinds the rapid iteration promised by vibe coding to a halt. +To ensure the AI generates code that is optimized, predictable, and leverages Neon's unique features, provide it with **[Neon AI Rules](https://github.com/neondatabase-labs/ai-rules)**. These rules are concise `.mdc` (markdown context) files that act like a specialized "style guide" for your AI, ensuring it understands how to best interact with Neon. -This is where **[Neon Branching](/branching)** becomes a superpower for responsible AI-assisted development. Forget slow, cumbersome staging environments. Neon leverages its unique copy-on-write architecture to let you create fully independent, isolated copies of your *entire* database, including all its data, in literally seconds. +### Optimizing library usage with AI Rules -**How Branching Transforms Vibe Coding testing:** +Consider setting up Drizzle ORM to interact with your Neon database. -Let's say your AI suggests a complex change, like adding a feature that requires altering tables and backfilling data: +* **Scenario:** You're initializing Drizzle ORM in a Next.js project to connect to Neon. +* **Your prompt to the AI:** + ``` + Set up Drizzle ORM in my Next.js project to connect to my Neon Postgres database. + ``` -1. **Before applying:** Instead of running the AI's code against your main database branch, you instantly [create a dedicated branch](/docs/manage/branches#create-a-branch) (e.g., `ai_feature_test`) for this experiment. -2. **Isolate the experiment:** Point your application to the new `ai_feature_test` branch's connection string. -3. **Run the AI code:** Now, let the AI apply its generated migrations and run its proposed code against this isolated branch. -4. **Test rigorously:** Thoroughly test the feature on the `ai_feature_test` branch. Does it work? Did it introduce any data inconsistencies? Did the migration apply correctly? -5. **Iterate or merge:** - * **If it breaks:** No problem! The mess is contained within the `ai_feature_test` branch. You can simply [delete the branch](/docs/manage/branches#delete-a-branch) and try a different approach with the AI. - * **If it works:** Fantastic! You now have much higher confidence. You can plan to apply the *tested* migration scripts to your main branch and merge the corresponding application code. +* **No AI rules provided:** + The AI might suggest using Drizzle with a standard `node-postgres` client, which may not work in serverless environments like Vercel edge functions or AWS Lambda: + ```typescript + import { drizzle } from 'drizzle-orm/node-postgres'; + export const db = drizzle(process.env.DATABASE_URL); + ``` -**The Vibe Coding Best Practice:** Make branching an integral part of your AI-assisted workflow. **Never** let AI apply untested database modifications directly to your production branches. Always create a dedicated Neon branch for testing AI-generated schema changes, data migrations, or complex features. This provides the isolation needed to experiment safely, validate thoroughly, and easily discard unsuccessful attempts, keeping your core database stable while you harness the speed of AI. +* **With AI rules: `neon-drizzle.mdc` rule provided to AI:** + + By including the [AI Rule for Neon with Drizzle (`neon-drizzle.mdc`)](/docs/ai/ai-rules-neon-drizzle) in your AI tool's context, the AI is guided to use Neon's serverless driver with Drizzle ORM: -## Automate migration safety checks with the Neon MCP Server + ```typescript + import { drizzle } from 'drizzle-orm/neon-http'; + import { neon } from '@neondatabase/serverless'; -While AI assistants excel at generating code, including database migration scripts, applying these directly requires careful validation. Neon's instant branching makes it easy to manually create isolated environments for testing these AI-generated migrations, a crucial best practice we've already discussed. However, the **[Neon MCP Server](/docs/ai/mcp-server)** takes this a step further by **automating this test-on-a-branch workflow directly within your AI tool** (like Cursor, Claude, etc.), making it a seamless part of your development process. + const sql = neon(process.env.DATABASE_URL); + export const db = drizzle(sql); + ``` -The Neon MCP Server acts as an intelligent bridge, allowing AI tools (like [Claude Desktop](/guides/neon-mcp-server), [Cursor](/guides/cursor-mcp-neon), etc.) that support the Model Context Protocol to interact with Neon using natural language *and* specialized, safety-oriented "tools". For database migrations, the key tools are `prepare_database_migration` and `complete_database_migration`. +### How to use Neon AI rules? -Here's how it enhances the process when you ask the AI to perform a schema change (e.g., "Add an `updated_at` column to the `orders` table"): +1. **Identify the Neon features** you're implementing (e.g., Drizzle ORM, serverless functions, authentication). +2. **Add the corresponding `.mdc` rule files** to your AI tool's context. + * **Cursor:** Place files like `neon-drizzle.mdc` into your project's `.cursor/rules/` folder. + * **Other AI Tools (e.g., Claude, ChatGPT with context):** Copy the content of the rule file (from the [Neon AI Rules GitHub repo](https://github.com/neondatabase-labs/ai-rules)) and provide it as part of your prompt or as an included context file. +3. **Prompt as usual:** Your AI will now be better equipped to generate Neon-optimized code. -1. **Automated branch creation (`prepare_database_migration`):** Instead of you manually creating a branch, the AI uses the `prepare_database_migration` tool. This triggers the Neon MCP server to automatically **leverage Neon's branching capability to create a fresh, temporary branch**. -2. **Targeted execution & verification:** The Neon MCP server then directs the execution of the AI-generated migration SQL **specifically against this newly created temporary branch**. The AI can then follow up (or be prompted to) by running descriptive commands or test queries *against the same temporary branch* to verify the schema change and ensure its correctness, all without affecting your main database. -3. **Controlled commit (`complete_database_migration`):** Once the AI (and you, reviewing its output) confirms the migration was successful in the isolated branch, you can approve the final step. The AI uses the `complete_database_migration` tool. The Neon MCP server then manages the process of applying the confirmed changes to your actual target branch and cleans up the temporary branch used for testing. -This automated workflow harnesses the power and safety of Neon's branching but makes it a seamless, integrated part of the AI-driven development process. The AI effectively uses a dedicated, temporary Neon branch as its sandboxed testing ground for every migration, orchestrated via the Neon MCP server. +### Available Neon AI rules -**The Vibe Coding Best Practice:** For AI-driven database migrations, utilize tools connected via the Neon MCP server. This setup ensures that the best practice of testing migrations on an isolated branch is **automatically applied**, integrating safety checks directly into the conversational workflow and reducing the chance of applying unverified schema changes generated by the AI. +* **Authentication (`neon-auth.mdc`):** Provides the AI with rules for effectively utilizing [Neon Auth](/blog/neon-auth-is-here-get-authentication-in-a-couple-of-clicks), and integrating authentication via Stack Auth. Learn more: [AI Rules: Neon Auth](/docs/ai/ai-rules-neon-auth) +* **Serverless driver usage (`neon-serverless.mdc`):** Provides the AI instructions and contextual examples for employing the Neon Serverless Driver. Learn more: [AI Rules: Neon Serverless Driver](/docs/ai/ai-rules-neon-serverless) +* **Drizzle ORM Integration (`neon-drizzle.mdc`):** Provides the AI instructions and contextual examples for effectively using the Neon serverless driver with Drizzle ORM. Learn more: [AI Rules: Neon with Drizzle](/docs/ai/ai-rules-neon-drizzle) -## Neon RLS: Database-level security for AI-generated applications +By using Neon AI Rules, you significantly increase the likelihood that the AI consistently generates code aligned with Neon's architecture and best practices from the start of your project. This reduces the need for manual adjustments and ensures that the generated code is optimized for performance, security, and maintainability. -So, you're leveraging AI to accelerate development, building features and fetching data efficiently. But a critical challenge remains: ensuring users only see the data they're supposed to. How do you implement authorization reliably, especially when AI is generating significant portions of your application logic? +## Isolate AI experiments with Instant Neon branching -The common approach, and often the default an AI might suggest, involves embedding authorization checks directly into your application's data-fetching code. You end up with logic sprinkled throughout your backend: +When AI suggests database schema changes or complex data migrations, testing them directly on your main development or production branch is risky. Neon's instant, copy-on-write **[branching](/branching)** allows you to create isolated database sandboxes in seconds, perfect for safely testing AI-generated code. -```javascript -// Inside an API endpoint or server function... -const userId = getCurrentUserIdFromSession(); // App logic to get user ID -const query = `SELECT * FROM projects WHERE id = $1 AND owner_user_id = $2`; // <-- Auth check mixed with query -const { rows } = await db.query(query, [projectId, userId]); -// ... application logic to handle results or lack thereof -``` +### Problem: Testing AI-generated schema changes directly is unsafe -Now, consider scaling this with AI assistance. Can you be certain the AI will consistently remember to add the correct `AND owner_user_id = $2` clause (or more complex permission logic) every single time it generates code that accesses sensitive data? What happens when requirements change? Relying on just application-level checks, especially when generated or modified by AI, creates a significant risk surface. One missed `WHERE` clause can lead to critical data leaks. +Applying AI-suggested `ALTER TABLE` statements or migration scripts without thorough testing can lead to: +* Data corruption in your production database. +* Unexpected side effects on other parts of your application. +* Rollbacks if issues arise. -Instead of scattering authorization logic across your application, a more robust approach is to define access rules directly within the database. Postgres Row-Level Security (RLS) provides precisely this mechanism. RLS allows you to create policies on your database tables that dictate which rows are visible or modifiable based on the context of the user making the query. +### Solution: Use a dedicated Neon branch for each AI experiment -**Making RLS practical: Neon RLS and JWT integration** +Before letting an AI apply database modifications, create a new branch. Test the changes in isolation, then discard or continue as needed. -While standard Postgres RLS is powerful, integrating it smoothly into modern web applications (which typically rely on JWTs for authentication) can involve managing complex session states or database roles. [Neon RLS](/blog/introducing-neon-authorize) simplifies this through built-in integration with popular authentication providers (like Clerk, Auth0, Stack Auth, and any asymmetric JWKS provider), automatically handling JWT verification. +**Workflow for safely testing AI-generated database changes:** -**How it works:** When Neon RLS is enabled and configured with your provider, Neon can automatically validate the JWT sent with each database connection request. It extracts relevant user information (like `sub` for user ID, or custom claims for roles/permissions) and makes it available within the database session for your RLS policies to use. +Let's say your AI suggests adding an `is_verified` column to your `users` table: -**Why Neon RLS is a Vibe Coding Best Practice:** +1. **AI suggests a migration:** + ```sql + ALTER TABLE "users" ADD COLUMN "is_verified" boolean DEFAULT false; + ``` -* **Less room for error:** Security is enforced by the database itself *before* any data is returned to the application. Even if your AI generates application code with flawed logic, RLS acts as a fundamental backstop, preventing unauthorized data access. -* **Centralized authorization logic:** All your authorization rules are defined in one place (the database migrations), making them easier to manage and update. This is especially important when working with AI, as it reduces the risk of inconsistent or incomplete authorization checks in your application code. +2. **Create a test branch (e.g., `feature/is_verified`):** + You can create a branch using the Neon Console, CLI, or API. + * **Using Neon CLI:** + ```bash + neon branches create --project-id --name feature/is_verified --parent production + neon connection-string --project-id feature/is_verified + ``` + > This creates `feature/is_verified` from your `production` branch and outputs the connection string. -## Sequence your moves: Coordinating migrations and deployments + * **Using Neon console:** Navigate to your project > Branches > "Create Branch". -When developing rapidly, especially with AI generating migration scripts or application code, it's easy to apply changes haphazardly. However, deploying database schema modifications, data migrations, and application code updates requires careful coordination. Simply asking your AI to "Add a `last_login` timestamp to the `users` table and update the profile page to show it" can lead to deployment chaos if the steps aren't executed in the correct order. The common pitfall is deploying application code that relies on a database change *before* that change is live, or applying a restrictive schema change (like making a column `NOT NULL`) before the application consistently provides data for it, resulting in runtime errors and frantic rollbacks. +3. **Apply AI's migration to the test branch:** + Connect to the `feature/is_verified` branch (e.g., using `psql` or your ORM's migration tool by changing your environment variables) and apply the AI-generated SQL: + ```sql + ALTER TABLE "users" ADD COLUMN "is_verified" boolean DEFAULT false; + ``` -Instead, treat database migrations and related application deployments like a carefully orchestrated multi-stage process. Each stage must complete successfully before the next begins. A robust sequence often starts with applying **backward-compatible schema changes**, such as adding a new `NULLABLE` column. This ensures existing application code continues functioning. Next, you deploy the **application code that is aware** of the new schema but doesn't require it yet, handling both old and new states gracefully and, importantly, starting to write to the new column. +4. **Test thoroughly on the isolated branch:** + * Run application tests that interact with the `is_verified` column. + * Check for any unintended data changes or performance issues. -Following this, you might perform any necessary **data backfills**, ensuring existing records conform to the new structure. Only then do you deploy application code that **relies** on the new schema element being present. Finally, if needed, you can apply **forward-incompatible schema changes**, like making the new column `NOT NULL` or removing old schema elements, now that the application no longer uses the previous state. +5. **Decide and act:** + * **If issues arise:** The `feature/is_verified` branch contains the problematic changes. Your `production` branch is unaffected. + * **Discard the branch:** + ```bash + neon branches delete --project-id feature/is_verified + ``` + * Iterate with your AI on a new approach, creating a new test branch. + * **If Successful:** You have high confidence in the change. + * Apply the *same validated migration script* to your `production` (or staging) branch during a planned deployment. + * You can delete the temporary test branch. -**The Vibe Coding Best Practice:** Adopt a phased rollout strategy for database and application changes. Guide your AI through backward-compatible schema updates, code deployments aware of (but not reliant on) changes, backfills, code reliance deployments, and finally, forward-incompatible schema cleanup. This structured approach minimizes deployment risk and ensures that your application remains stable throughout the process. +### Benefits of using Neon branching for AI experiments -## Rewind time: Safe rollbacks with Neon PITR +* **Safety:** Your primary database remains untouched during experimentation. +* **Isolation:** Each experiment runs in its own complete database copy. +* **Confidence:** Thoroughly test AI suggestions before committing them. -Despite careful sequencing and testing, sometimes a problematic change—perhaps a subtly flawed migration script suggested by AI or a complex manual error—slips into production, causing application errors or data corruption. In these high-pressure moments, you need a fast, reliable way to revert *both* the application code and the database state. Traditional approaches like complex rollback migrations or restoring from slow, potentially outdated `pg_dump` backups often mean extended downtime and unacceptable data loss. +Always create a dedicated Neon branch for testing AI-generated schema changes, data migrations, or complex features. This provides the isolation needed to experiment safely, validate thoroughly, and easily discard unsuccessful attempts, keeping your core database stable while you harness the speed of AI. -Neon's unique architecture, retaining a history of all database changes (the WAL), provides a powerful safety net: **[Point-in-Time Restore (PITR)](/blog/announcing-point-in-time-restore)**. Instead of complex manual reversals, you can treat the database's recent history as a timeline you can instantly rewind. If a bad migration impacts your production database, first identify the issue and determine the precise time *just before* the faulty migration was applied, perhaps using deployment logs or Neon's [Time Travel](/docs/guides/time-travel-assist) feature to query past states directly in the console. Then, execute the PITR operation from the Neon Console's **Restore** section, selecting the target timestamp. Neon completes the restore in **just a couple of seconds**, bringing the database state cleanly back to the desired point before the error occurred. +## Automate migration safety with Neon MCP server & Cursor -Crucially, this database rollback must be paired with **rolling back your application code** to the previous working version that corresponds to the restored database schema. Once both are reverted, verify the application's functionality and data integrity thoroughly. This streamlined process, facilitated by Neon's instantaneous PITR, transforms rollbacks from a high-risk, slow procedure into a fast, predictable recovery mechanism. +Instead of manually creating branches to test AI-generated migrations, the **[Neon MCP Server](/docs/ai/neon-mcp-server)**, when used with an MCP-compatible AI tool like **[Cursor](https://cursor.com/)**, automates this safety check directly within your chat workflow. Cursor, powered by the Neon MCP Server, will use a temporary Neon branch to test migrations and ask for your approval before applying them to your main branch. -**The Vibe Coding Best Practice:** Leverage Neon's Point-in-Time Restore as your primary database rollback strategy. When a deployment goes wrong, use PITR to quickly revert the database state to a point just before the error, coordinated with deploying the corresponding application code version. This provides a critical safety net, giving you the confidence to iterate quickly with AI tools, knowing you have a robust and fast fallback for unforeseen issues without resorting to risky manual rollbacks or slow backup restores. +**Example: Adding a column to `customers` via Cursor Composer with Neon MCP Server** + +Let's walk through how Cursor and the Neon MCP Server handle a schema change request for an e-commerce application. + +* **Scenario:** In your `'ecom-prod'` Neon project, you want to add a nullable `last_ordered_at` timestamp column to your `customers` table to track when a customer last placed an order. + +* **Your interaction in Cursor Composer (with Neon MCP Server connected):** + + 1. **You (User):** + ``` + In my Neon project 'ecom-prod', I need to add a new column to the 'customers' table. + The column should be named 'last_ordered_at', it should be a timestamp with time zone, and it should allow NULL values for now. + ``` + + 2. **Cursor Composer (with Neon MCP server):** + ``` + Cursor: Understood. I'll help you add a nullable 'last_ordered_at' timestamp column to your 'customers' table in the 'ecom-prod' project. + + > Called MCP Tool (prepare_database_migration) + > I will now run the SQL to add the column on the temporary branch. + > Now, let me verify the column was added correctly to the 'customers' table schema on the temporary branch. + > Called MCP Tool (describe_table_schema using table 'customers' on temporary branch) + > The 'last_ordered_at' column (TIMESTAMP WITH TIME ZONE, NULLABLE) has been successfully added to the 'customers' table in a temporary branch. + > Temporary Branch Name: br-temp-migration-[some_hash] + > Would you like to commit this migration to your main 'customers' table in the 'ecom-prod' project and then clean up the temporary branch? + ``` + + 3. **You (User):** + ``` + Wait, let me run some tests on the temporary branch first. Please provide me with the connection string for the temporary branch. + ``` + + 4. **Cursor Composer:** + ``` + Cursor: Sure! You can run your tests on the temporary branch. Here's the connection string: + > Called MCP Tool (get_connection_string) + > Connection String: neon://[your-connection-string-for-temporary-branch] + > Here's the connection string for the temporary branch. You can use it to connect and run your tests. + ``` + + 5. Once you run your tests, you can return to Cursor and let it know the results. + + 6. **You (User):** + ``` + I ran my tests, and everything looks good. Please apply the migration to the main branch. + ``` + + 7. **Cursor Composer:** + ``` + Cursor: Great. I will now apply the migration to your main branch in 'ecom-prod' and then clean up the temporary resources. + > Called MCP Tool (complete_database_migration) + > The migration has been successfully completed! The 'last_ordered_at' column has been added to your 'customers' table in the 'ecom-prod' project, and the temporary branch has been cleaned up. + ``` + +### Getting started with Neon MCP Server + +Follow the instructions in the [Neon MCP Server documentation](/docs/ai/neon-mcp-server) to set up the Neon MCP Server for your AI client. + +### Benefits of Using Neon MCP Server within your AI Tool + +* **Integrated safety:** The "test on a branch" best practice is built into the AI interaction, preventing accidental changes to your database. +* **Transparency:** Tools like Cursor typically show the MCP tool calls, so you see what operations the AI is performing on your Neon project. +* **User control:** You provide the final "go-ahead" before any changes are made to your main production (or development) branches. +* **Reduced risk:** Minimizes the chance of applying AI-generated migrations that are flawed or have unintended consequences on critical tables. + +## Database-level security for AI-generated applications + +AI tools can accelerate feature development, but ensuring correct data authorization within AI-generated code is challenging. Relying on application-level checks for every query increases the risk of errors and data leaks. + +### Problem: Application-level authorization is error-prone with AI + +AI might generate code that forgets or incorrectly implements authorization checks: + +* **Typical application-level check:** + ```typescript + // Inside an API endpoint... + async function getProject(projectId: string) { + const userId = await getCurrentUserId(); + if (!userId) throw new Error("Unauthorized"); + + // AI needs to remember to add this owner_user_id check every time and one mistake can expose data + const query = `SELECT * FROM projects WHERE id = $1 AND owner_user_id = $2`; + const { rows } = await db.query(query, [projectId, userId]); + + return rows; + } + ``` + Forgetting the `AND owner_user_id = $2` check exposes data. + +### Solution: Define authorization policies directly in the database using Neon RLS + +[Neon RLS](/docs/manage/neon-rls) leverages Postgres Row-Level Security (RLS) and integrates with JWT authentication providers (like Clerk, Auth0, etc.). Neon automatically validates the JWT and makes the user's ID available within SQL via the `auth.user_id()` function. You define policies that use this function to control row access centrally. + +* **Define RLS policy (example using drizzle):** + Place RLS definitions alongside your schema (e.g., in `schema.ts`). Drizzle's `authUid` helper simplifies using `auth.user_id()`. (See [Simplify RLS with Drizzle](/docs/guides/neon-rls-drizzle)). + ```typescript + import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; + import { crudPolicy, authenticatedRole, authUid } from 'drizzle-orm/neon'; + + export const projects = pgTable('projects', { + id: uuid('id').primaryKey().defaultRandom(), + name: text('name').notNull(), + owner_user_id: uuid('owner_user_id').notNull(), + }, + (table) => [ + // Apply RLS policy using Drizzle's crudPolicy and authUid helper + crudPolicy({ + role: authenticatedRole, + // Allow SELECT only if table's owner_user_id matches the JWT's user ID + read: authUid(table.owner_user_id), + // Allow INSERT/UPDATE/DELETE only if owner_user_id matches JWT user ID + modify: authUid(table.owner_user_id), + }) + ]); + ``` + This policy ensures *any* query against `projects` automatically filters by the authenticated user's ID at the database level. + +* **Querying with RLS:** + With the RLS policy handling authorization, your application code becomes simpler and safer. You can be rest assured that the database will only return rows the user is authorized to see. + ```typescript + async function getProject(projectId: string) { + const authToken = await getAuthTokenFromProvider(); // Get JWT via your auth provider helper + const db = drizzle(process.env.DATABASE_AUTHENTICATED_URL!); + + const result = await db + .$withAuth(authToken) // Pass JWT to Neon via Drizzle helper + .select() + .from(schema.projects) + .where(eq(schema.projects.id, projectId)); // optional, even if not provided, RLS will filter by user ID + + return result; + } + ``` + +### Getting started with Neon RLS + +1. **Choose Auth provider:** Select a JWT provider (Clerk, Auth0, etc.) that uses asymmetric keys (JWKS). See [Supported providers](/docs/guides/neon-rls#supported-providers). +2. Follow the [Neon RLS tutorial](/docs/guides/neon-rls-tutorial) to set up your database and define RLS policies. + +By enforcing authorization at the database level with Neon RLS, you create a strong security backstop. Even if AI generates imperfect application code, RLS helps prevent unauthorized data access, making your AI-assisted development process significantly safer. + +## Sequence deployments: Coordinate database and app changes + +Applying AI-generated database migrations and application code changes requires careful ordering to avoid downtime or errors. Deploying code that expects a database change before it's live, or making a schema change restrictive before the app is ready, will break your application. + +### Problem: Uncoordinated deployments cause runtime errors + +* **Example Failure:** Deploying application code that reads a new `users.last_login` column *before* the `ALTER TABLE ... ADD COLUMN last_login` migration has run will result in application errors. + +### Solution: Use a multi-stage deployment strategy + +Guide your AI (and your deployment process) through these distinct stages, especially when making potentially breaking changes like adding required columns. + +**Example Workflow: Adding a Required `user_role` Column** + +Assume you want to add a `user_role` (e.g., 'admin', 'member') to your `users` table and make it `NOT NULL`. + +* **Stage 1: Add Nullable Column (Backward-Compatible Schema Change)** + Apply a migration that adds the new column but allows `NULL` values. This doesn't break existing code. + * **Migration SQL (Generated by AI or manually):** + ```sql + ALTER TABLE "users" + ADD COLUMN "user_role" VARCHAR(50) NULL; + ``` + * **Deploy:** Apply this migration to your database. Existing application versions continue working. + +* **Stage 2: Deploy App v1 (Writes to New Column, Handles Nulls)** + Deploy application code that: + 1. Starts writing the `user_role` for new or updated users. + 2. Can read users where `user_role` might be `NULL` (treats `NULL` as a default role, e.g., 'member'). + * **Application Code Snippet (Conceptual):** + ```typescript + // Function to update user profile + async function updateUser(userId, data) { + const updateData = { ...data }; + // Start writing the new role if provided + if (data.role) { + updateData.user_role = data.role; + } + await db.update(users).set(updateData).where(eq(users.id, userId)); + } + + // Function to read user role + function getUserDisplayRole(user) { + // Handle cases where the role might still be NULL + return user.user_role ?? 'member'; + } + ``` + * **Deploy:** Deploy this application code. + +* **Stage 3: Backfill Data (Optional but often needed for `NOT NULL`)** + Run a script to populate the `user_role` for existing users who have `NULL`. + * **Backfill SQL:** + ```sql + UPDATE "users" + SET "user_role" = 'member' -- Or determine role based on other data + WHERE "user_role" IS NULL; + ``` + * **Deploy:** Run this SQL against your database. + +* **Stage 4: Deploy App v2:** + Deploy application code that now assumes `user_role` is always present. + * **Application code snippet:** + ```typescript + // Function to read user role (simplified) + function getUserDisplayRole(user) { + // Assumes user_role is now always populated + return user.user_role; + } + // Code creating users MUST now provide a user_role + ``` + * **Deploy:** Deploy this application code update. + +* **Stage 5: Make column `NOT NULL` (Forward-incompatible schema change)** + Apply a final migration to enforce the `NOT NULL` constraint, now that all data is populated and the application expects it. + * **Migration SQL:** + ```sql + ALTER TABLE "users" + ALTER COLUMN "user_role" SET NOT NULL; + ``` + * **Deploy:** Apply this migration to your database. + +### Guiding your AI for phased rollouts + +When using AI to generate migrations or application code, guide it through the phased rollout process. This ensures that each step is backward-compatible and that the application can handle changes gracefully. Guide your AI with prompts like: +* First, generate the SQL to add a *nullable* `user_role` column to `users`. +* Now, modify the user update function to write to `user_role` if provided, and update the display function to show 'member' if `user_role` is null. +* Generate the SQL to set `user_role` to 'member' for all users where it's currently null. +* Update the application code to assume `user_role` is never null. +* Finally, generate the SQL to make the `user_role` column `NOT NULL`. + +Adopting this phased rollout strategy minimizes deployment risks. Explicitly guide your AI through backward-compatible schema updates, application code adjustments, data backfills (if needed), and final schema enforcement to ensure your application remains stable and available throughout the update process. + +## Use Point-in-time restore (PITR) for fast rollbacks + +Even with careful sequencing, deployments can go wrong. An AI-generated application code might interact unexpectedly with the database, and introduce a bug corrupting data. In such cases, you need a reliable rollback strategy to revert to a known good state quickly. + +### Problem: Traditional database rollbacks are slow and complex + +Reverting a database to a previous state often involves restoring from `pg_dump` backups, which can take time and may not be granular enough. This can lead to extended downtime and potential data loss. + +### Solution: Use Neon's instant PITR + application code revert +Neon's **[Point-in-Time Restore (PITR)](/blog/announcing-point-in-time-restore)** leverages the database's history (WAL) to instantly rewind a branch to *any second* within your retention period. Combine this with reverting your application code for a fast, safe rollback. + +**Workflow for rolling back a failed deployment:** + +Imagine an AI suggested an application code change that caused a bug in production. + +1. **Identify the issue & rollback point:** + * Confirm the deployment failure. + * Determine the time just *before* the bad deployment, e.g., **14:29:59 UTC**. + +2. **Verify pre-deployment State:** + Use Neon's [Time Travel Assist](/docs/guides/time-travel-assist) to query the database state *as it was* at your chosen rollback time (14:29:59 UTC) *before* performing the actual restore. + * In Neon Console > Restore Tab: + * Select your `production` branch. + * Enter the timestamp (e.g., `YYYY-MM-DD 14:29:59 UTC`). + * Write a query: `SELECT COUNT(*) FROM orders;` + * Click **Run**. + * Verify the result matches your expectations (e.g., `1000` orders before the bad deployment). + +3. **Initiate database PITR:** Once confident in the timestamp, initiate the restore by clicking the **Proceed** button in the Neon Console. + +4. **Revert application code:** + Simultaneously, roll back your application code to the version deployed *before* 14:30 UTC. + * **Example Git command:** + ```bash + # Find the commit hash before the bad deployment + git log --before="YYYY-MM-DD 14:30:00 UTC" -n 1 + # Revert to the previous working state (or use your deployment system's rollback) + git checkout + # Re-deploy this application version + ``` + +5. **Verify system health:** + * Check application logs for errors. + * Run tests to ensure the application is functioning correctly. + +### Why Neon PITR is ideal for AI-assisted development + +* **Speed:** Restores complete in seconds, minimizing downtime. +* **Granularity:** Restore to any second within your history retention window. +* **Confidence:** Enables faster iteration with AI tools knowing a reliable, fast recovery option exists. + +Leverage Neon's Point-in-Time Restore as your primary database rollback strategy. When a deployment involving AI-generated changes goes wrong, use PITR to quickly revert the database state, coordinate it with deploying the corresponding application code version, and recover rapidly without significant data loss or complex manual interventions. ## Conclusion From e934c10be19f5a78eba8d20dd8b59debbd1cbfc9 Mon Sep 17 00:00:00 2001 From: Dhanush Reddy <29dhanushreddy@gmail.com> Date: Tue, 13 May 2025 13:16:41 +0530 Subject: [PATCH 3/3] docs: edit intro --- .../guides/best-practises-using-ai-with-neon.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/content/docs/guides/best-practises-using-ai-with-neon.md b/content/docs/guides/best-practises-using-ai-with-neon.md index 46ad33a359..eacd764739 100644 --- a/content/docs/guides/best-practises-using-ai-with-neon.md +++ b/content/docs/guides/best-practises-using-ai-with-neon.md @@ -5,12 +5,19 @@ enableTableOfContents: true updatedOn: '2025-04-28T00:00:00.000Z' --- -Generating code with AI using natural language, often called "vibe coding," provides significant speed advantages for development, especially during prototyping. You describe what you need, and the AI generates the code. +AI tools offer significant speed for generating code, including SQL and database interaction logic for your projects. While "vibe coding" - describing your needs in natural language is powerful for rapid prototyping, ensuring the reliability, security, and efficiency required for production applications demands a structured approach. -However, the reliability needed for production applications, particularly when managing databases, user data, and authentication, requires more than just speed. AI code generation, while powerful, can sometimes overlook critical details, security considerations, or database best practices. Incorrect SQL, inefficient queries, or insecure patterns can arise if the AI lacks sufficient context or guidance. +Without specific guidance and safety measures, AI-generated code can sometimes be generic and inefficient. This guide provides best practices to help you effectively use AI tools with Neon. You'll learn how to: -Consider this guide a set of best practices for using AI tools with Neon. These practices are designed to help you leverage the power of AI while ensuring that your code is maintainable, secure, and efficient. +* Provide clear context to your AI for precise code generation. +* Leverage Neon AI Rules to guide AI towards optimal, Neon-specific patterns. +* Use Neon Branching to instantly create isolated sandboxes for testing AI-generated database changes. +* Automate migration safety checks directly within your AI workflow using the Neon MCP Server. +* Enforce robust security at the database level with Neon RLS, even for AI-generated application logic. +* Sequence database and application deployments correctly to prevent runtime errors. +* Utilize Neon's Point-in-Time Restore (PITR) for fast and safe rollbacks. +By following these practices, you can confidently use AI to accelerate your development while maintaining standards for code quality, security, and maintainability. ## Provide clear context: Treat AI as a teammate