On Vibe Coding

AI
Programming
Author

Branden Collingsworth

Published

March 24, 2025

How do you use AI Agents to code without creating a mess?

Untangled

If you have spent any time in Cursor, Windstrem, Github Copilot, or Claude Code, you have probably watched an AI Agent mangle your code, hallucinate an API call, delete a failing test, or depart from the original intent of the code.

You have also probably felt the power of simply prompting a web app into existence in minutes. How do we bring the power of AI Agents to work without the mess? The solution is to treat an AI Agent just as you would any other developer.

Rules for (Vibe) Coding

Use Source Control

Always use source control. Git allows you to roll back the mess that the AI Agent creates. This is especially important when you are making multiple changes to a project or if you find the AI Agent has fallen into dependency hell. The best path forward sometimes is to burn it down and start over.

While this might put Vibe Coding out of the reach of non-programmers, it is essential for any serious software project with or without AI Agents.

Deny List

When using Yolo mode, the agent can run commands in the terminal for you. This is great for productivity, but it can also be a disaster. To avoid this, set up the deny list of commands that you do not want the agent to run. I recommend including the following:

delete file protection

This is an option in Cursor, not a specific command you need to add to the deny list.

git

Git is your way out of the mess, so you should not let the agent run commands without your supervision.

pip

Python package management is a real pain to fix. If the AI Agent doesn’t have the right environment activated, you can quickly end up with a mess. This is probably true of most package managers. What you are gaining in productivity is probably not worth the headache of adding new dependencies or messing up your environment.

If a developer on the team started installing new dependencies, deleting files, or messing with the environment, I would be just as concerned.

MCP Servers

A Model Context Server can be a better interface for an AI Agent compared to a terminal because the AI Agent doesn’t have to know what directory you are in, the correct shell commands, OS, etc. A dedicated MCP Server for git can have read-only access, an MCP Server for Docker can be more efficient than calling the docker command line, etc.

I recommend using project-level MCP Servers for almost all use cases. If the AI Agent has access to tools that it doesn’t need, it only makes it more likely that it will get confused and call a tool that is not appropriate for the task at hand. Docker and git are examples of MCP Servers that might be needed for all projects.

Sufficiently large teams will develop their own tooling to help onboard new developers and simplify the process of contributing to the project. Similarly, MCP Servers are tooling for AI Agents.

Use a Linter

Linting is a way to catch errors and potential issues in your code before it is committed. AI Coding tools are able to show the lints to the AI Agent so that it can fix the issues as they code. Setting standards for linting is a good way to ensure that the AI Agent is writing code that is consistent with the rest of the project and follows basic best practices.

In Python you can use a pyproject.toml file to set up linting.

[tool.pylint]
# Basic pylint settings
disable = [
    "line-too-long",
    "logging-fstring-interpolation",
]
max-line-length = 120

Type Checking

Type checking tools like MyPy and TypeScript ensure that the AI Agent is not hallucinating interfaces and types, which is very common in larger codebases. By setting up type checking, your AI Agent will see the errors and can fix them as they code.

It can be a pain to set up type checking for a project, but you know who is good at tedious coding tasks? AI Agents.

In Python you can use a pyproject.toml file to set up type checking.

[tool.mypy]
# Basic mypy settings
disallow_untyped_defs = true
ignore_missing_imports = true
strict = true

Write Tests

How do you know the AI Agent didn’t break anything when you asked it to implement a new feature? Think it’s odd that the AI Agent modified a file that you didn’t ask it to modify? If you have good test coverage, you can catch these issues before they are merged into the main branch.

I recommend avoiding mocking and stubbing. I find the AI Agent will write complex mocks and stubs that simply avoid actually testing the code. No mocking can make for long-running tests and require setting up a development or testing environment, but this too is a task that AI Agents are good at. AI Agents are also good at writing tests, so you should ask them to write tests for the code they have written. Checking the tests that AI Agents write for you is one of the best uses of your own brain power.

