Rulesets

Guide your AI to follow your team's coding standards, best practices, and project-specific conventions. Rulesets ensure consistent, high-quality code generation across your entire project.

Written By Nick Gatzoulis

Last updated 4 months ago

What are rulesets?

Rulesets are Markdown files that instruct the AI how to write code for your project. They live in your GitHub repository and guide every code generation decision.

Why use rulesets:

  • Enforce standards: Define naming conventions, code style, and architecture patterns

  • Integrate tools: Add guidelines for Supabase, Stripe, or other services you use

  • Save time: The AI follows your rules automatically—no need to repeat instructions

  • Maintain quality: Prevent common mistakes by documenting what to avoid

  • Onboard faster: New team members see your conventions in action

How it works: When you send a prompt, App2's AI agents read your rulesets before writing code. This ensures every generated file matches your team's standards.

Understanding ruleset types

App2 provides two types of rulesets that work together.

Default rulesets

App2 creates these automatically when your project starts. They provide baseline code quality guidelines.

Included defaults:

  • shared-guidelines.md - Universal best practices for all projects

  • web-guidelines.md or mobile-guidelines.md - Template-specific standards

  • Integration rules under integrations/<service>/ - Guidelines for Supabase, Stripe, etc.

Key points:

  • Generated based on your project template (web or mobile)

  • Located at rules/default/ in your repository

  • You can edit defaults to match your preferences

  • Cannot be deleted (but you can override them)

  • Auto-restored if missing during sandbox sync

Custom rulesets

Create your own rules to enforce team-specific conventions.

Use cases:

  • Company coding standards

  • API design patterns

  • Error handling requirements

  • Testing strategies

  • Documentation requirements

  • Security policies

Key points:

  • Located at rules/custom/ in your repository

  • Organize in folders for better structure

  • Fully editable and deletable

  • Work alongside defaults

  • Committed directly to your GitHub repo

Accessing rulesets

Find the rulesets manager in your project settings.

How to open:

  1. Open your project in App2

  2. Click Settings in the top navigation

  3. Select Rulesets from the settings menu

  4. View all your project's rulesets in the tree view

What you'll see:

  • Left panel: Tree view of Default and Custom folders

  • Right panel: Markdown editor with Write/Preview tabs

  • Actions: New Rule, Update Rule, Delete (custom only)

Note: Rulesets are only editable when your project status is READY or FAILED.

Managing rulesets

Viewing a ruleset

Steps:

  1. Open SettingsRulesets

  2. Expand Default or Custom folders in the tree

  3. Click a ruleset file to view its content

  4. Switch between Write and Preview tabs

The Preview tab renders your Markdown so you can verify formatting.

Creating a custom ruleset

Steps:

  1. Click New Rule button (top right)

  2. Choose Custom rules location (default rules are read-only)

  3. Enter an optional folder path:

    • Leave empty to save in root: rules/custom/

    • Use subfolders: backend, frontend/components, etc.

  4. Enter filename (automatically adds .md extension)

  5. Review the resulting path preview

  6. Click Continue

  7. Write your ruleset content in Markdown

  8. Click Create Rule

Example paths:

  • rules/custom/api-conventions.md - Root level

  • rules/custom/backend/error-handling.md - Organized by area

  • rules/custom/frontend/components/button-patterns.md - Deeply nested

Naming tips:

  • Use descriptive names: authentication-standards.md, not rules1.md

  • Use kebab-case for filenames: code-review-checklist.md

  • Group related rules in folders: backend/, frontend/, integrations/

Tip: Create a rules/custom/team-conventions.md file as your starting point for team standards.

Editing a ruleset

Steps:

  1. Select the ruleset from the tree view

  2. Modify content in the Write tab

  3. Preview changes in the Preview tab

  4. Click Update Rule when finished

  5. Changes commit automatically to GitHub

For default rulesets: You can update defaults to match your preferences. Your changes are preserved even when the sandbox restarts.

For custom rulesets: Edit freely. Changes sync immediately to your repository.

Deleting a custom ruleset

Steps:

  1. Select the custom ruleset in the tree

  2. Click Delete button

  3. Confirm deletion

  4. File is removed from your repository

Warning: Only custom rulesets can be deleted. Default rulesets cannot be removed.

Best practices

Write effective rulesets that guide the AI without being overly restrictive.

Structure your rules

Use clear sections:

# API Design Standards

## Naming conventions
- Use camelCase for variable names
- Use PascalCase for component names
- Use SCREAMING_SNAKE_CASE for constants

## Error handling
- Always wrap async functions in try-catch blocks
- Return typed errors, never throw strings
- Log errors with context using logger.error()

## Response format
All API endpoints must return:
{
  "success": boolean,
  "data": T | null,
  "error": { "code": string, "message": string } | null
}

Keep it scannable:

  • Use headings (H2, H3) for major sections

  • Use bullet points for lists of rules

  • Include code examples for clarity

  • Add inline code formatting with backticks

Be specific and actionable

Good examples:

  • "Use async/await instead of .then() chains for all asynchronous operations"

  • "Import UI components from @/components/ui using named imports"

  • "Prefix all database query functions with db (e.g., dbGetUser, dbCreatePost)"

Avoid vague rules:

  • "Write good code" (too vague)

  • "Don't make mistakes" (not actionable)

  • "Follow best practices" (unclear what practices)

Include examples

Show the AI exactly what you want:

## Button components

Use the Button component from our design system:

```tsx
import { Button } from '@/components/ui/button';

// Good
<Button variant="primary" size="md" onClick={handleClick}>
  Save Changes
</Button>

// Avoid
<button className="custom-button" onClick={handleClick}>
  Save Changes
</button>

### Explain the why

Help the AI understand the reasoning behind rules:

```markdown
## State management

Use Zustand for global state instead of Context API.

Why: Zustand provides better performance with selective subscriptions and simpler syntax. Context re-renders all consumers on any state change.

Example:
// store/useAuthStore.ts
import { create } from 'zustand';

export const useAuthStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

Organize by concern

Create focused rulesets:

  • authentication.md - Auth patterns, session handling

  • database.md - Query patterns, migrations, indexes

  • testing.md - Test structure, naming, coverage

  • styling.md - CSS conventions, theme usage

This keeps rules discoverable and maintainable.

Examples

# Team Coding Standards

## TypeScript rules
- Enable strict mode in tsconfig.json
- Never use `any` type—always define proper types
- Export types alongside implementation
- Use named exports over default exports

## Import order
Organize imports in this order:
1. React and framework imports
2. Third-party libraries
3. Local components
4. Local utilities
5. Type imports
6. CSS imports

## Function naming
- Event handlers: `handleClick`, `handleSubmit`
- Data fetchers: `fetchUsers`, `loadProfile`
- Boolean checks: `isLoading`, `hasError`, `canEdit`
- Transformers: `formatDate`, `parseResponse`

## Error boundaries
Wrap all route components with ErrorBoundary:

```tsx
<ErrorBoundary fallback={<ErrorPage />}>
  <YourComponent />
</ErrorBoundary>

### Example 2: Supabase integration rules

```markdown
# Supabase Best Practices

## Database queries
- Use Row Level Security (RLS) for all tables
- Create policies for `SELECT`, `INSERT`, `UPDATE`, `DELETE`
- Test queries with different user roles

## Client initialization
Initialize once and reuse:

```typescript
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);