built out personal preferences for AI tools
This commit is contained in:
54
CLAUDE.md
Normal file
54
CLAUDE.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Derek's AI Preferences
|
||||
|
||||
This file defines how AI assistants should work with me. Detailed references are in `preferences/`.
|
||||
|
||||
---
|
||||
|
||||
## Communication Style
|
||||
|
||||
- **Default:** Give a brief summary — what it does and why, not a full walkthrough
|
||||
- **On request:** Go deep. If I ask for detail, explain fully with context
|
||||
- **Format:** Bullet points and structured output always; avoid prose paragraphs
|
||||
- **Before working:** Ask clarifying questions first — don't assume and do a bunch of work that needs to be redone
|
||||
- **Tone:** Direct and concise; no filler, no preamble
|
||||
|
||||
See [preferences/communication.md](preferences/communication.md) for full detail.
|
||||
|
||||
---
|
||||
|
||||
## My Stack
|
||||
|
||||
| Context | Technologies |
|
||||
|---|---|
|
||||
| Org tooling (IT support) | PowerShell 5.1+, WPF, `.psd1` config files |
|
||||
| Web apps (self-hosted) | FastAPI, async SQLAlchemy 2.0, MySQL 8, Docker Compose, Nginx |
|
||||
| Static site | Hugo |
|
||||
| Container management | Portainer |
|
||||
| Version control | Gitea (self-hosted) |
|
||||
|
||||
---
|
||||
|
||||
## Always Do
|
||||
|
||||
- **Tests first:** Always provide a way to immediately test/run the code you write
|
||||
- **No credentials in code:** Secrets go in `.env` files; always provide a `.env.example`
|
||||
- **Consistent style:** Follow the conventions for the language in use — see preferences files
|
||||
- **Ask before big changes:** Clarify scope before rewriting or restructuring
|
||||
|
||||
---
|
||||
|
||||
## Never Do
|
||||
|
||||
- Commit automatically unless explicitly asked
|
||||
- Add features, refactoring, or cleanup beyond what was asked
|
||||
- Put credentials, passwords, or tokens directly in code or config files
|
||||
- Switch to a different stack/library without asking first
|
||||
|
||||
---
|
||||
|
||||
## Detailed References
|
||||
|
||||
- [preferences/communication.md](preferences/communication.md) — Response style and interaction rules
|
||||
- [preferences/powershell-wpf.md](preferences/powershell-wpf.md) — PowerShell and WPF coding standards
|
||||
- [preferences/python-web.md](preferences/python-web.md) — FastAPI, SQLAlchemy, Docker standards
|
||||
- [preferences/project-structure.md](preferences/project-structure.md) — Standard folder layouts per project type
|
||||
123
README.md
123
README.md
@@ -1,2 +1,123 @@
|
||||
# AI
|
||||
# AI Preferences
|
||||
|
||||
A personal repository of markdown files that define how AI systems should work with me. Instead of re-explaining preferences every session, these files act as a persistent "operating manual" that can be loaded into any AI tool.
|
||||
|
||||
---
|
||||
|
||||
## What's in This Repo
|
||||
|
||||
```
|
||||
AI/
|
||||
├── CLAUDE.md # Main entry point — loaded automatically by Claude Code
|
||||
└── preferences/
|
||||
├── communication.md # How I like responses structured and how to interact
|
||||
├── powershell-wpf.md # Standards for PowerShell and WPF applications
|
||||
├── python-web.md # Standards for FastAPI, SQLAlchemy, Docker Compose
|
||||
└── project-structure.md # Standard folder layouts per project type
|
||||
```
|
||||
|
||||
**`CLAUDE.md`** is the top-level summary. It references the `preferences/` files for detail. This is the file you point AI tools to — or it gets picked up automatically.
|
||||
|
||||
**`preferences/`** contains the detail files. Each covers a specific domain. You generally don't point tools directly at these; `CLAUDE.md` references them for context.
|
||||
|
||||
---
|
||||
|
||||
## How to Use These Files
|
||||
|
||||
### Claude Code (Automatic)
|
||||
|
||||
Claude Code automatically reads `CLAUDE.md` from the root of any project it's working in.
|
||||
|
||||
**Option A — Per project:** Copy `CLAUDE.md` into the root of any project repo. Claude Code will pick it up automatically every session.
|
||||
|
||||
**Option B — Global (recommended):** Place `CLAUDE.md` in your Claude global config folder so it applies to *every* project without copying:
|
||||
|
||||
```
|
||||
# Windows
|
||||
Copy-Item "CLAUDE.md" "$env:USERPROFILE\.claude\CLAUDE.md"
|
||||
```
|
||||
|
||||
> Note: If a project also has its own `CLAUDE.md`, Claude Code merges both — global preferences apply everywhere, project-specific ones add on top.
|
||||
|
||||
---
|
||||
|
||||
### Cursor
|
||||
|
||||
Cursor reads a `.cursorrules` file in the project root.
|
||||
|
||||
1. Copy the content of `CLAUDE.md` (and any relevant `preferences/` files) into a file named `.cursorrules` in your project root
|
||||
2. Cursor will load it automatically as context for all AI interactions in that project
|
||||
|
||||
---
|
||||
|
||||
### GitHub Copilot (VS Code)
|
||||
|
||||
Copilot reads custom instructions from VS Code settings or a `.github/copilot-instructions.md` file.
|
||||
|
||||
1. In your project, create `.github/copilot-instructions.md`
|
||||
2. Paste the content from `CLAUDE.md` into that file
|
||||
3. Copilot will use it as persistent context for that workspace
|
||||
|
||||
---
|
||||
|
||||
### ChatGPT / Claude.ai (Manual)
|
||||
|
||||
These tools don't auto-load files, so you paste preferences at the start of a conversation.
|
||||
|
||||
1. Open `CLAUDE.md`
|
||||
2. Copy the full contents
|
||||
3. Paste it as the first message in a new conversation with the instruction: *"These are my preferences. Follow them for this session."*
|
||||
|
||||
---
|
||||
|
||||
### Other AI Tools
|
||||
|
||||
Most AI tools support some form of system prompt or custom instructions. The general approach:
|
||||
|
||||
1. Use `CLAUDE.md` as your base — it's written to be tool-agnostic
|
||||
2. Look for the tool's "system prompt", "custom instructions", or "rules" setting
|
||||
3. Paste the contents of `CLAUDE.md` there
|
||||
|
||||
---
|
||||
|
||||
## Keeping Preferences Up to Date
|
||||
|
||||
Preferences should evolve as your stack and habits change. Treat this repo like any other project — update it when something no longer matches how you work.
|
||||
|
||||
### When to Update
|
||||
|
||||
- You adopt a new tool or framework and want it recognized as your standard
|
||||
- You find yourself correcting an AI on the same thing repeatedly — that's a sign it should be in the docs
|
||||
- A project convention changes (e.g., switching from one library to another)
|
||||
- You want to add a new project type template to `project-structure.md`
|
||||
|
||||
### How to Update
|
||||
|
||||
**Small change (tweak a preference):**
|
||||
1. Open the relevant `preferences/` file
|
||||
2. Edit the specific rule
|
||||
3. Commit: `git commit -m "Update [topic] preference"`
|
||||
|
||||
**New topic area (e.g., you start working with a new stack):**
|
||||
1. Create a new file in `preferences/` — e.g., `preferences/hugo-blog.md`
|
||||
2. Add a link to it in the `CLAUDE.md` Detailed References section
|
||||
3. Commit both files together
|
||||
|
||||
**Removing something outdated:**
|
||||
1. Delete or edit the relevant section
|
||||
2. If a whole `preferences/` file is no longer relevant, delete it and remove its link from `CLAUDE.md`
|
||||
|
||||
### After Updating
|
||||
|
||||
- If you're using the **global Claude Code** setup, the update applies automatically next session
|
||||
- If you've **copied** `CLAUDE.md` into other project repos, re-copy the updated version to those projects
|
||||
- If you're using **ChatGPT/Claude.ai** manually, you'll be pasting the updated file next time
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
- **`CLAUDE.md` should stay concise** — it's the quick-reference. Put detail in `preferences/` files
|
||||
- **Be specific, not general** — "use SQLAlchemy 2.0 async" is more useful than "use good database practices"
|
||||
- **Version your preferences** — git history lets you see what changed and roll back if needed
|
||||
- **One preference file per domain** — don't cram everything into one file; keep it organized so it's easy to find and update
|
||||
|
||||
37
preferences/communication.md
Normal file
37
preferences/communication.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Communication Preferences
|
||||
|
||||
## Response Depth
|
||||
|
||||
- **Default level:** Brief summary — what the code/change does, key decisions made, nothing more
|
||||
- **Deep detail:** When I ask "explain this", "why does this work", or "walk me through it" — go full depth with context, reasoning, and examples
|
||||
- **Never assume** I want the full explanation unless I ask; don't pad responses
|
||||
|
||||
## Format Rules
|
||||
|
||||
- Always use **bullet points** and **structured output** (headers, tables, lists)
|
||||
- Avoid prose paragraphs unless explaining a concept I asked about
|
||||
- Use code blocks for all code, commands, and file paths
|
||||
- Keep section headers short and scannable
|
||||
|
||||
## Before Doing Work
|
||||
|
||||
- **Ask clarifying questions first** — it's better to pause and confirm than to do work that gets thrown away
|
||||
- If a request is ambiguous, state your interpretation and ask if it's correct before proceeding
|
||||
- If a task will touch many files or make large structural changes, outline the plan first and get confirmation
|
||||
|
||||
## When Something Is Unclear
|
||||
|
||||
- Say so directly — don't guess silently
|
||||
- Offer 2–3 specific options to choose from rather than an open-ended question when possible
|
||||
|
||||
## Errors and Blockers
|
||||
|
||||
- Report errors clearly: what failed, why (if known), and what the options are
|
||||
- Don't retry the same failing approach repeatedly — propose an alternative
|
||||
|
||||
## Things to Avoid
|
||||
|
||||
- Filler phrases ("Great question!", "Certainly!", "Of course!")
|
||||
- Restating what I just said before answering
|
||||
- Explaining things I didn't ask about
|
||||
- Suggesting improvements or refactors beyond the scope of what was asked
|
||||
121
preferences/powershell-wpf.md
Normal file
121
preferences/powershell-wpf.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# PowerShell & WPF Standards
|
||||
|
||||
## General Rules
|
||||
|
||||
- Always include `#Requires -Version 5.1` at the top of every `.ps1` file
|
||||
- Use `$script:` scope for module-level variables — never `$Global:` unless absolutely necessary
|
||||
- Keep credential and sensitive values out of code — load from config files or environment variables only
|
||||
- Use `.psd1` files for all external configuration (not JSON, not XML, not hardcoded)
|
||||
|
||||
## Code Structure
|
||||
|
||||
- Organize code using `#region` / `#endregion` blocks with descriptive names
|
||||
- Group related functions within their own region
|
||||
- One function per logical task — keep functions focused
|
||||
|
||||
```powershell
|
||||
#region Configuration Functions
|
||||
|
||||
function Initialize-Config {
|
||||
...
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Functions
|
||||
|
||||
function Show-MainWindow {
|
||||
...
|
||||
}
|
||||
|
||||
#endregion
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- **Functions:** PascalCase Verb-Noun (`Get-UserConfig`, `Show-MainWindow`, `Initialize-App`)
|
||||
- **Variables:** PascalCase (`$UserConfig`, `$ScriptRoot`, `$AppVersion`)
|
||||
- **Script-scoped:** `$script:VariableName`
|
||||
- Use approved PowerShell verbs (`Get-`, `Set-`, `Show-`, `New-`, `Remove-`, `Initialize-`, `Invoke-`, etc.)
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Use `try/catch` for all operations that can fail (file I/O, network, registry, etc.)
|
||||
- Use `Write-Warning` for non-fatal errors that should be logged but allow execution to continue
|
||||
- Use `Write-Error` for failures that should stop the current operation
|
||||
- Create centralized helper functions for user-facing messages:
|
||||
|
||||
```powershell
|
||||
function Show-Error { param([string]$Message, [string]$Title = "Error") ... }
|
||||
function Show-Warning { param([string]$Message, [string]$Title = "Warning") ... }
|
||||
function Show-Info { param([string]$Message, [string]$Title = "Information") ... }
|
||||
```
|
||||
|
||||
## WPF Specifics
|
||||
|
||||
- Always ensure STA thread mode for WPF — include an STA re-launch block at the top of `.ps1` entry points
|
||||
- Load all WPF assemblies at the top of the file:
|
||||
|
||||
```powershell
|
||||
Add-Type -AssemblyName PresentationFramework
|
||||
Add-Type -AssemblyName PresentationCore
|
||||
Add-Type -AssemblyName WindowsBase
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
```
|
||||
|
||||
- Define XAML as a here-string and load it via `[System.Windows.Markup.XamlReader]::Parse()`
|
||||
- Use `$script:` variables to hold window/control references so they're accessible across functions
|
||||
|
||||
## Modules
|
||||
|
||||
- Shared/reusable functions belong in a `.psm1` module under a `Modules/` folder
|
||||
- The main `.ps1` entry point imports the module at startup:
|
||||
|
||||
```powershell
|
||||
Import-Module "$script:AppRoot\Modules\ProjectName.psm1" -Force
|
||||
```
|
||||
|
||||
- Module files follow the same naming, scoping, and region conventions
|
||||
|
||||
## Configuration Files
|
||||
|
||||
- Use `.psd1` (PowerShell Data Files) for all config — they're native, typed, and easy to version
|
||||
- Always provide a default config creation block if the file doesn't exist
|
||||
- Config keys should use PascalCase nested hashtables:
|
||||
|
||||
```powershell
|
||||
@{
|
||||
Application = @{
|
||||
WindowWidth = 800
|
||||
WindowHeight = 600
|
||||
}
|
||||
Email = @{
|
||||
SMTPServer = ''
|
||||
From = ''
|
||||
To = ''
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Never put actual credentials in `.psd1` config — use placeholder strings with comments
|
||||
|
||||
## Testing
|
||||
|
||||
- Use **Pester** for unit testing
|
||||
- Always provide a Pester test file alongside new functions
|
||||
- At minimum, test: happy path, expected failure cases, and edge cases
|
||||
- Run tests with: `Invoke-Pester -Path .\Tests\ -Output Detailed`
|
||||
|
||||
```powershell
|
||||
# Tests/Get-UserConfig.Tests.ps1
|
||||
Describe "Get-UserConfig" {
|
||||
It "Returns default config when file does not exist" { ... }
|
||||
It "Loads config from valid .psd1 file" { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
- Comment the **why**, not the **what** — code should be readable on its own
|
||||
- Use inline comments for non-obvious logic
|
||||
- Use block comments (`<# ... #>`) for function documentation (`.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.EXAMPLE`)
|
||||
116
preferences/project-structure.md
Normal file
116
preferences/project-structure.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Standard Project Structures
|
||||
|
||||
## PowerShell / WPF Application
|
||||
|
||||
For IT tooling and local GUI applications.
|
||||
|
||||
```
|
||||
ProjectName/
|
||||
├── ProjectName.ps1 # Main entry point — UI, startup, STA check
|
||||
├── company-settings.psd1 # External config — no credentials
|
||||
├── README.md
|
||||
├── Modules/
|
||||
│ └── ProjectName.psm1 # Shared functions, config loading, helpers
|
||||
├── Tests/
|
||||
│ └── *.Tests.ps1 # Pester test files
|
||||
└── Release-Notes/
|
||||
├── v1.0.md
|
||||
└── v1.1.md
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Entry `.ps1` handles: STA check, assembly loading, module import, config load, show window
|
||||
- All reusable logic lives in the `.psm1` module, not inline in the entry point
|
||||
- Config file is always in the same directory as the script
|
||||
- `Release-Notes/` uses one file per version: `v{major}.{minor}.md`
|
||||
|
||||
---
|
||||
|
||||
## FastAPI Web Application (Docker Compose)
|
||||
|
||||
For self-hosted web apps running behind Nginx.
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── docker-compose.yml
|
||||
├── .env.example # Always committed — placeholder values only
|
||||
├── .gitignore # Must include .env
|
||||
├── README.md
|
||||
├── nginx/
|
||||
│ └── default.conf
|
||||
└── backend/
|
||||
├── Dockerfile
|
||||
├── requirements.txt
|
||||
└── app/
|
||||
├── main.py
|
||||
├── config.py
|
||||
├── database.py
|
||||
├── dependencies.py
|
||||
├── models/
|
||||
│ └── user.py
|
||||
├── schemas/
|
||||
│ └── user.py
|
||||
├── routers/
|
||||
│ ├── auth.py
|
||||
│ └── [resource].py
|
||||
├── utils/
|
||||
└── tests/
|
||||
└── test_[resource].py
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- `.env` is never committed — `.env.example` always is
|
||||
- Each resource (users, items, etc.) gets its own model, schema, and router file
|
||||
- Tests live inside `backend/app/tests/`
|
||||
|
||||
---
|
||||
|
||||
## Hugo Blog (chns.tech)
|
||||
|
||||
Static site for tech blog posts.
|
||||
|
||||
```
|
||||
site/
|
||||
├── hugo.toml # Site config
|
||||
├── content/
|
||||
│ └── posts/
|
||||
│ └── YYYY-MM-DD-post-title.md
|
||||
├── themes/
|
||||
├── static/
|
||||
└── layouts/
|
||||
```
|
||||
|
||||
**Post front matter standard:**
|
||||
```yaml
|
||||
---
|
||||
title: "Post Title"
|
||||
date: YYYY-MM-DD
|
||||
draft: false
|
||||
tags: ["tag1", "tag2"]
|
||||
---
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Git Conventions
|
||||
|
||||
- **Branch:** Commit directly to `main` (solo projects)
|
||||
- **Commit messages:** Short imperative summary — `Add user auth`, `Fix config loading`, `Update README`
|
||||
- **Never commit:** `.env`, credentials, compiled binaries, `node_modules`, `__pycache__`
|
||||
- **Always include** a `.gitignore` appropriate for the project type
|
||||
|
||||
### Standard `.gitignore` entries
|
||||
|
||||
**PowerShell projects:**
|
||||
```
|
||||
*.exe
|
||||
company-settings.psd1 # If it contains org-specific paths
|
||||
```
|
||||
|
||||
**Python projects:**
|
||||
```
|
||||
.env
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.pytest_cache/
|
||||
```
|
||||
148
preferences/python-web.md
Normal file
148
preferences/python-web.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Python Web Stack Standards
|
||||
|
||||
## Stack
|
||||
|
||||
- **API:** FastAPI (async)
|
||||
- **ORM:** async SQLAlchemy 2.0 with ORM models (not raw queries)
|
||||
- **Database:** MySQL 8
|
||||
- **Runtime:** Docker Compose
|
||||
- **Reverse proxy:** Nginx
|
||||
- **Container management:** Portainer
|
||||
|
||||
## Project Layout
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── docker-compose.yml
|
||||
├── .env.example # Template — always include this, never commit .env
|
||||
├── .gitignore
|
||||
├── README.md
|
||||
├── nginx/
|
||||
│ └── default.conf
|
||||
└── backend/
|
||||
├── Dockerfile
|
||||
├── requirements.txt
|
||||
└── app/
|
||||
├── main.py # App entry point, router registration, startup events
|
||||
├── config.py # Settings loaded from environment via pydantic-settings
|
||||
├── database.py # Async SQLAlchemy engine and session factory
|
||||
├── dependencies.py # Shared FastAPI dependencies (auth, db session, etc.)
|
||||
├── models/ # SQLAlchemy ORM table definitions
|
||||
├── schemas/ # Pydantic request/response models
|
||||
├── routers/ # API endpoint handlers, one file per resource
|
||||
└── utils/ # Shared helpers
|
||||
```
|
||||
|
||||
## Configuration & Secrets
|
||||
|
||||
- All secrets and environment-specific values go in `.env` — never hardcode them
|
||||
- Always provide `.env.example` with placeholder values and comments explaining each variable
|
||||
- Load settings via `pydantic-settings`:
|
||||
|
||||
```python
|
||||
# app/config.py
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
class Settings(BaseSettings):
|
||||
db_host: str
|
||||
db_port: int = 3306
|
||||
db_name: str
|
||||
db_user: str
|
||||
db_password: str
|
||||
secret_key: str
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
|
||||
settings = Settings()
|
||||
```
|
||||
|
||||
## Database (SQLAlchemy 2.0 Async)
|
||||
|
||||
- Use async engine and session factory
|
||||
- Define models with `DeclarativeBase`
|
||||
- One model per file in `models/`
|
||||
|
||||
```python
|
||||
# app/database.py
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
engine = create_async_engine(DATABASE_URL, echo=False)
|
||||
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
```
|
||||
|
||||
- Inject DB sessions via FastAPI dependencies (not global sessions)
|
||||
- Use `async with session.begin()` for transactions
|
||||
|
||||
## Models
|
||||
|
||||
- One file per table in `models/`
|
||||
- Use typed columns with SQLAlchemy 2.0 `Mapped` syntax:
|
||||
|
||||
```python
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.database import Base
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
username: Mapped[str] = mapped_column(unique=True)
|
||||
email: Mapped[str]
|
||||
```
|
||||
|
||||
## Schemas (Pydantic)
|
||||
|
||||
- Separate schemas for Create, Update, and Response — don't reuse the same schema for all three
|
||||
- Response schemas should never expose password hashes or internal fields
|
||||
|
||||
```python
|
||||
class UserCreate(BaseModel): ...
|
||||
class UserResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
```
|
||||
|
||||
## Routers
|
||||
|
||||
- One router file per resource (e.g., `routers/users.py`, `routers/auth.py`)
|
||||
- Register all routers in `main.py` with a consistent prefix and tags
|
||||
|
||||
## Docker Compose
|
||||
|
||||
- Use named volumes for persistent data (database, uploads)
|
||||
- Always define a `healthcheck` for the database service
|
||||
- Use `depends_on` with `condition: service_healthy` for the backend
|
||||
- Keep the Nginx config in a `nginx/` folder and mount it as a volume
|
||||
|
||||
```yaml
|
||||
services:
|
||||
db:
|
||||
image: mysql:8
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
- Use **pytest** with **pytest-asyncio** for async tests
|
||||
- Always provide a test for each router endpoint at minimum (happy path + 1 failure case)
|
||||
- Use a separate test database — never run tests against the real DB
|
||||
- Run with: `pytest -v`
|
||||
|
||||
## Comments
|
||||
|
||||
- Docstrings on all functions that aren't self-explanatory
|
||||
- Comment the **why** for non-obvious logic, not the what
|
||||
Reference in New Issue
Block a user