AI is changing more and more of how I spend my time as a developer. I used to agonize over details, perfecting intricacies of small functions to maximize their generality. This exercise, while fun, delays delivery meaningfully. Immersing yourself in this type of development can also cause you to lose sight of the big picture, and precious hours are spent on ultimately unimportant contributions. Again: it’s fun. But there are times where “productive” takes precendence. In those moments, AI is proving to be a more effective implementer, but remains a poor long-term planner.

Delegating your work to AI does not come without caveats. The high-level design — aka the relationship between the underlying ideas you are trying to express and the code that represents them — is your responsibility. I’ve tried it the other way, giving vague requirements and letting AI have free rein. In the process I have wasted tens of thousands of tokens building myopic architecture that ultimately had to be discarded. While enduring the build/re-build process may have guided me to think about the right architecture, I could have just thought about the right architecture in the first place.

The following principles are guidelines for me to interact with coding assistants sustainably. This list was originally included in the Etch framework post (which, as of today, is officially released on PyPI!), and are intended as living reminders, hence an update with some recent learnings.

Assistants have improved dramatically since that original post; I used Claude 3.5 to build this framework, and the latest thinking models (Opus/Sonnet) are truly more like an employee to whom work can be delegated, not just a desk-side function generator. That said, I’ve found the principles below to have endured these improvements. The bugs AI will introduce are no longer clerical! The code will be beautiful, well organized, and (overly) commented. The errors you’ll encounter will require true understanding, which is why losing sight of these rules will translate to a lot of wasted effort; the very thing AI is meant to rescue us from!

Principles of Vibe Coding

At least for the time being, code assistants are not ready to work without supervision. That means your job as the developer is to act as tech lead. You understand the requirements, the intended structure, the coding standards, and — most importantly — the relationship between input and output.

  1. Always start with a design document. Treat the model the way you would a new hire: what do they need to know to make useful contributions? If you start with a too-brief or vague set of instructions, you are giving your assistant too many degrees of freedom and the resulting code will digress into nonsense. If you don’t want to spend the time to write a detailed project spec yourself, you can pair-author this with a model (obvious advice: you should read whatever it writes carefully), but you need to spend time thinking through what you want to build well before the first line of code is generated. Your first prompt should not be “build me a website,” it should be “here are the directories and classes we want to build, some information about how they interrelate, and the coding standards I’d like to follow.
  2. Write your own tests. While your agent will eagerly offer to do this, often unprompted, it’s generally inadvisable for the assistant to decide how it will be measured. First, you’re still the architect and you understand the requirements better than anyone! Second, function-level tests at the early stages of a project can be too restrictive for the model. You will want to offer enough freedom to make (controlled) design decisions.
  3. Trust but verify. You’ll hear the biggest proponents of vibe coding say that code is now an implementation detail, relating to today’s treatment of assembly. While the models are improving, I think that view is still a bit forward-looking. Non-technical prompters will have a hard time getting good, scalable results from assistants. Much like the trouble we saw with the front-end of Etch, someone who lacks the ability to take the wheel from the model will get stuck as soon as they reach a certain threshold of complexity. Stop and consider the risk of your project: is this a personal website where a bug causes a dead link? Or are you building something with consequences, where even small errors can compound into tangible damage?
  4. Prepare to learn. A simple fact: LLMs have read more code than you. One of the great reasons you shouldn’t be so prescriptive is that you’ll be surprised by some pretty clever snippets. When that happens, pause and study — you’ll be a better developer for it. This is, of course, inspired by (plagiarized from?) the collective wisdom of millions of developers and you should be excited by the opportunity to reflect that in your project. However, as soon as something doesn’t make sense — an unnecessary abstraction, funky regex, an eval or a hard-coded variable — put on your code reviewer hat and redirect the model away from something you can’t maintain.
  5. When you hit a roadblock, it’s your turn. AI will often paint itself into a corner and implement a nuanced bug. If you’ve spent the last several prompts blindly clicking “Accept”, it’s time to go back and understand the work that’s been delivered to you. Tricky bugs will send even the best models into a fruitless tailspin. I’ve tried the, “Nope, still broken. Here’s the console:” loop and it always takes longer than just reading the code myself and giving clear direction with complete information.
  6. Maintain a QUICKSTART.md As you grow your project, you will inevitably exhaust the context window. AI will get stuck “Summarizing your conversation” for even the most basic prompts. Try to organize your chats into focused conversations if there are many functional areas of your chat. The QUICKSTART file will give a new Agent an orientation on your project’s purpose, the code organization, your guidelines and your expectations for each prompt.

Always remember, working with an assistant is much more like managing a junior than pair-programming with a peer. While that junior possesses infinite energy and positivity, it also has an aggressive form of amnesia you need to manage around. When it starts to forget, or when it’s time to start fresh, you’ll need to fill the gaps back in to keep moving.