[TODO: Add your business context here]
[TODO: Add your core features here]
[TODO: Add your target audience here]
- Framework: Ruby on Rails 8.0.0 (Edge)
- Language: Ruby 3.4.4
- Database: PostgreSQL (Primary, SolidQueue, SolidCache, SolidCable), Redis (Dev ActionCable)
- Web Server: Puma
- Hotwire: Turbo + Stimulus for SPA-like interactions
- CSS Framework: Tailwind CSS
- JavaScript: Importmap/Propshaft
- Components: ViewComponent (
app/components)
- Authentication: Rails 8 Auth generator (Users + Teams)
- Authorization: Pundit (
app/policies) - Background Jobs: SolidQueue
- Admin Interface: Administrate
- AI Features: RubyLLM, OpenAI, Anthropic
- Communication: Postmark (Email), Twilio (SMS)
- Testing: RSpec, FactoryBot, Capybara, Selenium, WebMock, VCR
- HeroIcons - https://heroicons.com
- Write concise, idiomatic Ruby code with accurate examples
- Follow Rails conventions and best practices
- Use object-oriented and functional programming patterns as appropriate
- Prefer iteration and modularization over code duplication
- Use descriptive variable and method names (e.g.,
user_signed_in?,calculate_total) - Structure files according to Rails conventions (MVC, concerns, helpers, etc.)
- Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping
- Use
snake_casefor file names, method names, and variables - Use
CamelCasefor class and module names - Follow Rails naming conventions for models, controllers, and views
- Replace hard-coded values with named constants
- Use descriptive constant names that explain the value's purpose
- Keep constants at the top of the file or in a dedicated constants file
- Variables, functions, and classes should reveal their purpose
- Names should explain why something exists and how it's used
- Avoid abbreviations unless they're universally understood
- Don't comment on what the code does - make the code self-documenting
- Use comments to explain why something is done a certain way
- Document APIs, complex algorithms, and non-obvious side effects
- Each function should do exactly one thing
- Functions should be small and focused
- If a function needs a comment to explain what it does, it should be split
The application uses additional patterns beyond standard Rails MVC:
app/services/: Service Objects for complex business logicapp/queries/: Complex data retrieval logicapp/forms/: Form Objectsapp/policies/: Pundit authorization policiesapp/components/: ViewComponent classesapp/jobs/: Sidekiq job definitionsapp/validators/: Custom validationsapp/dashboards/: Administrate Dashboardsapp/javascript/controllers/: Stimulus controllers
Use service objects to:
- Make controllers simple
- Perform complex operations
- Make the code more DRY (if a complex operation is needed in more than one place)
- Make calls to external APIs
- When using the Service concern, services should no be instantiated, but used with the .run class method
Use ViewComponents to:
- Create more reusable pieces of UI
- Encapsulate logic in UI components
- Avoid littering standard Rails view files with complex logic
- Create methods in Ruby files for component behavior
Run individual test files using:
# Run specific spec file
bin/rspec spec/models/user_spec.rb
# Run specific test
bin/rspec spec/models/user_spec.rb:25
# Run all tests
bin/rspec- Write comprehensive tests using RSpec
- Prefer request specs rather than system tests
- Follow TDD/BDD practices
- Use factories (FactoryBot) for test data generation
- Use
letto define test data - Keep tests short and concise
- Don't test
assigns- test rendered content instead- ❌
expect(assigns(:records)).to include(record) - ✅
expect(response.body).to include(record.name)
- ❌
- Write tests before fixing bugs
- Keep tests readable and maintainable
- Test edge cases and error conditions
- Most tests should be isolated specs for services, models, and ViewComponents.
- Request specs are the preferred integration layer — hit real routing + DB and assert JSON/Turbo Stream responses.
- System tests are usually for key paths (signup, payments, core workflows), kept minimal and fast. Complex UI features that rely on JavaScript should have 1 happy and 1 sad path system test.
- Outside-in: Start with a request or component/system spec; drive behavior into small POROs; mock only at boundaries (HTTP, Stripe), never internals.
- Small steps: One assertion concept per example; refactor relentlessly.
- Confidence focus: High-value flows get end-to-end coverage; most wiring bugs caught by request/component specs.
- Don't test private methods: Assert behavior through public APIs. If a unit has complex internals worth testing, extract a PORO with a public interface and test that instead.
- Models: Use shoulda-matchers for validations/associations; test only your business logic.
- Services/Queries: Pure POROs; mock only external boundaries.
- Policies: Unit test predicates; a few request specs for integration (403s).
- ViewComponents: Test rendering + key text/ARIA; for Hotwire, assert Turbo Stream payloads in request specs.
- Hotwire/Stimulus:
- Component spec — HTML/data-controller wiring.
- Request spec — correct Turbo Stream response.
- One JS-enabled system test — happy-path smoke.
- Status, schema/shape of JSON, Turbo Stream target/action, jobs enqueued, emails sent, DB changes.
- Webhooks: Verify signature; use real sample payloads from fixtures.
- Default to a small set of examples per endpoint (3–4 total):
- Happy path (200/201)
- Permissions/authorization failure when applicable (401/403)
- Validation failure for representative invalid input (422)
- Optional: Not found (404) only if behavior is non-standard
- Avoid enumerating minor parameter variations at the request level. Cover permutations and edge cases in smaller unit/service specs.
- Prefer clarity over volume; use shared examples sparingly for repeated permission/validation checks.
- Lean factories;
build_stubbedfor unit;createonly if DB matters. - Traits over callbacks; add
FactoryBot.lintto CI. - Parallelize specs; use
freeze_time; disable external HTTP via WebMock/VCR.
- SimpleCov with a sensible threshold; profile slowest specs; fix flakes at root cause.
- Add characterization tests first; introduce seams; slice into POROs; replace with narrower tests after refactor.
TL;DR: Prefer request specs for integration, lean heavily on fast isolated specs for core logic, and keep system tests sparse — only for critical paths that truly need end-to-end verification.
- Use Hotwire for dynamic, SPA-like interactions
- Implement Turbo Frames for partial page updates
- Use Turbo Streams for real-time updates
- Create Stimulus controllers for JavaScript behavior
- Use utility classes over custom CSS
- Group related utilities with @apply when needed
- Use proper responsive design utilities
- Implement mobile-first approach
- Use semantic color naming
- Keep component styles consistent
- Use proper responsive breakpoints
- Follow naming conventions
Based on established patterns in the application, follow these consistent styling conventions:
Primary Colors:
- Primary action buttons:
bg-blue-600withhover:bg-blue-700 - Primary focus states:
focus:ring-2 focus:ring-blue-500 - Primary text links:
text-blue-600 hover:text-blue-900
Status Colors:
- Success/Completed:
bg-green-100 text-green-800 - Warning/Pending:
bg-yellow-100 text-yellow-800 - Info/Processing:
bg-blue-100 text-blue-800 - Error/Failed:
bg-red-100 text-red-800 - Neutral/Default:
bg-gray-100 text-gray-800
Headings:
- Page titles (H1):
text-2xl font-semibold text-gray-900 mb-2 - Section headings (H2):
text-lg font-medium text-gray-900 - Subsection headings (H3):
text-base font-medium text-gray-900
Body Text:
- Primary text:
text-gray-900(for important content) - Secondary text:
text-gray-600(for descriptions, captions) - Tertiary text:
text-gray-500(for metadata, timestamps) - Muted text:
text-gray-400(for placeholders, disabled states)
Text Sizes:
- Small text:
text-sm - Extra small:
text-xs - Button text:
font-medium
Container Patterns:
- Main content width:
max-w-6xl mx-auto - Standard spacing:
mb-8(between major sections),mb-6(between subsections) - Grid layouts:
grid gap-8 lg:grid-cols-2(for two-column layouts)
Card Components:
- Standard card:
bg-white border border-gray-200 rounded-lg shadow-sm p-6 - Hover states:
hover:bg-gray-50(for interactive cards) - Card sections:
border-b border-gray-200(for dividing sections)
Input Fields:
- Standard input:
w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent - Labels:
block text-sm font-medium text-gray-700 mb-2 - Helper text:
text-sm text-gray-500
Buttons:
- Primary button:
flex items-center gap-2 px-8 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors - Disabled state:
disabled:opacity-50 disabled:cursor-not-allowed - Button with icon: Include
gap-2and size-5 icons
Drag & Drop Zones:
- Border:
border-2 border-dashed border-gray-300 - Hover state:
hover:border-blue-400 hover:bg-gray-50 - Active styling:
rounded-lg p-8 text-center cursor-pointer transition-colors
File Items/List Items:
- Container:
flex items-center justify-between p-3 bg-gray-50 rounded-lg border - Icon spacing:
flex items-center space-x-3 - Action buttons:
text-gray-400 hover:text-red-500 transition-colors
Table Structure:
- Table:
min-w-full divide-y divide-gray-200 - Header:
bg-gray-50 - Header cells:
px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider - Body cells:
px-6 py-4 whitespace-nowrap - Row hover:
hover:bg-gray-50
Status Badges:
- Base:
px-2 inline-flex text-xs leading-5 font-semibold rounded-full - Apply status-specific background/text color classes
Icon Guidelines:
- Standard size:
h-5 w-5(20px) - Small icons:
h-4 w-4(16px) - Large decorative:
h-10 w-10(40px) - Icon colors:
text-gray-400(default),text-gray-500(interactive)
Loading/Progress:
- Progress bars:
w-full bg-blue-200 rounded-full h-2withbg-blue-600 h-2 rounded-full - Loading containers:
bg-blue-50 rounded-lg border border-blue-200
Empty States:
- Text:
text-center py-8 text-gray-500
Breakpoints:
- Use
lg:prefix for large screens (desktop-first approach) - Default mobile-first styling
- Grid responsive:
lg:grid-cols-2for desktop, single column on mobile
When creating index pages with tables, follow these patterns for consistency:
Implement consistent controller patterns:
def index
authorize ModelName
@tab = params[:tab] || "all"
query = current_account
.model_names
.includes(:associations)
# Filter by status if not "all" tab (assumes tab names match enum values)
query = query.where(status: @tab) unless @tab == "all"
query = query.order(created_at: :desc)
@pagy, @records = pagy(
query,
page: params[:page],
trim_extra: false,
limit_extra: true
)
endContent Styling:
text-sm text-gray-900- Primary text contenttext-xs text-gray-500- Secondary/subtitle texttext-xs text-gray-400- Tertiary/timestamp textmax-w-md truncate- Truncate long text with max widthmt-0.5- Small spacing between stacked content
Create dedicated status badge components:
class ModelName::StatusBadgeComponent < ApplicationComponent
def initialize(record)
@record = record
end
def call
color = {
pending: "blue",
success: "green",
failed: "orange",
error: "red"
}.fetch(@record.status.to_sym, "gray")
tooltip_text = @record.status_message if @record.status_message.present?
render Ui::BadgeComponent.new(@record.status.titlecase, color, tooltip: tooltip_text)
end
endThis pattern ensures consistent table layouts, proper CSS usage, and maintainable code structure across all index pages.
- Implement proper authentication and authorization (Devise, Pundit)
- Use strong parameters in controllers
- Protect against common web vulnerabilities (XSS, CSRF, SQL injection)
- Use database indexing effectively
- Implement caching strategies (fragment caching, Russian Doll caching)
- Use eager loading to avoid N+1 queries
- Optimize database queries using includes, joins, or select
- Use exceptions for exceptional cases, not for control flow
- Implement proper error logging and user-friendly messages
- Use ActiveModel validations in models
- Handle errors gracefully in controllers and display appropriate flash messages
- Use
bin/dev(Overmind/Foreman) to run development processes - Follow
README.mdinstructions for setup
- Use Rubocop for linting with autofix:
bin/rubocop -A - Use ERB-Lint for ERB files:
bundle exec erblint - Run tests frequently:
bin/rspec
- Use
rails consolefor interactive debugging - Use
binding.irbfor breakpoints in development - Check logs in
log/development.log - Use Rails generators for scaffolding:
rails g model,rails g controller
Factory Management:
- Lean factories;
build_stubbedfor unit tests;createonly if DB matters - Use traits over callbacks for factory variations
- Add
FactoryBot.lintto CI to catch factory issues early - Keep factories minimal - only include required attributes by default
Test Data & Performance:
- Parallelize specs for faster execution
- Use
freeze_timefor consistent time-based testing - Disable external HTTP via WebMock/VCR for reliable tests
- Mock only at boundaries (HTTP, external services), never internals
Test Structure Guidelines:
- One assertion concept per example; refactor relentlessly
- Keep test descriptions clear and behavior-focused
- Use
letfor lazy-loaded test data - Group related tests in
contextblocks with clear descriptions
Services (following Service concern patterns):
- Test class methods (
.run) not instance methods for Service concern - Mock external dependencies but avoid deep mocking
- Use the service matchers from
spec/support/matchers/services.rb - Don't create specs for private methods - test through public interface
ViewComponents:
- Test rendering + key text/ARIA attributes
- For Hotwire components, assert Turbo Stream payloads in request specs
- Component spec for HTML/data-controller wiring
- Keep component tests focused on rendering behavior
Models:
- Use shoulda-matchers for validations/associations
- Test only custom business logic, not Rails functionality
- Focus on edge cases and business rules
Request Specs (Primary Integration Layer):
- Default to 3-4 examples per endpoint:
- Happy path (200/201)
- Authentication/authorization failure (401/403)
- Validation failure for invalid input (422)
- Not found (404) only if behavior is non-standard
- Assert status, JSON schema/shape, Turbo Stream target/action
- Test jobs enqueued, emails sent, DB changes
- Keep examples minimal - avoid minor parameter variations
System Tests (Minimal Usage):
- Only for critical paths needing end-to-end verification
- JavaScript-dependent features: 1 happy path, 1 sad path
- Focus on key workflows (signup, payments, core features)
- API Responses: Status codes, JSON structure, error messages
- Turbo Streams: Target elements, action types, content updates
- Background Jobs: Proper job enqueueing with expected arguments
- Database Changes: Record creation, updates, state transitions
- External Services: Proper API calls made (via mocking)
- User Experience: Flash messages, redirects, rendered content
Webhooks:
- Verify signature validation
- Use real sample payloads from fixtures
- Test both successful processing and error handling
Time-Dependent Code:
- Use
freeze_timefor consistent results - Test edge cases around time boundaries
- Mock time-sensitive external API responses
Multi-Tenant Testing:
- Ensure proper tenant scoping in all tests
- Test cross-tenant data isolation
- Use appropriate tenant context in factory setup
- Always read existing files before making changes to understand current patterns
- Prefer editing existing files over creating new ones
- Follow existing code patterns and conventions found in the codebase
- Run tests after making changes:
bin/rspec - Write tests for new functionality
- Use existing test patterns found in
spec/directory - When creating specs for services that use the Service concern, use the matchers on spec/support/matchers/services.rb
- Do not create specs for services private methods
- Check current working directory and file structure using available tools
- Use search tools to understand how similar functionality is implemented
- Look at related files to understand patterns and conventions
You have access to a full test environment and should always ensure code quality before finishing work:
- Always write and run RSpec specs for new functionality
- Prefer request specs over system tests for better performance and reliability
- Use existing test patterns and factories found in the codebase
- Run
bin/rspecto execute tests and verify functionality - Tests must pass before work is considered complete
- Always run
bin/rubocop -Abefore finishing work to fix linting errors - This command will automatically fix most linting issues
- Ensure all linting errors are resolved before submitting changes
- Code quality checks are mandatory, not optional
- Implement functionality
- Write comprehensive RSpec tests (preferring request specs)
- Run
bin/rspecto verify tests pass - Run
bin/rubocop -Ato fix linting issues - Re-run tests if linting changes affected functionality
- Only consider work complete when both tests pass and linting is clean
When suggesting code, always consider the business context and follow the established patterns and conventions outlined above.
These insights come from practical development work and complement the instructional guidelines above.
- PR reviews often highlight patterns already in the codebase - When reviewers suggest using specific validators or patterns, check how similar functionality is implemented elsewhere
- Security concerns are iterative - Address immediate concerns but recognize that comprehensive security often requires multiple layers
- Don't over-engineer security - Match the security level to what's already established in the codebase
- Controller complexity is a common code smell - Actions over 20-30 lines often indicate business logic that belongs elsewhere
- Service objects are powerful for complex operations - They isolate business logic, improve testability, and make controllers thin
- Extract common code into private methods - Duplicated code in controllers often indicates missing abstractions
- Understand the testing framework's patterns - Service specs may use different patterns than controller specs (e.g., class methods vs instance methods)
- Mock at appropriate boundaries - Mock external dependencies but be careful about mocking too deeply
- Test fixtures need to match validations - When adding validations, ensure test factories are updated accordingly
- ActiveStorage validators exist - Don't write manual file validations when Rails provides cleaner declarative options
- Database constraints should match business logic - If a field is truly required, enforce it at the database level
- Follow existing patterns - New code should match the style and patterns already established in the codebase
- Run tests frequently during refactoring - Catch issues early rather than accumulating multiple problems
- Use linters consistently - Run rubocop after changes to maintain code quality
- Read error messages carefully - Many issues (like Service::Error handling) become clear from stack traces
- Always add an empty line at end of files
These learnings come from conducting thorough code reviews and quality assessments:
- Listen to user intent carefully - When asked to review work, don't assume you need to make edits. Sometimes users want analysis and reporting only.
- Multi-agent analysis is powerful - Breaking down complex reviews into specialized agents (code quality, security, testing, business impact) provides comprehensive coverage without overwhelming the user.
- Test failures matter even if "just setup issues" - Failing tests, regardless of cause, represent technical debt and reduce confidence in the codebase.
- Simple implementations can have complex implications - A straightforward scope addition can impact reporting, user workflows, support processes, and more.
- Consider the full ecosystem - Changes to data visibility affect not just the immediate UI but also analytics, customer service, and user expectations.
- Documentation gaps compound over time - When code uses magic values (like status strings), the lack of documentation makes maintenance harder.
- Consistency is crucial for UX - Hidden items in lists but accessible directly creates cognitive dissonance for users.
- Quality assessment should be holistic - Technical correctness is just one dimension; operational readiness, user impact, and maintainability are equally important.