Vibe coding in style.md

Founders, designers, PMs, marketers—technical at different levels, but not engineers—use code generation tools heavily now. It feels liberating. It’s also incredibly addictive. But the reality check begins when an engineer opens your repo and (despite their best effort to make things work) must throw away everything you vibe coded and rewrite it from scratch. If you want to prevent that, this post is for you. This is how we turned “vibe code cleanup” into a reusable asset that packs Martian engineering vibes into one small file: AGENTS.md.
Hire Evil Martians
AGENTS.md is the bottled essence of a Martian engineer. Try it on your next AI-assisted project!
The Cloud Cards story
Some backstory. When thinking about smarter ways to promote the San Francisco Ruby Conference, Victoria Melnikova came up with an idea: let existing attendees generate fun personal invitations with cartoon characters based on their photos.
Next thing I remember: burning midnight credits and building the app with Ruby on Rails, Avo, RubyLLM, and Nano Banana. The generated “Cloud Cards” would sometimes misspell the conference name, but they looked cute and matched our branding almost too well.
I tried deploying the app to Railway, ran into some issues, and shared access to the repo and project with Vladimir Dementyev (conveniently, he’s my colleague at Evil Martians and is one of the most brilliant web engineers in the world).
Vladimir’s feedback is normally not for the weak of heart, but this time it was extreme: he locked in and rewrote the whole app from scratch.
When I looked at the app again, it made me remember that reading code can be a joy. It felt like luxury—and it was, given how much skill and attention went into it.
Before I knew it, I had a new addiction: “generating” code that looks like Vladimir wrote it. Utterly elegant and self-explanatory.
The problem: not everyone has a Vladimir
There is one catch: there is no world-class engineer on permanent duty to spend their life rewriting my vibe coded apps.
We cannot have nice things!
What we did have was both versions of the same app, both the original and the refactored Cloud Cards. So the question became: can we use them to teach a model to mimic Vladimir’s style?
It was worth a try.
Our 3-step process
Here’s the process we used:
-
Ask a thinking model to compare the two versions and generate a document explaining the key differences and Vladimir’s “style”. Treat this as a design doc for future work: something we can hand to a model while building the next application, so the result is closer to what Vladimir would write himself.
-
Take that large file, style.md, and summarize it into a shorter, more focused file called AGENTS.md:
- AGENTS.md restates the app context and use case
- It also encodes the key style and structure decisions as rules and patterns
-
Use a thinking model again to spot any technical inconsistencies and improve the quality of the document. A manual read-through is also important.
Here’s that resulting file: AGENTS.md.
Next, I used AGENTS.md while building a new project—this time, an experiment with text editing and AnyCable. After a couple of days, Vladimir reviewed the new codebase. He shared direct, detailed feedback, but this time, he said he could work with the code instead of throwing it away.
Was this a win? At minimum, it was a promising start! We were already progressing to a different class of feedback, and for the first time, the code felt like something I could read, too.
Inside AGENTS.md
Disclaimer: to be clear, these are not the “official” Evil Martians Rails guidelines. They are imperfect by design, produced by a very simple process, from a single refactoring case. But it’s a good start, and already practical.
So, what’s actually inside AGENTS.md? Surprisingly, quite a lot. Here are a few of the rules.
Use domain language
When naming things, use the same “normal” words you would use in natural language, instead of generic technical terms. The result is code that does not need a legend: names are already self-explanatory.
Bad: User, Image
Good: Participant, Cloud, Invitation
Use enums for state
Use enum for state to get predicate methods, bang methods, and scopes for free:
class Cloud < ApplicationRecord
enum :state, %w[uploaded analyzing analyzed generating generated failed].index_by(&:itself)
end
# Usage:
cloud.uploaded? # Predicate method
cloud.generating! # Bang method (update + save)
Cloud.generated.count # ScopeExtract code from models to namespaced classes
A simple organization technique to avoid fat models: keep models focused on data, relationships, validations, and simple logic. Move complex behavior to namespaced classes.
class Cloud < ApplicationRecord
# Model: just data and simple methods
def ready_to_generate?
analyzed?
end
end
# app/models/cloud/card_generator.rb
class Cloud::CardGenerator
private attr_reader :cloud, :api_key
def initialize(cloud, api_key: GeminiConfig.api_key)
@cloud = cloud
@api_key = api_key
end
def generate
# Complex API logic here, returns IO object
end
endWhen to extract:
- Any method over ~15 lines
- Any method calling external APIs
- Any complex calculation
- Anything reused across more than one place
Use anyway_config for type-safe configuration
# config/configs/application_config.rb
class ApplicationConfig < Anyway::Config
class << self
delegate_missing_to :instance
private
def instance
@instance ||= new
end
end
end
# config/configs/gemini_config.rb
class GeminiConfig < ApplicationConfig
attr_config :api_key
end
api_key = GeminiConfig.api_keyWhy?
- Type-safe (validated on load)
- Singleton-style access
- Organized in config/configs/
- Easy to extend with helpers (ssl?, configured?, etc.)
- Environment-specific (development, test, production)
Never use Rails credentials or ENV directly.
Patterns to prefer (and avoid)
Other examples encouraged in the file:
- Form Object
- Query Object
- ViewComponent
A few things it pushes against:
- Service Object
- Result Object
These are not universal truths, just the rules for this specific style and codebase. The rest of the file goes into more detail.
Manual edits
We also found some hallucinations in the AGENTS.md file, so we cleaned them up manually.
One example is that it mistakenly concluded that ActiveJob::Continuable requires bundlebun gem.
Turning cleanup into an asset
We turned a very expensive and non-scalable service “Vladimir cleans up my vibe code” into a reusable asset that is making my generated code more manageable.
Our plan from here:
- Try this AGENTS.md (Vladimir-flavored) on other vibe coding projects.
- Extend it (or create new ones) using the same process on more “vibe cleaning” cases.
- Keep experimenting with better formats for style guides and guardrails for code generation.
- Share results and keep learning in public.
What do you think of this little experiment? If you decide to try something similar—or if you already are—share your results!



