# vibe-coding-best-practices Awesome, I'll pull together the latest tips and ideas around "vibe coding" for AI development teams (focused on JS and Python ecosystems) and weave your bullets into a structured, engaging, Medium-style guide. I'll make it accessible but substantial enough to serve technical teams of mixed backgrounds. I'll get started and keep you posted! # AI Pair Programming and Vibe Coding: A Team Guide to LLM-Driven Development ## Introduction The rise of large language models (LLMs) has unlocked a new style of software development often nicknamed **“vibe coding.”** This approach shifts much of the coding workload to AI, letting developers (and even non-developers) describe goals in natural language and have the AI generate the code ([Vibe coding - Wikipedia](https://en.wikipedia.org/wiki/Vibe_coding#:~:text=Vibe%20coding%20,The%20term%20was%20introduced%20by)). In vibe coding, *the programmer’s role changes from writing every line by hand to guiding, testing, and refining AI-generated code* ([Vibe coding - Wikipedia](https://en.wikipedia.org/wiki/Vibe_coding#:~:text=Vibe%20coding%20,The%20term%20was%20introduced%20by)). This means that now everyone from seasoned engineers to non-coders can collaborate on software – as long as they follow good practices to keep the project on track. *An AI coding assistant (ChatGPT) generating a JavaScript function from a plain-English prompt. “Vibe coding” lets a developer describe a problem and get working code in return, shifting the human role to oversight and testing ([Vibe coding - Wikipedia](https://en.wikipedia.org/wiki/Vibe_coding#:~:text=Vibe%20coding%20,The%20term%20was%20introduced%20by)).* **Why do we need new guidelines?** Coding with AI is powerful but not magically easy. As developer Simon Willison notes, *“Using LLMs to write code is difficult and unintuitive… it takes significant effort to figure out… how best to apply them”* ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=Using%20LLMs%20to%20write%20code,how%20best%20to%20apply%20them)). If you assume the AI will perfectly implement your project without your guidance, *“you’ll quickly be disappointed”* ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=If%20you%20assume%20that%20this,skill%20you%E2%80%99ll%20quickly%20be%20disappointed)). The AI can produce incorrect or messy code if left unchecked – it’s essentially an **over-confident pair programming assistant** that works at light speed but will *“absolutely make mistakes – sometimes subtle, sometimes huge”* ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=to%20think%20of%20them%20as,on%20tedious%20tasks%20without%20complaint)). For a team with mixed experience levels, harnessing an LLM effectively requires process and discipline. This guide will walk through best practices for LLM-assisted development in a team setting. We’ll cover everything from planning your project and writing tests first, to using new AI coding tools (like Cursor or Windsurf) side-by-side with your IDE, to keeping the AI on a “tight leash” so it follows sound engineering practices. We’ll also discuss how non-coders can get involved (with UI-first tools like Replit and Lovable), strategies for debugging and version control with AI-generated code, and why it’s important to continuously refactor and experiment. The goal is to **enjoy the speed and creativity of vibe coding without losing the quality and reliability of good software engineering.** Let’s dive in! ## Map Out Goals and Constraints *Before* Coding Every successful project starts with a plan. When working with an AI, clear goals and explicit constraints are more important than ever. **Begin by mapping out what you want to build in plain English** – this could be a simple design doc or a Markdown file in your repo that the whole team (and the LLM) can reference. Define the project’s objectives, core features, and any requirements or restrictions. For example, list user stories or use cases, outline which technologies or APIs to use, and note constraints like “must support mobile web” or “data must be stored locally, not in the cloud.” By writing these down, you create a shared understanding that you can also feed into the LLM’s context so it *knows the rules* from the start. - **Write a “spec” in Markdown:** Many teams find it useful to maintain a `PLAN.md` or `SPEC.md` in the repository containing the project vision, feature list, and constraints. You can even draft this collaboratively with the LLM – ask it to “help outline the features for X” or review what you’ve written. Keep this document updated as goals evolve. Because it’s in the repo, everyone (including late-joining team members or a different AI model) can see what the current plan is. - **Define done & out-of-scope items:** In your plan, describe what a successful outcome looks like (e.g. *“User can sign up with email and see a personalized dashboard”*) and what is explicitly **out of scope** (e.g. *“No social media login for v1”*). Clearly stating constraints in natural language helps ensure the LLM doesn’t wander off implementing unneeded features. If you later prompt the AI, you can prepend these constraints (e.g. “Important: do not use any database other than PostgreSQL” or “Only consider solutions that run in the browser”) to keep it within bounds. - **Leverage the spec during prompting:** Once you have goals and constraints laid out, actually use them! For instance, when you ask the LLM to generate code, you might begin the prompt with a summary of the goal and relevant constraints: *“We’re building a Next.js app for a to-do list. It must use our existing FastAPI backend and OAuth2 for auth. The UI should be responsive. Now, please implement the signup page….”* Providing this context focuses the AI on what matters. It’s much easier to get correct output if the AI is grounded in your project’s specifics. - **Keep it updated:** Treat the plan as a living document. As you discover new constraints (say, an API has a rate limit, or a library doesn’t work as expected), note that in the spec. You (and the AI) can then adjust course accordingly. If something in the plan changes, inform the LLM in your next prompt or restart the session with the new info to avoid it following outdated instructions. Spending a bit of time up front on mapping the project can save tons of confusion later. It aligns human collaborators and gives the AI a north star to follow. Remember, an LLM is great at *following patterns* – so give it a solid pattern (your plan) to follow from the get-go. ## Leverage AI Pair-Programming Tools (Cursor, Windsurf, etc.) One of the best ways to code side-by-side with an LLM is to use specialized AI-enhanced development tools. Editors like **Cursor** and **Windsurf** (formerly Codeium) integrate the LLM into your coding environment ([Vibe code or retire | InfoWorld](https://www.infoworld.com/article/3960574/vibe-code-or-retire.html#:~:text=Vibe%20coding%20is%20a%20cute,Studio%20Code%20forks%20or%20plugins)) ([Why Windsurf is the Best Free AI Code Editor You've Never Heard Of](https://dev.to/dev_michael/why-windsurf-is-the-best-free-ai-code-editor-youve-never-heard-of-3ofj#:~:text=Why%20Windsurf%20is%20the%20Best,%E2%80%94where%20you%20can)). Think of them as souped-up IDEs where the AI is always on hand to suggest code, generate new files, or even fix bugs as you code. These tools can supercharge productivity – but you need to know how to dance with them. - **AI in your IDE:** Cursor and Windsurf are essentially forks or extensions of VS Code that embed an AI assistant ([Vibe code or retire | InfoWorld](https://www.infoworld.com/article/3960574/vibe-code-or-retire.html#:~:text=Vibe%20coding%20is%20a%20cute,Studio%20Code%20forks%20or%20plugins)). For example, Cursor lets you chat with an LLM about your codebase, and Windsurf offers a “Write Mode” where you can literally *write and generate files directly from prompts* in the editor ([Why Windsurf is the Best Free AI Code Editor You’ve Never Heard Of - DEV Community](https://dev.to/dev_michael/why-windsurf-is-the-best-free-ai-code-editor-youve-never-heard-of-3ofj#:~:text=editor%20commands%2C%20new%20editors%20like,would%20in%20a%20traditional%20assistant)). Instead of switching to a separate browser tab (like ChatGPT) and copy-pasting code, these tools keep the conversation in your coding context. The AI can see your file structure and sometimes even open relevant files when needed. This is incredibly handy – it means the LLM can use more context about your project and you can quickly apply its suggestions. - **Autocompletion on steroids:** Much like GitHub Copilot or Tabnine, these editors give you AI autocompletions as you type, but often with bigger context windows and more “whole function” suggestions. They can generate boilerplate code (config files, repetitive functions, tests) in seconds. For instance, as you start writing a function signature or a comment describing a function, the AI might complete the entire function for you. This feels like coding with an **“over-confident pair programmer who’s lightning fast”** at providing examples ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=to%20think%20of%20them%20as,on%20tedious%20tasks%20without%20complaint)). The speed is thrilling – you describe what you want and watch the code materialize. - **Ask, don’t just autocomplete:** Beyond inline suggestions, you can explicitly ask the AI to perform tasks. In Windsurf’s chat or Cursor’s command palette, you might say “Create a React component that displays a user profile card,” and it will generate a new file with that component. Or highlight a block of code and ask, “Refactor this to use async/await instead of promises.” The AI acts almost like a junior dev at your side. In fact, many developers now think of these LLMs as *“digital interns”* that type code for you based on detailed instructions ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=,to%20do)) ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=,valid%E2%80%94raising%20an%20error%20if%20not)). You control the high-level logic; the AI takes care of syntax and boilerplate. - **Stay in control:** While these tools feel magical, **don’t turn off your brain!** The AI might be integrated into your IDE, but *you* are still the lead developer. Always review the suggestions before accepting. If the AI writes a function, skim it for correctness and style. Because these models can and do produce errors (even nonsense), treat them as helpful but **fallible assistants**. As one author quipped, the current crop of code generation tools are impressive but “all bad” in some ways ([Vibe code or retire | InfoWorld](https://www.infoworld.com/article/3960574/vibe-code-or-retire.html#:~:text=Vibe%20coding%20is%20a%20cute,Studio%20Code%20forks%20or%20plugins)) – meaning they aren’t perfect, and unchecked outputs could introduce bugs or security issues. Use the AI’s speed, but double-check its work like you would review a junior dev’s code. - **When to involve AI (and when not to):** These assistants excel at rote tasks and exploration. Generating a bunch of model classes, stubbing out API clients, converting a JSON to TypeScript types – perfect. They’re also great at answering “how do I…?” with code examples. But for critical architecture or tricky logic, you might want to do some thinking yourself and then instruct the AI with a clear plan (we’ll discuss how to guide the AI in the next section). And if you have a non-coder teammate working with you, consider pairing them with a UI-driven tool like Lovable or Replit, which we’ll cover later. In short: use AI tools for what they’re good at, but **you set the direction**. Vibe coding with integrated tools can feel like the future – one pundit even declared *“vibe coding is how we will all write code in the future. Start learning it now, or your career… will end.”* ([Vibe code or retire | InfoWorld](https://www.infoworld.com/article/3960574/vibe-code-or-retire.html#:~:text=Vibe%20coding%20is%20how%20we,a%20software%20developer%20will%20end)) (Bold claim, but it captures how significant this shift might be!) By embracing AI pair-programming tools now, your team can prototype faster and automate tedious work. Just remember to keep a critical eye on the AI’s contributions. ## Guide the LLM with Clear Prompts and Constraints An LLM will do exactly what you ask – **and that’s why you have to be very clear about what you want.** When working with AI, the old saying “garbage in, garbage out” definitely applies. Crafting good prompts and setting boundaries for the AI’s output is a key skill for LLM-assisted development. Here’s how to guide your AI collaborator: - **Be specific and “authoritative” in prompts:** Don’t be shy – *tell the AI exactly what to do*. After some initial experimentation, you should switch into a more directive mode, treating the LLM like an intern who needs detailed instructions ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=,to%20do)) ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=,valid%E2%80%94raising%20an%20error%20if%20not)). For example, instead of asking *“Can you help with user auth?”*, break it down: *“Implement a function `register_user(email, password)` that creates a new user in the database. Use bcrypt to hash passwords and return a JWT token on success.”* The more precisely you describe the desired outcome (function names, libraries, error handling), the closer the AI’s first attempt will be. As Willison notes, LLMs respond well to clearly stated function signatures and requirements – you act as the designer, and the AI fills in the implementation ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=,as%20we%20notice%20that%20problem)) ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=I%20find%20LLMs%20respond%20extremely,the%20body%20to%20my%20specification)). - **Constrain the “output zone”:** When possible, scope the AI’s work to a specific part of your code. If your tool allows, you might highlight a section for it to edit, or open a specific file and say “only modify this file.” If you’re using a chat interface, you can delimit sections of code in your prompt and ask it to operate within those. For example: *“Below is `index.html`. Add a meta viewport tag in the head section and nothing else:\n```html\n…code…```”*. By explicitly limiting what the AI should touch or produce, you avoid it spitting out unrelated files or changes. This helps prevent the AI from *“hallucinating” new file names or making redundant files* that you never asked for ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,and%20modular%20code%20got%20harder)). Keeping it focused also makes it easier to verify the output. - **Watch out for loops and off-track outputs:** Sometimes the AI might get stuck or go in circles – for instance, repeatedly generating the same chunk of code or failing to satisfy a requirement and looping on it. It may even invent functions or files that don’t exist (a hallucination). Pietro, who experimented with fully automated vibe coding, noted that *“the models got stuck in loops or hallucinated file names”*, and it took very *“verbose prompts to get back on track.”* ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,level.%20Very%20OCD%20triggering)). The lesson: **monitor the AI’s output critically.** If you notice nonsense or repetition, intervene. Don’t just hit “retry” blindly; instead, rephrase your instructions or break the task into a smaller chunk. For example, if “Build my entire app” yields chaotic results, try “Let’s start by setting up the database schema” first. You can always build piece by piece. And if a conversation has gone truly off the rails, don’t hesitate to **“wipe the slate clean and start again”** – often the best fix is beginning a fresh session when the current thread stops being useful ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=When%20you%20start%20a%20new,slate%20clean%20and%20start%20again)). - **Use an iterative, conversational approach:** Remember that interacting with an LLM is a back-and-forth dialogue, not a one-shot command. You can always ask for refinement. If the code it generated isn’t up to par, say so and guide it: *“The code works but isn’t using our utility function for logging. Please refactor to use `log_info()` instead of printing.”* The AI won’t get offended – in fact, *it’s happy to rewrite code multiple times* until you’re satisfied ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=)). This iterative refinement is where a lot of the power lies. You can incrementally steer the AI: first get a rough solution, then improve naming, then optimize performance, and so on. Treat it like a junior dev who doesn’t mind being asked to rewrite the same thing 10 times. That said, keep an eye on context length – long sessions can lead the model to lose focus or start mixing up details. If that happens, consolidate what you have and start a new chat with a summary of the current state for further changes. - **Consider an “LLM instructions” file:** For complex projects or long engagements with an AI, some teams create a persistent **instructions file** outlining overall conventions. This might include the coding style guide, architectural patterns to follow, or reminders like “Always include error handling for database operations” or “Use our team’s utility library for HTTP requests instead of raw fetch.” You can then feed chunks of this file into the AI’s context at the start of each session (or when needed) to enforce consistency. Essentially, it’s like giving the AI an employee handbook. This isn’t foolproof (the AI might still deviate), but it can help maintain standards across multiple prompts and even across different AI models. Think of it as a way to encode *“good engineering practices”* that you want the LLM to follow. Many emerging “AI engineering” tools encourage saving such context so you don’t have to repeat yourself each time. Guiding the LLM with clarity and constraints is crucial. As Andrej Karpathy – one of the pioneers of vibe coding – suggested, you need to keep the AI on a **“tight leash”** to rein in its tendency to produce BS or wander off-track. The better you are at communicating *exactly* what you need (and nothing more), the better your AI pair programmer will serve you. ## Embrace Test-Driven Development (Yes, Even with an AI) When you have an AI that can spit out code in seconds, it’s tempting to just build features rapidly and “see if it works.” But this can lead to fragile code and difficult debugging down the line. A smarter approach is to **write tests first or in parallel** – essentially, apply classic test-driven development (TDD) principles in your LLM-assisted workflow. In fact, having the AI help with testing is one of the best ways to ensure it’s producing *correct* code. Remember: *“You have to test what it writes!”* ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=,what%20it%20writes)) – you cannot fully outsource the responsibility of correctness to the machine. - **Start with integration tests or end-to-end goals:** Begin by describing (and coding) how the system should behave from the user’s perspective. For a web app, that might be an integration test simulating user actions (or at least a high-level function call demonstrating usage). You can ask the LLM to help draft these tests. For example: *“Write a pytest test case for the `register_user` function, checking that it hashes the password and returns a valid JWT.”* By writing a test first, you clarify the acceptance criteria for the feature. The AI can often produce a reasonable test based on your description. This not only gives you a starting point for implementation but also serves as a crystal-clear spec for the AI when it writes the code to make the test pass. - **Have the AI generate unit tests for its own code:** A neat trick is to **ask the LLM to produce tests immediately after it writes a piece of functionality.** As soon as you get a code snippet from the AI (say a new module or function), prompt: *“Now write me the tests for this.”* As Willison shares, he often follows up code generation with exactly that instruction – e.g., *“Now write me the tests using pytest”* – so the AI saves him the effort of typing out the test cases he had in mind ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=I%20find%20LLMs%20respond%20extremely,the%20body%20to%20my%20specification)) ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=I%E2%80%99ll%20often%20follow,sitting%20in%20my%20head%20already)). The AI might catch things you didn’t mention explicitly (like edge cases) and include them in tests. Plus, if the AI’s code was slightly wrong or incomplete, writing tests can expose those gaps. It’s pretty satisfying when the AI writes code and then writes a test suite, which you can run immediately to see green or red. - **Don’t skip running the tests:** Generating tests is great, but actually **running** them is what matters. Make sure you execute the tests in a real environment (your machine, CI, or Replit, etc.) to verify that everything passes. Often you’ll find some failing cases – which is totally fine. This is the moment to debug (with the AI’s help if needed) or to have the AI fix its code until the tests all pass. The mantra here is: *if you haven’t seen the code run, it’s not done* ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=I%20wrote%20about%20this%20at,that%20the%20code%20actually%20works)). A test suite gives you that confidence quickly. - **Use tests as a safety net for refactoring:** As you iterate with the AI, you’ll likely want to refactor code (improve structure, rename things, optimize). Good tests give you the freedom to refactor continuously. If the AI suggests a refactor (for example, “Let’s break this into two functions”), you can say “sure” and know that your tests will catch any regression. Similarly, if you decide to let the AI attempt a different approach to a problem, you can always fall back to the last version if the new one fails the tests. This is especially important given that AI-generated code can sometimes work in one context and fail in another. With tests, you’ll detect those issues sooner rather than later. - **Integration tests with the LLM’s help:** Beyond unit tests, you can involve the AI in writing integration or end-to-end tests. For example, in a Next.js + FastAPI project, you might ask for a **FastAPI test client script** that starts the server and hits a few endpoints, or a **Playwright test** that opens the web app and simulates a login flow. The AI can stub these out quickly. You will likely need to tweak things (since external interactions can be tricky to simulate perfectly), but having the AI draft the structure is a big time-saver. And running those tests will tell you if the whole system (frontend + backend) is wired together correctly – which is something that pure unit tests or the AI’s code generation might not guarantee. One common observation from “pure vibe coding” experiments is that the code quality without tests tends to be poor. Pietro’s all-LLM app, for instance, ended up with *“no tests”* and *“definitely not production-level”* code ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,level.%20Very%20OCD%20triggering)). We don’t want that for serious projects. By insisting on tests and using the AI to assist in writing them, you effectively **make the LLM follow good engineering practices**. It turns the development into a dialogue: *“Here’s a test, AI. Make the code pass it.”* This keeps the AI accountable to a known outcome and prevents it from introducing too many sloppy hacks. In short, **TDD + LLM = sanity**. You get the rapid coding benefits of the AI, with a safety harness to catch mistakes. ## Debugging Methodically with the AI as Assistant Even with tests and planning, bugs and errors are inevitable. Debugging in an LLM-powered workflow is a joint effort between you and the AI. The key is to stay **methodical**: don’t panic-edit code. Instead, use a structured approach to diagnose the problem, and consider leveraging the AI to brainstorm possible causes and solutions. - **Examine error messages and outputs first:** When something fails – whether it’s a failing test or a runtime error – take a deep breath and **read the error carefully**. This might sound obvious, but with AI code you might be tempted to immediately ask the AI “fix this bug.” Instead, treat it like a normal bug: what is the stack trace or symptom telling you? Try to localize the issue. For example, if a test says “expected X but got Y,” think about where in the code that discrepancy could originate. Only once you have a grasp of the failure should you involve the AI for help. - **Ask the AI for debugging insight:** One great use of an LLM is as a rubber-duck debugging partner. You can copy-paste the error message or the relevant code section into the prompt and ask something like: *“This test is failing with XYZ error. What are some possible reasons?”* The AI can be very good at recalling typical causes for errors and suggesting avenues to check. It might list, say, five potential causes for a NullPointerException or logic bug. This helps you systematically consider things you might have overlooked. Essentially, you’re using the AI to **generate hypotheses** for the bug. - **Fix bugs step by step:** Once you have some theories, tackle them one at a time. You can certainly ask the AI to suggest a fix – e.g., *“Given this failing test and code, suggest a fix for the function”*. Often it will provide a patch or a revised function. Make sure you understand the suggestion before applying it blindly. Compare it to your own thinking: does it address the root cause or is it just silencing the symptom? If it’s not clear, you can even ask the AI to explain its proposed fix. After applying a fix (AI-suggested or your own), run the tests again or reproduce the scenario to see if the issue is resolved. - **Be ready to iterate and explore multiple fixes:** If the first attempt doesn’t solve it, don’t despair – debugging is usually an iterative process. It might take a few rounds of back-and-forth with the AI. Each time, narrow down the possibilities. For example, if you tried a fix for cause A and it didn’t work, tell the AI: *“I tried X and it didn’t solve the issue; what else could it be?”* This way you systematically eliminate wrong guesses. Keep notes (mental or written) of what you’ve tried to avoid going in circles. The AI can occasionally suggest the same fix again in different words – if you recognize this, steer it: *“We already tried that and it failed. What about the database schema – could that be the issue?”* - **Use the AI to verify your understanding:** Another debugging strategy is to explain the code or problem to the AI (much like you’d explain to a coworker) and see if it spots a flaw in your reasoning. For instance, *“I expected this loop to terminate when X, but it’s not. Here’s the code… I think the condition is correct. Can you spot any logical error?”* Sometimes describing the problem helps you solve it yourself; if not, the AI’s fresh perspective might catch that you, say, forgot an `await` or used the wrong variable in a comparison. - **Copy-paste carefully:** When sharing code or errors with the AI, give it just enough to be useful. You might not want to dump your entire codebase in the prompt – that can overwhelm context and also risk sharing sensitive info. Instead, isolate the snippet related to the bug (plus any usage example if relevant) and the error text. By minimizing extraneous info, you help the AI focus on the bug at hand. Also, if your error references specific lines, include those lines so the AI has the right context. - **Recognize AI limitations in debugging:** LLMs are good at common patterns, but sometimes a bug is too specific or involves nuance the AI doesn’t “truly” understand. Karpathy himself noted that AI tools often fail to truly grok or fix certain bugs, requiring him to *“experiment with unrelated changes until the problems are resolved”* ([Vibe coding - Wikipedia](https://en.wikipedia.org/wiki/Vibe_coding#:~:text=descriptions%20rather%20than%20manually%20writing,5)). In practice, this means if neither you nor the AI can figure it out quickly, try a different approach: add more logging to see what’s happening, simplify the failing code to isolate the issue, or even take a break and return with fresh eyes. Use the AI as a helper, but know when to take over and drive the investigation using your own debugging skills. After all, *at the end of the day, understanding and ensuring the software works is the developer’s responsibility* ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=I%20wrote%20about%20this%20at,that%20the%20code%20actually%20works)) ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=Your%20responsibility%20as%20a%20software,strengthening%20those%20manual%20QA%20habits)). By debugging methodically with the AI’s assistance, you turn a potentially frustrating process into a more structured, even educational, experience. The AI can help you reason through the problem, but it’s your steady guidance that will lead to the solution. Many developers find that involving the AI in debugging actually sharpens their own understanding, because it forces them to clearly articulate the problem and consider multiple solutions – a very good habit in any case. ## Enforce Code Quality and Best Practices One risk of letting an AI generate lots of code is that you might end up with a codebase that “works” but is messy or inconsistent in style. In a team setting, especially, you want to maintain readability and good engineering hygiene. The good news is you can make the LLM an ally in this by instructing it to follow best practices and by using your normal quality tools (linters, formatters) alongside the AI. Here’s how to keep the code quality high: - **Establish style guidelines upfront:** Decide on the basics like naming conventions, code formatting, and project structure, and inform the AI of these preferences early. For example, if you’re coding in Python and your team uses snake_case for variables and Black for formatting, mention this: *“Use snake_case for variable and function names. Keep lines under 80 chars.”* The AI will usually adhere to that if told. If it starts deviating (maybe the model has its own idea of style), gently correct it: *“Rename these variables to follow snake_case (e.g. userName -> user_name).”* Consistently reinforcing this will result in more uniform output. - **Make the AI write docstrings and comments:** Good documentation is a best practice you shouldn’t abandon with AI coding. In fact, LLMs are quite capable of producing docstrings and code comments – they often do so unprompted if you ask for a function. Encourage this! If the AI gives you a function without a docstring, you can prompt: *“Add a one-line docstring explaining the function.”* Some AI-generated code will even include docstrings and inline comments by default (especially if the model was trained on well-documented code). Embrace that, and edit for accuracy as needed. Over time, this habit trains the AI that you expect documented code. - **Use linters and formatters – and let the AI fix issues:** Run your linter (like ESLint for JavaScript/TypeScript or flake8 for Python) on AI-written code. If it flags problems (unused variables, undefined references, style issues), feed those back to the AI: *“Our linter says X is defined but not used; please fix that.”* or *“Reformat this code to satisfy PEP8.”* The AI can apply these fixes quickly. This saves you manual cleanup and also subtly teaches the model what is acceptable in your project. Treat the AI like a junior dev who needs to adhere to the team’s coding standards – you might even say “please run the equivalent of ESLint and fix any issues” and it will try to comply. - **Encourage sensible architecture:** LLMs don’t always naturally produce the cleanest architecture – they might cram too much into one function or create odd abstractions. So guide the design. For example, if you notice the AI wrote a giant function doing multiple tasks, you can prompt: *“Refactor this function into smaller helper functions for clarity – one for validating input, one for processing, etc.”* The AI will split the code. Similarly, if it introduced global state and you prefer dependency injection, you can instruct it to rewrite using passed parameters instead. Essentially, don’t accept a subpar design; ask the AI to improve it. It *will* do things like remove duplication or add a layer of abstraction if you tell it to. This is how you make the AI follow solid engineering practices rather than just churning out whatever works. - **Periodically review and refactor with the team:** It’s good practice to do code reviews among human team members even if an AI wrote the code. Set aside time to go through the AI-generated code, perhaps on pull requests, and ensure it meets your standards. If something is confusing or hacky, mark it and have the AI or a developer refactor it. This ensures that over time your codebase remains maintainable. A danger of relying on AI suggestions is accumulating technical debt unknowingly – regular review fixes that. You can even include the AI in the review by asking it to explain certain code or by using it to generate a refactored version for comparison. But always add your human judgment. One interesting thing experienced developers report is that good LLMs can actually *force you to up your game* on best practices. A well-tuned model might output code that catches exceptions diligently, uses proper patterns, and includes types and docs – essentially being *“much less lazy”* than some of us humans about the boring stuff ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=HTTP%20library%E2%80%9D%20if%20you%20can%E2%80%99t,the%20top%20of%20your%20head)) ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=The%20good%20coding%20LLMs%20are,code%20with%20the%20relevant%20types)). Use that to your advantage! Let the AI instill some discipline, and in areas where it falls short, correct it and enforce the rules. Over multiple iterations, the AI will adapt to the patterns you reinforce. The result: a codebase that doesn’t just *work*, but is also clean, understandable, and a joy for the whole team to collaborate on. ## UI-First Prototyping for Everyone (Replit and Lovable) Not everyone on your team may be comfortable writing code or diving into VS Code with Cursor. That’s okay – there are AI development platforms that cater to **non-coders and designers**, allowing them to participate in the creation process through a more visual or conversational interface. Two notable ones are **Replit** and **Lovable**: - **Replit: rapid prototyping in the browser:** Replit is an online IDE that’s great for quickly spinning up apps and sharing them. It now integrates AI (their Ghostwriter tool) which means you can code in a browser with AI assistance and then instantly show the results to others. For a team, this is fantastic for UI-first development. A designer or PM can sketch out an idea in Replit (with or without writing code) and use the AI to flesh it out. You could literally say, *“Create a simple web page with a form and a submit button that calls an API”* in the Replit chat and watch it generate the HTML/CSS/JS. Replit’s live preview link can then be shared so even non-technical team members can see and play with the prototype. The threshold to experimentation is super low – no setup, just a link. This empowers non-coders to take initiative and test ideas, with the AI handling the coding heavy lifting behind the scenes. - **Lovable: “AI full-stack engineer” for no-coders:** **Lovable** (lovable.dev) is a new platform that took the no-code world by storm in late 2024. Its pitch: *“Idea to app in seconds, with your personal full-stack AI engineer.”* Unlike code-centric tools like Cursor, Lovable is aimed at *anyone* – you just chat about what app you want, and it builds it. It became famous for letting people create production-looking software without writing code (it even hit the front page of Hacker News by enabling anyone to build apps via prompting) ([Sweden's Lovable, an app-building AI platform, rakes in $15M after spectacular growth | TechCrunch](https://techcrunch.com/2025/02/25/swedens-lovable-an-app-building-ai-platform-rakes-in-16m-after-spectacular-growth/#:~:text=Using%20generative%20AI%20to%20create,world%20inhabited%20by%20trained%20engineers)) ([Sweden's Lovable, an app-building AI platform, rakes in $15M after spectacular growth | TechCrunch](https://techcrunch.com/2025/02/25/swedens-lovable-an-app-building-ai-platform-rakes-in-16m-after-spectacular-growth/#:~:text=Lovable%2C%20a%20Swedish%20AI%20startup%2C,A%20round%20led%20by%20Creandum)). By early 2025 it amassed **500,000 users and was generating 25,000 new apps per day**, reaching an annual recurring revenue of $17M in just a few months ([Sweden's Lovable, an app-building AI platform, rakes in $15M after spectacular growth | TechCrunch](https://techcrunch.com/2025/02/25/swedens-lovable-an-app-building-ai-platform-rakes-in-16m-after-spectacular-growth/#:~:text=Lovable%20enables%20anyone%20to%20build,scaling%20to%2030%2C000%20paying%20customers)) ([Sweden's Lovable, an app-building AI platform, rakes in $15M after spectacular growth | TechCrunch](https://techcrunch.com/2025/02/25/swedens-lovable-an-app-building-ai-platform-rakes-in-16m-after-spectacular-growth/#:~:text=Lovable%20enables%20anyone%20to%20build,scaling%20to%2030%2C000%20paying%20customers)). Those numbers are insane – clearly there is demand for letting non-engineers build software through AI. In practice, Lovable works a bit like talking to ChatGPT but with a UI preview. You might say, “I need an e-commerce store with a product gallery and a checkout” – and it will scaffold it out. Non-coders can then tweak text or upload images, and ask the AI to adjust colors or layout. Lovable’s “AI agent” handles connecting the pieces, and it even claims to produce *“production-ready”* code on the backend ([Sweden's Lovable, an app-building AI platform, rakes in $15M after spectacular growth | TechCrunch](https://techcrunch.com/2025/02/25/swedens-lovable-an-app-building-ai-platform-rakes-in-16m-after-spectacular-growth/#:~:text=Lovable%20enables%20anyone%20to%20build,scaling%20to%2030%2C000%20paying%20customers)). - **How teams can use these UI-first tools:** The great thing about platforms like Lovable is that they lower the barrier to entry. Your UX designer or product manager can start a project there to **flesh out ideas in a visual way**. They don’t need to know about Next.js or FastAPI – they just describe what they want. Once a prototype is in place, your developers can take that and either integrate it with more complex systems or at least use it as a reference implementation. Alternatively, you might keep the project in a tool like Lovable if it’s meeting the needs; code-oriented devs can contribute by writing custom code for the tricky parts and injecting it into the project (Lovable does allow code editing for advanced sections). This kind of platform also ensures that things like responsive design, basic SEO (meta tags, favicons, etc.), and deployment are handled out of the box – relieving the team of a lot of boilerplate concerns. - **A word of caution on complexity:** UI-first AI tools are amazing for prototypes and MVPs. However, if your project has very custom logic or needs fine-grained control, you might hit their limits. In Lovable, you’re somewhat constrained to what its AI can figure out and the frameworks it uses under the hood. There may come a point where you need to export the code and move to a traditional codebase for ultimate flexibility. Luckily, many such platforms allow exporting projects. So one strategy is: **use no-code AI platforms for initial development and user testing**, then later **import the project into your team’s code repo for further development** when it gets more complex. This hybrid approach lets non-coders participate early and ensures a smooth transition to a maintainable codebase as the project grows. By incorporating tools like Replit and Lovable, you make AI development a team sport that’s not limited to those who can write Python or JavaScript. These tools exemplify the idea of vibe coding for everyone – in the words of Simon Willison, *“everyone deserves the ability to automate tedious tasks… vibe coding shaves that initial barrier down to almost flat.”* ([Not all AI-assisted programming is vibe coding (but vibe coding rocks)](https://simonwillison.net/2025/Mar/19/vibe-coding/#:~:text=If%20vibe%20coding%20grants%20millions,not%20be%20happier%20about%20it)) ([Not all AI-assisted programming is vibe coding (but vibe coding rocks)](https://simonwillison.net/2025/Mar/19/vibe-coding/#:~:text=proficient%20software%20developers,barrier%20down%20to%20almost%20flat)) When a non-coder can literally create a functional app by chatting with an AI, it’s a testament to that principle. Your job as the development team is to facilitate this collaboration, ensure the results are integrated into the larger product vision, and add the engineering rigor when needed. The future of development is looking a lot more inclusive and “multiplayer,” and embracing these UI-first AI tools can give your team a huge boost in productivity and creativity. ## Version Control Strategies: Branch, Reset, and Experiment Working with an AI assistant can lead to rapid iterations – you might generate code, then generate a different approach, then another, all in a short span. If you’re not careful, your project history can become a tangled mess. That’s where **version control discipline** comes in. By structuring your git usage around AI sessions, you can keep things manageable, undo mistakes easily, and compare different solutions. Here are some strategies: - **Use a new branch for each major AI-assisted session or feature:** Instead of having one long-running branch where you and the AI dump everything, create separate branches for distinct tasks or attempts. For example, if you’re implementing a new feature with GPT-4’s help, do it on `feature/new-feature-gpt4`. If you want to see how Claude would do it, maybe try `feature/new-feature-claude`. This way, your main branch (e.g. `main` or `dev`) stays stable until you have something solid to merge. It also means if one approach fails spectacularly, you can just abandon that branch and start a fresh one, without cluttering the main line of development. Think of branches as your sandbox for experiments – cheap and easy to throw away if needed. - **Commit early and often:** Treat AI code generation like you would normal development in terms of committing progress. Even though the AI might generate a ton of code at once, break it into logical commits. For instance, after the AI scaffolds a new module, run tests and then commit *“Add user auth module (AI generated) – passes basic tests.”* Frequent commits give you safe points to return to if something goes wrong later. They also document the evolution of the code, which is useful for code review and for your own memory. If an AI suggestion introduces a bug that you only catch later, you can `git bisect` to find which commit did it – and each commit being small helps pinpoint the issue. - **Leverage `git reset --hard` (or discard changes) when needed:** One advantage of having recent commits is that you can quickly **undo** to a known good state if the AI’s latest output is garbage. Let’s say you ask the AI to refactor a component and it makes things worse. If you haven’t committed those changes yet, you can simply not stage them, or if you did commit and regret it, use `git reset HEAD~1` to pop that commit off. In more drastic cases, if you find the whole branch has gone astray, you might scrap it and create a new branch from the last stable commit. Frequent saves (commits) and the willingness to reset will save you from “bad code pile-up.” It’s a bit like having a time machine – don’t be afraid to use it. The AI won’t be offended if you revert its changes! In fact, Simon Willison mentions that often the fix for a stuck AI conversation is to start fresh ([Here’s how I use LLMs to help me write code](https://simonwillison.net/2025/Mar/11/using-llms-for-code/#:~:text=When%20you%20start%20a%20new,slate%20clean%20and%20start%20again)) – the same applies to code: sometimes a clean slate (or a revert to earlier state) is the best path forward when confusion sets in. - **Clean up after a solution is found:** Once you do arrive at a working solution (say the feature is implemented and tests are green), consider cleaning up the commit history before merging to main. You might squash some experimental commits or rebase to a simpler story. Also, delete any branches that were just exploratory. This keeps your repository history tidy. The reason this matters is that AI-assisted commits can sometimes be noisy (lots of “tried X, reverted Y” commits if you saved every experiment). It’s fine in the moment, but when future maintainers look at history, you want them to see a coherent progression. So you might merge with a clean commit message summarizing what was done, and not include every false start in the main history. Think of it as curating the history after the fact. (You still have the full history on the branch if you ever need to dig into it.) - **Compare different model branches (optional):** If you did explore a feature with multiple models (e.g., one branch with GPT-4’s approach, another with a smaller model or different strategy), it can be illuminating to do a diff between those branches. You might find one model’s code is more efficient or simpler. In some cases, you might even manually merge the best parts from each. Using branches per model gives you this flexibility to A/B test AI solutions. Just be careful to reconcile differences before final merge – you don’t want duplicated logic or diverging styles in your main code. Ideally, pick one approach (maybe whichever had fewer bugs or aligns more with your expectations) and carry that forward. Overall, treat your version control as a safety net and experiment log. The AI can generate “bad code layers” if you keep piling changes on changes without resets – a phenomenon where mistakes compound. By branching and resetting liberally, you **prevent the accumulation of cruft**. You’re effectively saying: “Let’s try a fresh approach, but keep the good bits we learned.” This mirrors how you might talk to the AI itself (“Let’s start over and do it this way...”), but here you’re doing it with your codebase. A little discipline in using Git will go a long way to keep your AI-assisted project from descending into chaos. ## Managing Multiple Models and Logging Progress In 2025, we have a variety of AI models at our disposal – from OpenAI’s GPT-4, to Anthropic’s Claude, to open-source models and beyond. Each has its own strengths and quirks. A savvy AI developer will **use the right model for the task**, and sometimes that means switching models mid-project or running them in parallel to see which performs better. Additionally, with so much AI-generated content, it’s important to log what was done to avoid confusion later. Here’s how to handle multiple models and maintain a good record: - **Switch models when stuck or for specific tasks:** Not all LLMs are equal. For example, you might find GPT-4 is excellent at understanding complex instructions and writing well-structured code, but a model like Claude might be better at following a long conversation with lots of context (since Claude historically had larger context windows). If you hit a wall with one model – say it keeps giving you an incorrect solution – consider trying the prompt on another model. It’s like getting a second opinion. In practice, you might use a high-end model for critical pieces and a faster, cheaper model for boilerplate. Some coding tools let you configure which model to use; in others, you might just manually use the different web UIs or APIs. For instance, you could maintain one chat in ChatGPT and another in Claude for the same problem, and compare answers. Don’t be afraid to involve multiple “AI brains” in your project. Just keep track of which code came from where, because their styles might differ. - **Branch per model or approach:** As mentioned in the version control section, if you use multiple models to attempt the same feature, isolating them in different branches can help. For example, `feat/login-gpt4` vs `feat/login-claude`. This way you don’t mix the outputs together inadvertently. You can evaluate each branch: run tests, see which one is cleaner or more efficient, and then decide which to adopt (or merge the best of both). This branching strategy acts as a log of “GPT-4’s solution” vs “Claude’s solution.” It can be very informative for the team to review both, and it’s also a form of hedging – if one model’s output has hidden bugs, the other might not. - **Log your interactions and decisions:** When coding with an AI, a lot of the design rationale might be in the conversation with the model. It’s useful to save important exchanges. Some teams save the chat transcripts in the repo (perhaps in a `ai_logs/` directory or attached to pull requests). At minimum, keep a running document or journal of what you asked the AI to do and what changes were made. This doesn’t have to be super formal – even commit messages can serve this purpose if written descriptively (e.g., “AI (GPT-4): Added caching to improve response time”). The point is, if someone later wonders *“why did we implement it this way?”*, you might actually have the AI conversation logged that contains the context. Moreover, logging helps with model evaluation. If you find later that a piece of code is problematic, you can look back at the log and see which model/version produced it and what prompt was used, which might help in fixing it or at least avoiding that pattern in the future. - **Evaluate and document model performance:** Over time, note which model has been most helpful for your project’s needs. Perhaps GPT-4 writes the clearest code but uses newer libraries that your environment doesn’t support, whereas another model writes slightly clunkier code but never forgets edge cases. These observations can guide future model choices. Share these findings with your team: e.g., *“Claude tends to produce more concise database queries, let’s use it when optimizing SQL”* or *“GPT-3.5 struggled with our TypeScript definitions; GPT-4 did much better there.”* This kind of meta-knowledge becomes part of your team’s expertise in AI-assisted development. It also ties into the spirit of continuous experimentation – new model versions come out frequently, so having a habit of evaluating and logging results will let you quickly adopt improvements. - **Consider cost and access**: Using multiple models might have practical limitations (cost, rate limits, etc.). Keep an eye on API usage if you’re calling models programmatically. You might reserve the expensive model for where it really matters (complex logic, critical code) and use cheaper ones for simpler tasks or exploration. This is analogous to using the right tool for the job and also managing your resources. Document any such strategy so the whole team knows – e.g., *“We use local LLM X for generating docs because it’s free and fast, but use paid API Y for production code generation.”* Logging each model’s involvement can also help in cost analysis later (you could tally how many calls or tokens were used for each model). By thoughtfully juggling models and keeping records, you ensure that your AI development process is reproducible and learnable. It’s very much like keeping lab notes during experiments – and indeed, we’re in a phase where developing with AI feels a bit experimental. Don’t let that experimentation be haphazard. Track it, learn from it, and soon you’ll have a playbook of what model or method works best for each scenario your team encounters. ## Favor Small, Focused Projects (and Refactor Often) When using LLMs to generate code, **scope and complexity are critical considerations.** Large monolithic projects with sprawling codebases can confuse both the AI and the developers. It’s often more effective to break problems (and repositories) into smaller, focused units that the AI (and you) can fully grasp in context. Additionally, continuously refactoring and simplifying your code becomes even more important when an AI is contributing, to prevent messy accumulations of code. Here’s why you should aim small and refactor often: - **Context windows are limited:** LLMs can only “see” a certain amount of text (the context window). If you have a giant project with dozens of files, the AI won’t have all of that loaded at once, which means it might not recall relevant parts of your code when generating new code. By keeping projects smaller or working on one component at a time, you increase the chance the AI has the necessary context. If your project is large, consider focusing on one module or microservice at a time in your prompts, rather than feeding the AI the whole codebase. Some advanced setups involve vector databases or retrieval systems to fetch relevant code snippets for the AI, but that’s complex – an easier way is just to keep the scope of each AI interaction limited. Splitting a big project into smaller services or libraries can make AI coding more effective (and as a bonus, makes life easier for human developers too!). - **Easier comprehension for non-experts:** Since our team includes non-coders or junior devs, smaller projects are easier for them to understand as well. It’s less overwhelming to manage a focused codebase. If each micro-project has a clear purpose, a newbie (human or AI) can load up just that part and get to work. For example, instead of one mega-repo containing frontend, backend, and deployment scripts, you might have a frontend repo and a backend repo. A non-coder could use the AI to experiment with the frontend without touching backend code at all, and vice versa. Modularizing your work is a win for collaboration. - **Refactor continuously with tests in place:** We talked about how tests give you confidence to refactor. Make use of that. After each major feature or two, consider doing a refactoring pass (with or without AI assistance) to clean up the code. This prevents the “bad layers” effect where each successive AI generation piles new code on top of old without rethinking structure. For instance, if after a few sessions you notice a lot of duplicate helper functions or inconsistent patterns, take a moment to unify them. You could prompt the AI: *“Refactor these 3 similar functions into one generic function.”* Or do it manually if that’s safer, then run tests to ensure nothing broke. Regular refactoring keeps the codebase coherent and easier to prompt on later. It also fights entropy – remember Pietro’s observation that as his AI-built app grew, *“code quality is quite terrible… files get messy… very OCD triggering!”* ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,level.%20Very%20OCD%20triggering)). Don’t let it get to that point in a team project. Tidy as you go. - **Avoid the huge monorepo temptation:** It might be tempting to throw everything into one giant repository and have the AI handle all of it, but in practice, that’s where things often break down. If your project feels complex, see if you can split it. Maybe separate an SDK from the main app, or split internal tools from customer-facing code. Each repository or sub-project should have its own README or spec (which the AI can refer to). If you do keep a monorepo for valid reasons, then at least isolate your prompts to one section at a time, and use directory-specific context (e.g., “we’re working in `/analytics-service/` folder now, focus only on that”). The goal is to minimize the moving parts the AI has to juggle in one go. - **Microservices and APIs by AI:** Interestingly, LLMs can help you create microservices very quickly. You might decide to spin up a small FastAPI service for a particular feature separate from the main app. The AI can generate the boilerplate for that service and you keep it as an independent piece. This aligns with the philosophy of small focused projects. You can then connect them via APIs. While microservice architecture has its own overhead, in the context of AI dev it can compartmentalize complexity. Just ensure each service has good tests and clear API docs (the AI can help write those too!). By favoring smaller projects and regular refactoring, you make the AI’s job easier and your team’s workflow smoother. Smaller scope means fewer hallucinations and inconsistencies from the AI. Continuous refactoring means the code stays clean and understandable, which in turn feeds back into better AI output (since the AI is learning from a well-structured codebase as context, not a tangled mess). Essentially, **treat simplicity as a feature.** The techniques may evolve, but simple, well-factored code will always be easier to work with – for humans *and* machines. ## Keep a Spirit of Experimentation Finally, it’s important to acknowledge that LLM-assisted development is a **rapidly evolving field**. New techniques, tools, and models are emerging almost weekly. What works well today might be surpassed by a better approach tomorrow. For teams working in this space, the best mindset is one of continuous learning and experimentation. - **Stay updated on community trends:** Follow blogs, forums, and social media where developers share their experiences with vibe coding and AI tools. For example, developers often post on Reddit or personal blogs about “I built X entirely with GPT-4” or “New prompt strategy for better code quality.” These real-world stories can give you ideas to try. The community is discovering best practices together. As of April 2025, there’s already a wealth of discussion about vibe coding – from Medium articles ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=Thanks%20to%20my%20job%20and,experiment%21%20So%20I%20asked%20myself)) ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,level.%20Very%20OCD%20triggering)) to InfoWorld op-eds ([Vibe code or retire | InfoWorld](https://www.infoworld.com/article/3960574/vibe-code-or-retire.html#:~:text=Vibe%20coding%20is%20how%20we,a%20software%20developer%20will%20end)) – and the conversation continues. Make it a habit for someone on the team to scout for new tips or tools every so often and share with the group. - **Try new tools as they appear:** The AI coding tool landscape is fast-moving. Today’s Windsurf and Cursor might be joined by other editors or plugins tomorrow that offer new capabilities (maybe deeper integration with version control, or better error analysis, etc.). If a new tool comes out claiming to improve on your current workflow, give it a shot on a small example project. For instance, if an “AI agentic editor” appears that can handle multi-step coding tasks autonomously, you might test it on a non-critical task to see if it boosts productivity. Not every new thing will stick, but being an early adopter (cautiously) can keep your team ahead of the curve. Just ensure you don’t disrupt active projects with unstable tools – isolate experiments. - **Experiment with prompt techniques:** Prompt engineering is half art, half science, and it’s evolving. Some developers find success with a certain style (e.g., writing the entire desired code as comments and asking the AI to fill it in), while others use step-by-step prompting (first ask for a plan, then ask for code based on the plan). Encourage team members to experiment with different prompting methods and share the results. Maybe one person discovers that asking the AI to “think through the implementation step by step before coding” yields fewer bugs – that’s great, share it and incorporate it into your guidelines. We’re all effectively training ourselves on how to communicate with these AIs. Over time, you’ll build a library of prompt patterns that work well for your domain. - **Monitor the AI’s improvements:** The models themselves are improving. OpenAI, Anthropic, Google – all are refining their LLMs. A task that a year ago produced nonsense might now work flawlessly. Pietro, in his vibe coding experiment, said he’ll repeat the experiment every few months to see how far things have advanced ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,%E2%80%94%20and%20drastically%20more%20productive)). You can do the same in a more incremental way: re-evaluate pain points periodically. If generating a certain complex piece of code was beyond AI last quarter, try again with the latest model update – you might be surprised. This also means some constraints you define today (“the AI can’t do X well, so we do it manually”) could be removed in the future. Remain open to changing your processes as the tech evolves. - **Foster a culture of play and learning:** Perhaps most importantly, keep the vibe (pun intended) upbeat and curious. AI development is new for everyone; there will be hiccups and weird moments. Treat them as learning experiences rather than failures. If the AI writes a ridiculous piece of code, have a chuckle, share it in the group chat (we’ve all seen it!), and then figure out why it went wrong. Celebrate the wins – like that moment it saved you an hour by generating a complex regex or when it helped a non-coder teammate successfully build a feature. By maintaining a spirit of experimentation, the team will be more resilient and adaptive. Developers won’t feel threatened by the AI but rather engaged in mastering a new tool. Non-developers will feel encouraged to participate because the team is open to trying things. In essence, approach LLM-assisted development as a journey. The destination (fully AI-driven engineering?) is not fully known, but along the way we’re discovering powerful new ways to build software. Keeping an experimental mindset ensures your team doesn’t stagnate or cling to a practice that might become outdated. As one article on the state of vibe coding put it, *“What a time to be alive!”* ([The State of Vibe Coding — April 2025 Edition | by Pietro Bolcato | Apr, 2025 | Medium](https://medium.com/@pietrobolcato/the-state-of-vibe-coding-april-2025-edition-29cc839fb587#:~:text=,%E2%80%94%20and%20drastically%20more%20productive)) – the excitement is justified. Embrace it, experiment with it, and continuously refine your approach to make the most of this fast-changing landscape. ## Conclusion AI pair programming and vibe coding are transforming how software is built. Teams that include both veteran developers and complete newcomers can now collaborate in unprecedented ways – describing problems in plain English, having AI assistants generate code, and iterating at breakneck speed. By following the practices outlined in this guide – from careful planning and prompt guidance to test-driven development, disciplined version control, and a mindset of constant improvement – you can harness LLMs as a **force multiplier** rather than a source of chaos. Remember, the goal isn’t to let the AI replace human judgment, but to **augment** our capabilities. Treat the LLM as a fast, tireless junior developer who needs your direction and oversight. Make it follow your team’s best practices, involve it in brainstorming and debugging, and leverage specialized tools to bridge gaps for non-coders. Keep your projects manageable in size, your code clean, and your tests green. And don’t forget to have fun experimenting – we’re at the frontier of a new programming era, and staying curious and adaptable is key. By integrating these approaches, your team can build robust software with the help of AI, faster than ever before, without sacrificing quality. So map out that project, fire up Cursor or your tool of choice, and start coding with your AI sidekick. As the vibe coding pioneers would say: the *“hottest new programming language”* is English ([Vibe coding - Wikipedia](https://en.wikipedia.org/wiki/Vibe_coding#:~:text=The%20concept%20of%20vibe%20coding,longer%20need%20to%20learn%20specific)), and we’re all learning how to “speak” it to our AIs effectively. Happy coding – and happy vibing – to you and your team!