"The best code is the code you don't write. The second best is the code that's obviously correct."
Embrace the framework:
- Rich domain models over service layers
- Framework routing over custom routing
- Built-in patterns over imported patterns
- Framework's database tools, not external ORMs
- Build solutions before reaching for packages
- Trust the framework's opinions - they exist for good reasons
What to avoid (universal anti-patterns):
- External auth libraries when framework auth exists
- Complex permission systems over simple role checks
- External job queues when framework has one
- External caching when framework provides it
- Component libraries when templates work
- GraphQL when REST is sufficient
- Factory patterns in tests when fixtures/seeds work
- Microservices when a monolith suffices
Development Philosophy:
- Ship, Validate, Refine - get to production to learn
- Fix root causes, not symptoms
- Write-time operations over read-time computations
- Database constraints over application validations
- Simple code that works > clever code that impresses
- Controllers/Views - Request handling, routing, responses
- Models/Data - Domain logic, state management, queries
- Frontend - Templates, components, interactivity
- Architecture - Routing, auth, jobs, caching
- Testing - Unit tests, integration tests, fixtures
- Dependencies - What to use vs avoid
- Code Review - Review against framework conventions
- General Guidance - Philosophy and conventions
Specify a number or describe your task and your framework (Django, Laravel, Next.js, etc.)
After reading relevant references, apply patterns to the user's code.
| Framework |
Core Conventions |
What to Embrace |
| Django |
Fat models, thin views, ORM queries |
Admin, class-based views, forms, signals |
| Laravel |
Eloquent, Blade, Facades, Artisan |
Resource controllers, form requests, events |
| Next.js |
Server components, file routing, API routes |
App router, server actions, middleware |
| Spring Boot |
Auto-config, dependency injection, JPA |
Starters, repositories, @Transactional |
| Phoenix |
Contexts, Ecto, LiveView, channels |
Generators, PubSub, presence |
| FastAPI |
Pydantic, async, dependency injection |
OpenAPI, background tasks, middleware |
| Rails |
Fat models, thin controllers, conventions |
Hotwire, concerns, Active* libraries |
| Remix |
Loaders, actions, nested routes |
Form handling, progressive enhancement |
| NestJS |
Decorators, modules, providers |
Guards, interceptors, pipes |
|
|
|
Actions: Domain verbs that describe what happens
- Good:
card.close(), order.ship(), user.activate()
- Bad:
card.setStatus('closed'), order.updateShipped(true)
Predicates: Boolean queries derived from state
- Good:
card.closed?, order.shipped?, user.active?
- Bad:
card.isClosed, order.getIsShipped()
Collections: Descriptive scope names
- Good:
chronologically, alphabetically, recent, active, pending
- Bad:
sortByDate, filterActive, getRecent
REST Mapping (Universal)
Instead of custom actions, create new resources:
Custom Action → RESTful Resource
POST /cards/:id/close → POST /cards/:id/closure
DELETE /cards/:id/close → DELETE /cards/:id/closure
POST /orders/:id/ship → POST /orders/:id/shipment
POST /users/:id/ban → POST /users/:id/ban
State as Data (Universal)
Instead of boolean flags, use related records or enums:
Boolean Flag → State Record/Enum
card.closed = true → card.closure (record exists)
order.shipped = true → order.shipment (record exists)
user.status = 'banned' → user.ban (record exists)
Query with joins/relations, not boolean comparisons:
- Cards with closures = closed cards
- Cards without closures = open cards
The Framework Way
Before writing custom code, ask:
- Does the framework have a built-in way to do this?
- Is there a convention I should follow?
- Will this surprise other developers familiar with the framework?
- Am I fighting the framework or working with it?
All detailed patterns in references/:
- Shared vocabulary - Everyone knows what to expect
- Faster onboarding - New devs understand the codebase immediately
- Less decision fatigue - Spend energy on business logic, not architecture
- Community support - Problems are already solved and documented
- Upgrades are easier - Framework updates don't break custom patterns
The 90/10 Rule
The framework solves 90% of your problems elegantly. The remaining 10% often feels like it needs custom solutions, but usually:
- You're overcomplicating the requirement
- There's a framework pattern you haven't discovered
- The "limitation" is actually protecting you from a bad idea
When you truly need to go custom, document why and keep it isolated.
1---2name: framework-conventions-guide-23description: This skill should be used when writing code in any opinionated framework's distinctive style. It applies when writing framework-based applications, creating models/controllers/views, or any framework code. Triggers on code generation, refactoring requests, code review, or when the user mentions framework conventions. Embodies the philosophy of embracing framework conventions, fighting complexity, and choosing simplicity over cleverness.4---56<objective>7Apply framework-native conventions to code in ANY opinionated framework. This skill teaches the universal principles that framework creators follow, regardless of language or framework.8</objective>910<essential_principles>11## Core Philosophy1213"The best code is the code you don't write. The second best is the code that's obviously correct."1415**Embrace the framework:**16- Rich domain models over service layers17- Framework routing over custom routing18- Built-in patterns over imported patterns19- Framework's database tools, not external ORMs20- Build solutions before reaching for packages21- Trust the framework's opinions - they exist for good reasons2223**What to avoid (universal anti-patterns):**24- External auth libraries when framework auth exists25- Complex permission systems over simple role checks26- External job queues when framework has one27- External caching when framework provides it28- Component libraries when templates work29- GraphQL when REST is sufficient30- Factory patterns in tests when fixtures/seeds work31- Microservices when a monolith suffices3233**Development Philosophy:**34- Ship, Validate, Refine - get to production to learn35- Fix root causes, not symptoms36- Write-time operations over read-time computations37- Database constraints over application validations38- Simple code that works > clever code that impresses39</essential_principles>4041<intake>42What are you working on?43441. **Controllers/Views** - Request handling, routing, responses452. **Models/Data** - Domain logic, state management, queries463. **Frontend** - Templates, components, interactivity474. **Architecture** - Routing, auth, jobs, caching485. **Testing** - Unit tests, integration tests, fixtures496. **Dependencies** - What to use vs avoid507. **Code Review** - Review against framework conventions518. **General Guidance** - Philosophy and conventions5253**Specify a number or describe your task and your framework (Django, Laravel, Next.js, etc.)**54</intake>5556<routing>57| Response | Reference to Read |58|----------|-------------------|59| 1, "controller", "view", "route" | [universal-patterns.md](./references/universal-patterns.md) - REST patterns section |60| 2, "model", "data", "query", "orm" | [universal-patterns.md](./references/universal-patterns.md) - State and models section |61| 3, "frontend", "template", "component" | [universal-patterns.md](./references/universal-patterns.md) - Frontend section |62| 4, "architecture", "auth", "job", "cache" | [universal-patterns.md](./references/universal-patterns.md) |63| 5, "test", "testing" | [universal-patterns.md](./references/universal-patterns.md) - Testing section |64| 6, "dependency", "package", "library" | [anti-patterns.md](./references/anti-patterns.md) |65| 7, "review" | Read all references, then review code |66| 8, general task | Read relevant references based on context |6768**After reading relevant references, apply patterns to the user's code.**69</routing>7071<framework_detection>72Before providing guidance, identify the framework and load its specific conventions:7374| Framework | Core Conventions | What to Embrace |75|-----------|------------------|-----------------|76| **Django** | Fat models, thin views, ORM queries | Admin, class-based views, forms, signals |77| **Laravel** | Eloquent, Blade, Facades, Artisan | Resource controllers, form requests, events |78| **Next.js** | Server components, file routing, API routes | App router, server actions, middleware |79| **Spring Boot** | Auto-config, dependency injection, JPA | Starters, repositories, @Transactional |80| **Phoenix** | Contexts, Ecto, LiveView, channels | Generators, PubSub, presence |81| **FastAPI** | Pydantic, async, dependency injection | OpenAPI, background tasks, middleware |82| **Rails** | Fat models, thin controllers, conventions | Hotwire, concerns, Active* libraries |83| **Remix** | Loaders, actions, nested routes | Form handling, progressive enhancement |84| **NestJS** | Decorators, modules, providers | Guards, interceptors, pipes |85</framework_detection>8687<quick_reference>88## Universal Naming Conventions8990**Actions:** Domain verbs that describe what happens91- Good: `card.close()`, `order.ship()`, `user.activate()`92- Bad: `card.setStatus('closed')`, `order.updateShipped(true)`9394**Predicates:** Boolean queries derived from state95- Good: `card.closed?`, `order.shipped?`, `user.active?`96- Bad: `card.isClosed`, `order.getIsShipped()`9798**Collections:** Descriptive scope names99- Good: `chronologically`, `alphabetically`, `recent`, `active`, `pending`100- Bad: `sortByDate`, `filterActive`, `getRecent`101102## REST Mapping (Universal)103104Instead of custom actions, create new resources:105106```107Custom Action → RESTful Resource108POST /cards/:id/close → POST /cards/:id/closure109DELETE /cards/:id/close → DELETE /cards/:id/closure110POST /orders/:id/ship → POST /orders/:id/shipment111POST /users/:id/ban → POST /users/:id/ban112```113114## State as Data (Universal)115116Instead of boolean flags, use related records or enums:117118```119Boolean Flag → State Record/Enum120card.closed = true → card.closure (record exists)121order.shipped = true → order.shipment (record exists)122user.status = 'banned' → user.ban (record exists)123124Query with joins/relations, not boolean comparisons:125- Cards with closures = closed cards126- Cards without closures = open cards127```128129## The Framework Way130131Before writing custom code, ask:1321. Does the framework have a built-in way to do this?1332. Is there a convention I should follow?1343. Will this surprise other developers familiar with the framework?1354. Am I fighting the framework or working with it?136</quick_reference>137138<reference_index>139## Domain Knowledge140141All detailed patterns in `references/`:142143| File | Topics |144|------|--------|145| [universal-patterns.md](./references/universal-patterns.md) | REST mapping, state as data, naming, testing, frontend |146| [anti-patterns.md](./references/anti-patterns.md) | What to avoid, complexity traps, over-engineering signs |147</reference_index>148149<success_criteria>150Code follows framework conventions when:151- Routes map to CRUD verbs on resources152- Models use framework's built-in composition patterns153- State is tracked via records/relations, not booleans154- No unnecessary service objects or abstractions155- Framework solutions preferred over external packages156- Tests use framework's built-in test utilities157- Interactivity uses framework's frontend solution158- Authorization logic lives close to domain159- Jobs are shallow wrappers calling domain methods160- Configuration uses framework patterns, not custom solutions161</success_criteria>162163<philosophy>164## Why Convention Over Configuration?1651661. **Shared vocabulary** - Everyone knows what to expect1672. **Faster onboarding** - New devs understand the codebase immediately1683. **Less decision fatigue** - Spend energy on business logic, not architecture1694. **Community support** - Problems are already solved and documented1705. **Upgrades are easier** - Framework updates don't break custom patterns171172## The 90/10 Rule173174The framework solves 90% of your problems elegantly. The remaining 10% often feels like it needs custom solutions, but usually:175- You're overcomplicating the requirement176- There's a framework pattern you haven't discovered177- The "limitation" is actually protecting you from a bad idea178179When you truly need to go custom, document why and keep it isolated.180</philosophy>