AI-Assisted Software Engineering
Prompt intentionally, plan deliberately, and build with precision using modern AI tools, MCP connectors, subagents, and project-scoped skills.
Prompting Intentionally
A prompt is a specification. The quality of the output is bounded by the quality of the input. Every strong prompt answers four questions.
What exists?
Tell the model what you are working with β tech stack, relevant files, constraints already in place, what you have already tried.
What must not change?
Boundaries the solution must respect β existing API contracts, performance limits, conventions, files it must not touch.
What do you need?
The concrete deliverable, stated precisely. "Add a payment webhook endpoint that validates the payload and upserts a payment record."
How do you want it?
Output shape β a single file, a diff, a step-by-step plan, a short answer, or a code block with explanation inline.
Weak prompt// No context, no constraint, vague goal "Add authentication to my app." // Result: boilerplate that may not fit your // stack, overwrite existing code, or add // libraries you did not want.
Strong prompt// Context + Constraint + Goal + Format "This is a Laravel 11 API. Auth uses Sanctum and is already wired. Do not touch AuthController. Add a POST /webhooks/payment endpoint that: validates JSON body (event_id, payment_id, event, amount, currency), upserts a Payment model, and returns {status:'received'}. Show only the controller method and route."
Planning Before You Build
AI writes code faster than you can think through architecture. Slow down at design time β that is where AI is most powerful and where skipping it costs the most.
Cursor, Claude Code & Codex
Three tools, three interaction models. They complement each other β knowing when to reach for each one multiplies your output.
- Inline completions with full codebase context
- Composer β multi-file edits in one prompt
- Agent mode β runs terminal commands autonomously
- Best for: file-level edits, refactors, quick lookups
- Strength: zero context-switching from editor
- Reads, writes, and searches your entire repo
- Plans multi-step tasks before executing them
- Spawns subagents for complex workloads
- Supports project-scoped skills (slash commands)
- Best for: architecture changes, feature builds, long tasks
- Strength: deep reasoning + full repo awareness
- Runs in an isolated cloud sandbox
- Handles long async tasks without blocking your machine
- Spins up a full dev environment per task
- Best for: parallelizing independent workstreams
- Strength: run N tasks simultaneously, review PRs later
Rule: Use Cursor for speed inside a file. Use Claude Code when you need the agent to reason across the whole repo. Use Codex to parallelize tasks that would block your own flow.
MCP β Model Context Protocol
MCP is an open standard that lets AI tools talk to external systems in a structured, composable way. Instead of copy-pasting context into prompts, the model pulls exactly what it needs.
Practical example: With a GitHub MCP server, Claude Code can read open issues, look at PR diffs, and leave comments β without you copying and pasting any of it into the prompt. The model fetches context on demand.
How Subagents Work
A subagent is an AI agent spawned by a parent agent to handle a focused sub-task. The parent orchestrates; subagents execute. This enables parallelism, context isolation, and specialization.
Why subagents?
- Context isolation: each agent gets only the context it needs β parent stays clean
- Parallelism: independent sub-tasks run concurrently
- Specialization: agents can be given different tools or instructions
In Claude Code
- Invoked via the
Agenttool with asubagent_type - Types include:
general-purpose,Explore,Plan - Can run in background β fire and forget, get notified on completion
- Worktree isolation: changes committed in a branch, not your working tree
Project-Scoped Skills
Skills are reusable prompt templates committed to the repo. Call them as slash commands β /playwright, /laravel-feature, /vue-component β and the model follows your team's conventions automatically.
Consistency
Every developer gets the same scaffolding. No drift between how one engineer structures a controller and another.
Speed
One command replaces a multi-paragraph prompt. The context, conventions, and output format are pre-loaded.
Living Documentation
The skill file is the canonical description of how your team builds a feature. It lives in version control alongside the code.
Anatomy of a skill file
.claude/commands/your-skill.md--- description: Short description shown in the /command list allowed-tools: Read, Write, Bash, Glob, Grep # tools the agent may use --- # Context What the agent needs to know about this project before starting. Stack, conventions, file structure, patterns to follow. # Instructions Step-by-step directions. Be as specific as your conventions require. Reference actual paths, class names, and patterns from this repo. # Task $ARGUMENTS # replaced at runtime with what you type after the command
Where they live: Commit skills to .claude/commands/ at the repo root. They are available to every developer who clones the project and visible as /command-name in Claude Code.
Skill: Playwright E2E Tests
Invoke /playwright <describe the flow>. The agent reads existing test patterns, creates a Page Object Model, and writes a fully structured E2E test.
--- description: Generate a Playwright E2E test with Page Object Model for a given user flow allowed-tools: Read, Write, Glob, Bash --- # Context This project uses Playwright with TypeScript. Tests live in `tests/e2e/`. Page Object Models live in `tests/e2e/pages/`. Use `data-testid` attributes as selectors β never CSS classes or XPath. Follow Arrange-Act-Assert. One behaviour per test() block. # Instructions 1. Read two or three existing tests in `tests/e2e/` to understand the project's style. 2. Identify whether a POM for the target page already exists. If not, create one. - POM class name: `{PageName}Page` extending `BasePage` - Expose locators as readonly properties, actions as methods 3. Write the test file using test.describe + test() blocks. 4. Use await expect(page).toHaveURL() and role-based locators where possible. 5. Run `npx playwright test --headed {file}` and fix any failures before finishing. # Task Write a Playwright E2E test for the following flow: $ARGUMENTS
Skill: Laravel Feature
Invoke /laravel-feature <describe the feature>. The agent scaffolds the full vertical slice β migration, model, request, service, controller, route, and feature test β all following your team's conventions.
--- description: Scaffold a complete Laravel feature: migration, model, request, service, controller, route, and test allowed-tools: Read, Write, Bash, Glob, Grep --- # Context Laravel 11, PHP 8.3. Architecture: thin controllers β Service classes β Eloquent models. - Controllers: `app/Http/Controllers/` β no business logic, only HTTP concerns - Form Requests: `app/Http/Requests/` β all validation lives here - Services: `app/Services/` β all business logic, injected via constructor - Models: typed Eloquent properties, $fillable defined, no logic beyond scopes - Routes: registered in `routes/api.php` or `routes/web.php` using Route::apiResource - Tests: `tests/Feature/`, use RefreshDatabase, test happy path + validation errors # Instructions 1. Read `app/Services/` and one existing controller to confirm current patterns. 2. Create the migration. Run php artisan migrate to verify it is valid. 3. Generate the Model with fillable, casts, and any obvious relationships. 4. Create the Form Request with validation rules. Reject clearly invalid input. 5. Create the Service class injected into the controller via constructor. 6. Create the Controller with index / store / show / update / destroy as needed. 7. Register the route. Run php artisan route:list to confirm. 8. Write a Feature test covering: successful creation, validation failure, and not-found. 9. Run php artisan test --filter {FeatureName}. Fix any failures. # Task $ARGUMENTS
Skill: Vue Component
Invoke /vue-component <describe the component>. The agent produces a typed <script setup> component and extracts reusable logic into a composable when the state warrants it.
--- description: Generate a Vue 3 component (script setup + TypeScript) and composable if logic is reusable allowed-tools: Read, Write, Glob --- # Context Vue 3 with <script setup> and TypeScript. Components in `src/components/` (PascalCase). Composables in `src/composables/use{Name}.ts`. Pinia for global state. - Props: defineProps<{ prop: Type }>() β always typed, never PropType - Emits: defineEmits<{ eventName: [payload: Type] }>() - Expose nothing by default; use defineExpose only when the parent truly needs it - Styles: <style scoped> with BEM-like class names - No inline styles; no style bindings unless driven by a prop value (e.g. width) # Instructions 1. Read 1β2 existing components in `src/components/` to match the project's conventions. 2. Decide whether state and async logic should live in the component or a composable. - Extract to composable if: logic is used in >1 place OR the component exceeds ~80 lines. 3. Write the `{Name}.vue` file with props, emits, slots, and template. 4. If a composable is warranted, write `use{Name}.ts` and import it in the component. 5. Add a brief // Usage comment at the top of the component showing a minimal example. # Task $ARGUMENTS