⚔ AI Battle

DeepSeek V4 vs Claude: Code Generation

The Prompt
Build a complete REST API endpoint in Python (FastAPI) for a task management system. Include: a SQLAlchemy model for tasks with id, title, description, status, priority, created_at, and updated_at fields. Create CRUD endpoints (GET list with filtering/pagination, GET by id, POST create, PUT update, DELETE). Add input validation with Pydantic, proper error handling, and return appropriate HTTP status codes. The code should be production-ready.
DeepSeek V4 DeepSeek V4 — more scaffolding, needs polish
DeepSeek V4 generated a comprehensive solution spanning 287 lines across three files:\n\n**models.py:**\n```python\nfrom sqlalchemy import Column, Integer, String, Text, DateTime, Enum\nfrom sqlalchemy.sql import func\nfrom database import Base\nimport enum\n\nclass TaskStatus(str, enum.Enum):\n pending = \"pending\"\n in_progress = \"in_progress\"\n completed = \"completed\"\n cancelled = \"cancelled\"\n\nclass TaskPriority(str, enum.Enum):\n low = \"low\"\n medium = \"medium\"\n high = \"high\"\n critical = \"critical\"\n\nclass Task(Base):\n __tablename__ = \"tasks\"\n id = Column(Integer, primary_key=True, index=True)\n title = Column(String(255), nullable=False, index=True)\n description = Column(Text, nullable=True)\n status = Column(Enum(TaskStatus), default=TaskStatus.pending, nullable=False)\n priority = Column(Enum(TaskPriority), default=TaskPriority.medium, nullable=False)\n created_at = Column(DateTime(timezone=True), server_default=func.now())\n updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())\n```\n\n**schemas.py:**\n```python\nfrom pydantic import BaseModel, Field, ConfigDict\nfrom datetime import datetime\nfrom typing import Optional, List\nfrom models import TaskStatus, TaskPriority\n\nclass TaskCreate(BaseModel):\n title: str = Field(..., min_length=1, max_length=255, description=\"Task title\")\n description: Optional[str] = Field(None, max_length=5000)\n status: TaskStatus = TaskStatus.pending\n priority: TaskPriority = TaskPriority.medium\n\nclass TaskUpdate(BaseModel):\n title: Optional[str] = Field(None, min_length=1, max_length=255)\n description: Optional[str] = Field(None, max_length=5000)\n status: Optional[TaskStatus] = None\n priority: Optional[TaskPriority] = None\n\nclass TaskResponse(BaseModel):\n model_config = ConfigDict(from_attributes=True)\n id: int\n title: str\n description: Optional[str]\n status: TaskStatus\n priority: TaskPriority\n created_at: datetime\n updated_at: datetime\n\nclass TaskListResponse(BaseModel):\n tasks: List[TaskResponse]\n total: int\n page: int\n page_size: int\n total_pages: int\n```\n\n**main.py:**\n```python\nfrom fastapi import FastAPI, Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\nfrom typing import Optional\nimport math\nfrom database import get_db, engine, Base\nfrom models import Task, TaskStatus, TaskPriority\nfrom schemas import TaskCreate, TaskUpdate, TaskResponse, TaskListResponse\n\nBase.metadata.create_all(bind=engine)\napp = FastAPI(title=\"Task Management API\", version=\"1.0.0\")\n\n@app.get(\"/tasks\", response_model=TaskListResponse)\ndef list_tasks(\n status: Optional[TaskStatus] = Query(None),\n priority: Optional[TaskPriority] = Query(None),\n search: Optional[str] = Query(None, min_length=1),\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n db: Session = Depends(get_db)\n):\n query = db.query(Task)\n if status:\n query = query.filter(Task.status == status)\n if priority:\n query = query.filter(Task.priority == priority)\n if search:\n query = query.filter(Task.title.ilike(f\"%{search}%\"))\n total = query.count()\n tasks = query.order_by(Task.created_at.desc()).offset((page - 1) * page_size).limit(page_size).all()\n return TaskListResponse(\n tasks=tasks, total=total, page=page,\n page_size=page_size, total_pages=math.ceil(total / page_size)\n )\n\n@app.get(\"/tasks/{task_id}\", response_model=TaskResponse)\ndef get_task(task_id: int, db: Session = Depends(get_db)):\n task = db.query(Task).filter(Task.id == task_id).first()\n if not task:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"Task not found\")\n return task\n\n@app.post(\"/tasks\", response_model=TaskResponse, status_code=status.HTTP_201_CREATED)\ndef create_task(task_data: TaskCreate, db: Session = Depends(get_db)):\n task = Task(**task_data.model_dump())\n db.add(task)\n db.commit()\n db.refresh(task)\n return task\n\n@app.put(\"/tasks/{task_id}\", response_model=TaskResponse)\ndef update_task(task_id: int, task_data: TaskUpdate, db: Session = Depends(get_db)):\n task = db.query(Task).filter(Task.id == task_id).first()\n if not task:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"Task not found\")\n update_data = task_data.model_dump(exclude_unset=True)\n for field, value in update_data.items():\n setattr(task, field, value)\n db.commit()\n db.refresh(task)\n return task\n\n@app.delete(\"/tasks/{task_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef delete_task(task_id: int, db: Session = Depends(get_db)):\n task = db.query(Task).filter(Task.id == task_id).first()\n if not task:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"Task not found\")\n db.delete(task)\n db.commit()\n```\n\nDeepSeek also included a database.py setup file and added a Dockerfile with docker-compose.yml for deployment, plus brief API documentation with example curl commands.
Claude Opus DeepSeek V4 — more scaffolding, needs polish
Claude Opus generated a tighter, 198-line solution in two files with inline comments explaining architectural decisions:\n\n**models.py:**\n```python\nfrom __future__ import annotations\nimport enum\nfrom datetime import datetime\nfrom sqlalchemy import Column, Integer, String, Text, DateTime, Enum, Index\nfrom sqlalchemy.sql import func\nfrom database import Base\n\n\nclass TaskStatus(str, enum.Enum):\n pending = \"pending\"\n in_progress = \"in_progress\"\n completed = \"completed\"\n archived = \"archived\"\n\n\nclass TaskPriority(int, enum.Enum):\n \"\"\"Integer-backed so we can ORDER BY priority natively.\"\"\"\n low = 0\n medium = 1\n high = 2\n critical = 3\n\n\nclass Task(Base):\n __tablename__ = \"tasks\"\n __table_args__ = (\n Index(\"ix_tasks_status_priority\", \"status\", \"priority\"),\n )\n\n id = Column(Integer, primary_key=True)\n title = Column(String(255), nullable=False)\n description = Column(Text, default=\"\")\n status = Column(Enum(TaskStatus), nullable=False, default=TaskStatus.pending)\n priority = Column(Enum(TaskPriority), nullable=False, default=TaskPriority.medium)\n created_at = Column(DateTime(timezone=True), server_default=func.now())\n updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())\n```\n\n**main.py:**\n```python\nfrom fastapi import FastAPI, Depends, HTTPException, Query, Path, status\nfrom pydantic import BaseModel, Field, ConfigDict\nfrom sqlalchemy.orm import Session\nfrom datetime import datetime\nfrom typing import Annotated\n\nfrom database import get_db, engine, Base\nfrom models import Task, TaskStatus, TaskPriority\n\nBase.metadata.create_all(bind=engine)\n\napp = FastAPI(title=\"Task Manager\", version=\"1.0.0\")\n\n# ── Schemas ──────────────────────────────────────────────\n\nclass TaskCreate(BaseModel):\n title: str = Field(..., min_length=1, max_length=255)\n description: str = Field(\"\", max_length=10_000)\n status: TaskStatus = TaskStatus.pending\n priority: TaskPriority = TaskPriority.medium\n\nclass TaskPatch(BaseModel):\n title: str | None = Field(None, min_length=1, max_length=255)\n description: str | None = Field(None, max_length=10_000)\n status: TaskStatus | None = None\n priority: TaskPriority | None = None\n\nclass TaskOut(BaseModel):\n model_config = ConfigDict(from_attributes=True)\n id: int\n title: str\n description: str\n status: TaskStatus\n priority: TaskPriority\n created_at: datetime\n updated_at: datetime\n\nclass Page(BaseModel):\n items: list[TaskOut]\n total: int\n page: int\n pages: int\n\n# ── Helpers ──────────────────────────────────────────────\n\nDB = Annotated[Session, Depends(get_db)]\n\ndef _get_or_404(db: Session, task_id: int) -> Task:\n if task := db.get(Task, task_id):\n return task\n raise HTTPException(status.HTTP_404_NOT_FOUND, \"Task not found\")\n\n# ── Routes ───────────────────────────────────────────────\n\n@app.get(\"/tasks\", response_model=Page)\ndef list_tasks(\n db: DB,\n status: TaskStatus | None = None,\n priority: TaskPriority | None = None,\n q: str | None = Query(None, min_length=1, description=\"Search title\"),\n page: int = Query(1, ge=1),\n size: int = Query(20, ge=1, le=100),\n):\n query = db.query(Task)\n if status:\n query = query.filter(Task.status == status)\n if priority:\n query = query.filter(Task.priority == priority)\n if q:\n query = query.filter(Task.title.ilike(f\"%{q}%\"))\n\n total = query.count()\n items = (\n query\n .order_by(Task.priority.desc(), Task.created_at.desc())\n .offset((page - 1) * size)\n .limit(size)\n .all()\n )\n return Page(items=items, total=total, page=page, pages=-(-total // size))\n\n@app.get(\"/tasks/{task_id}\", response_model=TaskOut)\ndef get_task(task_id: Annotated[int, Path(ge=1)], db: DB):\n return _get_or_404(db, task_id)\n\n@app.post(\"/tasks\", response_model=TaskOut, status_code=status.HTTP_201_CREATED)\ndef create_task(body: TaskCreate, db: DB):\n task = Task(**body.model_dump())\n db.add(task)\n db.commit()\n db.refresh(task)\n return task\n\n@app.put(\"/tasks/{task_id}\", response_model=TaskOut)\ndef update_task(task_id: int, body: TaskPatch, db: DB):\n task = _get_or_404(db, task_id)\n for k, v in body.model_dump(exclude_unset=True).items():\n setattr(task, k, v)\n db.commit()\n db.refresh(task)\n return task\n\n@app.delete(\"/tasks/{task_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef delete_task(task_id: int, db: DB):\n task = _get_or_404(db, task_id)\n db.delete(task)\n db.commit()\n```\n\nClaude's version used modern Python 3.12 syntax (PEP 604 unions, Annotated types), added a composite database index for common query patterns, used db.get() instead of filter/first for primary key lookups, made priority integer-backed for native sorting, and extracted a _get_or_404 helper to eliminate the repeated 404 pattern.
🔍 Analysis
Both models produced fully functional, production-usable FastAPI code — but the approaches reveal different philosophies.\n\nDeepSeek V4 went for completeness: more files, Docker setup, curl examples, and thorough documentation. The code itself is clean and conventional. It's the kind of output a junior-to-mid developer would be thrilled to receive — copy, paste, deploy. The extra deployment scaffolding (Dockerfile, docker-compose) is genuinely useful for getting started fast.\n\nClaude Opus went for craft: fewer lines, smarter patterns, and subtle production-readiness signals. The composite index on status+priority shows database awareness. Integer-backed priority enums enable native SQL sorting. `db.get()` for primary key lookups is both cleaner and more performant than `filter().first()`. The `Annotated[Session, Depends(get_db)]` type alias is idiomatic modern FastAPI. The `-(-total // size)` ceiling division trick avoids an import.\n\nThe real differentiator is in the details that matter at scale. Claude's composite index would prevent full table scans on the most common query pattern (filter by status, sort by priority). DeepSeek's version would need that index added later when performance issues surface. Claude's code is 31% shorter while being functionally equivalent — less code means fewer bugs and easier maintenance.\n\nDeepSeek wins on breadth (deployment files, documentation). Claude wins on depth (code quality, performance awareness, modern idioms). For a production codebase that needs to be maintained by a team, Claude's output requires less refactoring.

Run your own battle

Compare DeepSeek V4, Claude Opus and more AI models side-by-side with any prompt — free.

Try NailedIt.ai →