Once you have good test coverage, you can be confident that the AI Agent is not breaking anything when it is making changes.

Write Documentation

Use a consistent documentation style. This will help the AI Agent understand your code and write better documentation; the same goes for humans. Use meaningful variable and function names, add comments to explain the why behind the code, and keep the readme and todo files up to date. You can ask the AI Agent to update the documentation after it has written the code.

Cursor rules can be used to set up a consistent documentation style.

# Documentation Style

## Python Documentation Style

- Add a docstring to all functions and classes
- Use Google style docstrings

Logging

Logging is about adding context for the developer to help them understand how the code is running. It’s most useful when it’s been a while since you last looked at the code or when you are trying to debug an issue. Since, AI Agents don’t have memory, logging is a good way to help them quickly understand the code and determine how it works or why it doesn’t.

Logging to files is a good way to make adding logging output to the context for the AI Agent easier. I find that AI Agents are much more likelyt to solve a bug on the first try if they have good logging output along with the code.

In Python you can set up logging to a file like this.

import logging

logging.basicConfig(filename='app.log', level=logging.INFO)

logging.info("Starting the application")

Error Handling

Error handling is another way to make the AI Agent better at debugging. Catching errors and adding context to the error message ensures that the AI Agent knows what cause the error. I find error handling is especially important when dealing with external APIs that the AI Agent doen’t have visibility into.

In Python you can set up error handling like this.

try:
    # call the database
    data = database.get_data()
except Exception as e:
    logging.error(f"Error getting data: {e}")
    raise e

Cursor Rules

Every project should start with a cursor rules file. Currently, Cursor has User Rules and Project Rules. User Rules are global and apply to all projects. Project Rules can be auto-attached to the context whenever a file glob is included in the context. So working on a *.py file could auto-attach your python.mdc rules file.

User rules should have some very basic rules that apply to all the files in the project. I like to also use the user rules to tell the AI Agent about the terminal or MCP Servers and how I expect it to interact with them.

It’s a good idea to have rules for each language you are using, your testing strategy, your project structure, and other development tools you are using.

Cursor rules files can be manually attached to the context, which is handy for things like tests or project dependencies. But you don’t have to only use rules for telling the AI Agent what to do. You can also insert any file like a readme, todo list, or changelog into the context when it’s needed.

Here is an example of a cursor rules file for testing a Python project.

# Testing

## Python Testing

- Use pytest for testing
- Don't mock or stub tests
- Write unit tests for all functions and classes
- Use coverage to track test coverage and aim for 80% coverage
- Use pytest-cov to report on test coverage
- Do not change the test files unless you are adding a new feature or have been asked to fix a test specifically

Dev Container

Using a dev container makes it easier to onboard new developers and keep the development environment consistent. It also makes it easier to test the project in a clean environment. Dev containers allow you to rebuild everything from scratch, which is great when you walked away from your project for a moment while the AI Agent decided to try to downgrade your nginx version to solve a dependency issue.

You can specify a dev container in VSCode using the dev container extension and the devcontainer.json file. I realize that adding Docker to my Vibe Coding rules might put Vibe Coding out of reach for non-programmers, but before you give up, ask your AI Agent to help you set it up.

VS Code Settings

If you set up linting, type checking, testing, and a dev container, you should make sure that your IDE is configured to use all the tools you have set up. This can be done by including a .vscode/settings.json file in the root of the project. Ideally, you should see the same linting and type issues in the IDE that the AI Agent is seeing. So being explicit about these settings is important.

Example of a .vscode/settings.json file for a Python project.

{
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
        "source.fixAll": "explicit",
        "source.organizeImports": "explicit"
    }
}

Conclusion

AI makes it very easy to set up a professional-grade development environment for any project. If you add a git repo, a dev container, cursor rules, linters, type checking, and testing to start your project, you can expect professional results.

Back to top