Convention is compression
Every new LLM session starts from zero. Last week I spent five minutes explaining things I’d explained the week before — where the migrations live, how the models are named, what the test setup looks like. The model had no memory of any of it.
Rails solved this problem in 2004. DHH called it convention over configuration. Name your table users and Rails knows the model is User, the primary key is id, a foreign key elsewhere is user_id, the views live in app/views/users/. Nobody configures any of this. It’s pre-agreed.
That pre-agreement does more work than it looks. A convention is compression — in the information-theoretic sense. Compression works by encoding frequently-used patterns into shorter representations that both parties already hold. Once the scheme is agreed, you stop transmitting what you both already know. Rails conventions meant a new developer, an ORM, a code generator, and a test runner could all navigate the same codebase without negotiating with each other first.
The same argument applies to LLMs, but the stakes land differently. A human developer loads conventions once, during onboarding, and carries them for years. An LLM session starts from zero every time. Without shared conventions encoded somewhere the model can read, you pay that onboarding cost on every session — in tokens, in time, in the wrong assumptions the model makes when you don’t catch them fast enough.
Where Rails went wrong
Rails proved the value of conventions. It also demonstrated the failure mode.
The problem wasn’t the conventions themselves — it was how they were implemented. Rails encoded them through metaprogramming: method_missing, module inclusions, callback chains inherited across controller layers. When everything worked, the abstraction was seamless. When something broke, decompression was agonising.
Picture this: a before_action :authenticate_user! defined in ApplicationController. A before_action :require_admin added in AdminController. A skip_before_action tucked into a child controller six months later by someone who’s since left. The behaviour is completely determined by three files in a specific inheritance order — but when an endpoint behaves unexpectedly, the stack trace points into ActionController internals, not your code. You know that something is running. You don’t know why.
Rails standardised by hiding complexity. You got speed and paid for it with opacity.
What a plugin does differently
An LLM plugin doesn’t abstract — it names. It’s a file. A SKILL.md that says: posts live here, frontmatter looks like this, check for these style issues, structure reports this way. The model reads it; the context is loaded. The output is plain text, plain code — whatever was asked for, written to the spec in the file. When it’s wrong, you trace it directly back to the file that specified the behaviour.
The convention encodes the where and the what. The how stays visible.
Something else falls out of this. Rails conventions and their documentation were always separate things — the behaviour lived in the framework, the explanation lived in a wiki or Confluence tab somewhere. To understand why before_action :authenticate_user! was running, you’d read the code in one window and search for the explanation in another. With a SKILL.md, the spec that governs the behaviour is the thing you read. Same file, same moment.
The next time I opened a Claude session, I stopped explaining. The map was already there.