home / skills / srbhr / resume-matcher / backend-dev

backend-dev skill

/.agents/skills/backend-dev

This skill helps you design and implement robust backend endpoints, schemas, and LLM integration for Resume Matcher.

npx playbooks add skill srbhr/resume-matcher --skill backend-dev

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.9 KB
---
name: backend-dev
description: |
  Backend development agent for Resume Matcher. Handles FastAPI endpoints, Pydantic schemas, TinyDB operations, LiteLLM integration, and Python service logic. Use when creating or modifying backend code.
metadata:
  author: resume-matcher
  version: "1.0.0"
allowed-tools: Bash(python:*) Bash(pip:*) Bash(uv:*) Read
---

# Backend Development Agent

> Use when creating or modifying FastAPI endpoints, Pydantic schemas, database operations, LLM integrations, or Python service logic.

## Before Writing Code

1. Read `docs/agent/architecture/backend-guide.md` for architecture
2. Read `docs/agent/apis/front-end-apis.md` for API contracts
3. Read `docs/agent/llm-integration.md` for LLM patterns
4. Check existing code in the relevant directory first

## Non-Negotiable Rules

1. **All functions MUST have type hints** - no exceptions
2. **Use `copy.deepcopy()`** for mutable defaults
3. **Log detailed errors server-side**, return generic messages to clients
4. **Use `asyncio.Lock()`** for shared resource initialization
5. **Pass API keys directly** to litellm via `api_key=`, never `os.environ`

## Project Structure

```
apps/backend/app/
├── main.py          # FastAPI app, CORS, lifespan, routers
├── config.py        # Pydantic BaseSettings from env
├── database.py      # TinyDB wrapper (JSON file storage)
├── llm.py           # LiteLLM wrapper (multi-provider)
├── routers/         # API endpoint handlers
├── services/        # Business logic layer
├── schemas/         # Pydantic request/response models
└── prompts/         # LLM prompt templates (Jinja2)
```

## Patterns

### New Endpoint

```python
from fastapi import APIRouter, HTTPException
from app.schemas.my_schema import MyRequest, MyResponse
from app.services.my_service import process_data
import logging

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1", tags=["my-feature"])

@router.post("/my-endpoint", response_model=MyResponse)
async def create_thing(request: MyRequest) -> MyResponse:
    try:
        result = await process_data(request)
        return result
    except Exception as e:
        logger.error(f"Failed to create thing: {e}")
        raise HTTPException(status_code=500, detail="Operation failed. Please try again.")
```

### New Schema

```python
from pydantic import BaseModel, Field

class MyRequest(BaseModel):
    name: str = Field(..., min_length=1, max_length=200)
    description: str | None = None

class MyResponse(BaseModel):
    id: str
    name: str
    status: str = "created"
```

### Database Operation

```python
import copy
from app.database import get_db

DEFAULT_DATA = {"sections": [], "metadata": {}}

async def get_or_create(doc_id: str) -> dict:
    db = get_db()
    existing = db.get(doc_id)
    if existing:
        return existing
    data = copy.deepcopy(DEFAULT_DATA)  # ALWAYS deepcopy mutable defaults
    db.insert(data)
    return data
```

### LLM Call

```python
from app.llm import get_completion
from app.config import settings

async def improve_text(text: str) -> str:
    prompt = f"Improve this resume text:\n\n{text}"
    result = await get_completion(
        prompt=prompt,
        model=settings.LLM_MODEL,
        api_key=settings.LLM_API_KEY,  # Pass directly, not via env
        json_mode=True,
    )
    return result
```

## Error Handling Pattern

```python
except Exception as e:
    logger.error(f"Operation failed: {e}")  # Detailed for server logs
    raise HTTPException(
        status_code=500,
        detail="Operation failed. Please try again."  # Generic for client
    )
```

## Checklist Before Committing

- [ ] All functions have type hints
- [ ] Mutable defaults use `copy.deepcopy()`
- [ ] Error handling logs details, returns generic messages
- [ ] New endpoints registered in `main.py` router includes
- [ ] Schemas defined for all request/response bodies
- [ ] API keys passed via `api_key=` parameter

Overview

This skill is a backend development agent for Resume Matcher that creates and modifies FastAPI endpoints, Pydantic schemas, TinyDB operations, LiteLLM integrations, and Python service logic. It enforces project conventions and helps implement robust, typed, and traceable backend code for resume matching features. Use this agent when you need safe, production-ready backend changes that follow the app architecture and integration patterns.

How this skill works

The agent inspects project layout and existing code, then generates or updates FastAPI routers, Pydantic request/response models, service functions, and TinyDB wrappers according to the project's patterns. It adds LLM call wrappers using LiteLLM, ensures API keys are passed directly to the library, and wires endpoints into the main app with proper error logging and typed signatures. The agent also applies non-negotiable rules like type hints, deepcopy for mutable defaults, and async locks for shared initialization.

When to use it

  • Adding new API endpoints for resume scoring, suggestions, or parsing.
  • Creating or updating Pydantic schemas for request/response contracts.
  • Implementing TinyDB read/write flows or default-document creation.
  • Integrating or modifying LiteLLM calls for resume improvement features.
  • Refactoring service logic to add type hints, logging, or error patterns.

Best practices

  • Always include full type hints on functions and method signatures.
  • Use copy.deepcopy() for any mutable default objects to avoid shared state.
  • Log detailed error context server-side but return generic client messages.
  • Pass LLM credentials directly via api_key= when calling LiteLLM functions.
  • Use asyncio.Lock() to guard one-time shared resource initialization.
  • Register new routers in main.py and define Pydantic schemas for all payloads.

Example use cases

  • Add a POST /api/v1/match endpoint that validates job+resume payloads and returns ranked matches.
  • Create Pydantic models for a resume-parsing response and wire them into the router.
  • Implement get_or_create database helper that deepcopies DEFAULT_DATA and inserts into TinyDB.
  • Wrap an LLM prompt for keyword suggestions using get_completion with api_key from settings.
  • Refactor an existing service to centralize error logging and convert sync code to async with locks.

FAQ

Should I ever store API keys in environment variables?

The project requires passing API keys directly to LiteLLM via api_key=; do not rely on hidden env access when calling the library from service code.

How should I handle mutable default configuration objects?

Always create copies with copy.deepcopy() before modifying or storing defaults to prevent shared-mutable-state bugs.