All skills
asyrafhussin avatar

/technical-debt

@4df703d

Technical debt inventory, prioritization, and audit for PHP/Laravel (MySQL) and Node/TypeScript/React projects. Use when assessing code health, identifying refactoring candidates, planning debt paydown, or auditing a codebase for accumulated debt. Triggers on "audit technical debt", "find tech debt", "debt inventory", "what should we refactor first", or tasks involving code health, security debt, performance debt, data debt, observability debt, debt prioritization, or remediation planning.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/technical-debt

This session only. Nothing lands on disk.

rulesperf-missing-pagination.md

≈996 tokens on demand. Your agent reads this file only when SKILL.md points to it.

Unbounded Result Sets

Impact: HIGH (One large customer kills latency for everyone)

An endpoint that returns "all" records works fine until one customer has 100,000 of them. At that point, the request times out, exhausts memory, and (in shared-tenancy systems) takes down the database for everyone else. Unbounded result sets are time bombs proportional to your most successful customer.

How to Detect

# Find list endpoints / repository methods without LIMIT or pagination
grep -rEn '\\->all\\(|\\->get\\(\\)|findAll|fetchAll' --include='*.php' --include='*.ts'

# Frontend: full-result-set components without "load more" / virtualization
grep -rEn 'map\\(|forEach\\(' --include='*.tsx' src/ | head -50  # review for unbounded lists

# Check actual prod metrics: max rows returned by each endpoint over the last 30 days
# (most APMs / DB monitors expose this)

Heuristic: any list endpoint without an explicit limit is debt. Any endpoint that returns objects with nested collections (orders → items, users → posts) is doubly so.

Incorrect

// ❌ Returns every order ever placed by the customer
public function index(Customer $customer) {
    return OrderResource::collection($customer->orders);   // 50,000 rows → 8MB response
}

// ❌ "Export all users" endpoint loads entire table into memory
public function export() {
    $users = User::all();                                  // OOM at scale
    return Excel::download(new UsersExport($users), 'users.xlsx');
}
// ❌ Frontend asks for everything and filters client-side
const allTransactions = await fetch('/api/transactions').then(r => r.json());
const recent = allTransactions.filter(t => isThisMonth(t.date));   // 100,000 rows → 200,000 ms parse

Correct

// ✅ Cursor-based pagination (preferred for "infinite scroll" / large datasets)
public function index(Customer $customer, Request $request) {
    return OrderResource::collection(
        $customer->orders()->latest()->cursorPaginate(50)
    );
}

// ✅ Streaming export — never loads the full set into memory
public function export() {
    return response()->streamDownload(function () {
        User::query()->orderBy('id')->lazy()->each(function ($user) {
            // write one row at a time
        });
    }, 'users.csv');
}
// ✅ Server filters; client requests only what it needs
const { data, nextCursor } = await fetch('/api/transactions?since=2026-05-01&limit=50')
  .then(r => r.json());

Benefits:

  • Memory and latency stay bounded regardless of customer size
  • Database returns fewer rows over the wire
  • Frontend can render results progressively

Remediation Strategy

  • Effort: S–M per endpoint (cursor pagination is more invasive than offset; both are mechanical)
  • When to pay down:
    • NOW: any endpoint where the result count is user-controlled and unbounded
    • NOW: any export endpoint loading the entire result set into memory
    • Then: add a max-result-count assertion in CI for list endpoints
  • Pagination choice:
    • Cursor — best for "next page" UX, large datasets, real-time-ish data (no skip cost)
    • Offset — easier to implement, OK for small/medium datasets; expensive at high page numbers
    • Keyset — similar to cursor but using a real column (id, created_at)

Tip: when retrofitting pagination on a public API, support both old (full response) and new (paginated) shapes during a deprecation window, then remove the unbounded form.

Reference: Laravel Pagination · Slack — Cursor Pagination · Use the Index, Luke — Paging Through Results

Source: SKILL.md on GitHub

No alerts16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides a comprehensive and safe framework for identifying, prioritizing, and auditing technical debt in PHP/Laravel and Node/TypeScript/React projects. No security risks or malicious behaviors were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

Signed by skilld at 4df703d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 5 months ago
metadata
{
  "author": "agent-skills",
  "version": "1.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/technical-debt