A solid foundation for a agentic python projects
If you ask Claude “make a project that downloads the latest Mr Beast videos” it will probably create a directory with some python files. In an attempt to get you from 0 to Mr Beast videos, it may take some shortcuts.
I recommend adding the following to your global claude context:
For new python projects, do the following
- Use uv init
- Prefer to use type annotations where reasonable
- Install ty, ruff, pytest, and coverage as uv dev deps
- When ruff/ty rules are too restrictive, do a line-level ignore. Use file-level rule ignores or globally ignoring rules in pyproject.toml only if necessary.
- Add pre-commit hook for ty check, ruff check, and ruff format, using default rules. Do this using `pre-commit` package
- Use coverage as part of testing to ensure a reasonable level of test coverage
- Run ruff check, ruff format, ty check, and pytest in cicd
These are all a little opinionated, but it should be no sweat off your back or the agent’s back. I’ll explain each:
Use uv init
uv is the current industry standard for python packages. I remember a time before it, with poetry, or even worse managing a pip requirements.txt.
It is lightning-fast at resolving dependencies, and configuration for it can be managed in the pyproject.toml file very easily.
Type annotations where reasonable
I am a fan of overusing type annotations. My consts ideally use Final like THING_THRESHOLD: Final[float] = 1.337.
And even if there is a generator like event: Event = Event.generate(1) which seems pretty explicit, I like to do it for correctness.
It becomes a little overkill when you need to annotate with local types, like if Event may result in a circular import. The classical way is to do something like event: "Event" and a if typing.TYPE_CHECKING: block at the top, but this really becomes overkill so that’s why I would prompt “where reasonable”. I think the llm figures this out.
ty, ruff, pytest, and coverage
ty and ruff are code checkers which make sure the code is correctly typed and formatted, and will find common errors. I prompt it to install as development dependencies so they don’t end up in the deployed artifact, since they don’t need to be there.
ty is a static type checker, so if I do foo: int = "bar" it will complain. This brings python closer to languages like go or rust which will not compile in this case. Without it, you are building on shaky foundation.
ruff check . will find issues like unused imports, insecure function usage (like pickling in a vulnerable way), or creating datetimes without a explicit timezone.
ruff format just keeps the code style consistant.
pytest is just to encourage it to write tests, but more custom is coverage which prompts the agent to think about code coverage. Agents are very good at iterating based on metrics like lint failures, test completion, or in this case code coverage percent. If you desire, you can give it a specific number like 80% code coverage to aim for, and encode this in the cicd.
Ignoring ty/ruff rules properly
Sometimes the coding agents just ignore the ty/ruff rules globally when they find a good place to ignore the rule. For example a ruff check rule S105 may trigger if you name something SECRET_KEY: Final[str] = "abc" since it sees SECRET in the variable name. The best way to handle this is something like # noqa: S105. But sometimes the agent gets lazy and globally ignores it, which is not ideal.
pre-commit hooks
The pre-commit package is a helper to define rules in a yaml file, and get them into the .git/hooks so they run before commit. If they fail, the commit does not occur.
This helps true vibecoding agents get a negative reenforcement loop of writing code and failing to commit it when it fails ty and ruff validation, then learning to run it each time. This encourages cleaner and correct code.
CICD
This is just a prompt to get it to write to the workflows directory if you are using github. The actions to enforce coverage and linting are cheap, so why not do them.
Prompting to do pytest in cicd is a good start, things may get a little more complicated if there are more dependencies like needing postgis or other containers, but the agent should be able to figure that out when it gets complicated.