The world of software development is constantly evolving, and the recent advancements in artificial intelligence are undeniably changing the game. As a developer, I've been exploring how these powerful tools can enhance my workflow, particularly for building Laravel applications. It's a landscape that shifts rapidly, with new models and tools emerging constantly.
This article shares my journey navigating this landscape. I'll start by comparing some of the leading AI models I've evaluated for coding tasks. Then, I'll dive into Cursor, an AI-first code editor that has become central to my process. Most importantly, I'll detail the specific, structured workflow I've developed which combines manual scaffolding, meticulous AI planning using tools like Repomix and Google AI Studio, and agent-driven execution in Cursor. Finally, I'll share some crucial reflections on balancing AI assistance with developer ownership, the importance of review, and necessary considerations like security.
When it comes to AI coding assistants, things move fast. Right now, my personal go-to is Gemini 2.5 Pro. Why? For me, it currently feels like the most advanced model specifically for coding tasks. It's impressively fast, and the fact that it's often cheaper, or even free during its preview, is a major plus. I also find its approach valuable; it seems to 'think' through the problem before providing an answer, which often leads to better, more robust code compared to others.
So, how does it stack up against other strong contenders like Claude 3.7 Sonnet and GPT-4o?
Gemini 2.5 Pro is getting a lot of buzz, often called the "best AI for coding" right now. It performs very well on technical benchmarks and, in practical tests I've seen or run, it tackles complex coding challenges effectively. Think generating working code for things like flight simulators or tricky algorithms, often in one shot. Its massive 1 million token context window (with 2 million potentially available) is a game-changer, allowing it to understand huge codebases. While it's fantastic for complex system building and even fixing code generated by other models, it's not perfect. I've encountered occasional bugs, and some users note its general reasoning outside of pure coding isn't always top-tier, though its vision capabilities are strong. The free tier also comes with rate limits.
Claude 3.7 Sonnet is another powerhouse. Many developers rave about its ability to generate remarkably clean, almost bug-free code right out of the gate, often outshining GPT-4 in reliability. Its real strength, in my opinion, is its clarity – it explains code well and its 'Thinking Mode' (though often requiring a paid subscription) is fantastic for step-by-step debugging. However, its context window is smaller (200k tokens), which can be a limitation for very large projects, and it can be pricier than Gemini. While generally reliable, I've seen reports where it struggled with the most complex generation tasks that Gemini handled.
And what about GPT-4o? It brings strong multimodal capabilities to the table and is generally cheaper than Claude. Its large context window is also a benefit for understanding code context. However, based on recent comparisons and user feedback, it seems to be lagging behind both Gemini 2.5 Pro and Claude 3.7 Sonnet specifically for complex coding tasks. Many find it produces buggier code or struggles to follow intricate instructions compared to the other two.
In short: For my current needs, Gemini 2.5 Pro offers the best blend of cutting-edge coding performance, large context understanding, and cost-effectiveness. Claude 3.7 is an excellent, highly reliable alternative, especially if you value near bug-free code and clear debugging assistance. GPT-4o, while versatile, doesn't quite seem to match the specialized coding strengths of the other two right now. Naturally, this landscape is constantly evolving, so what's best today might change tomorrow!
While choosing the right base model is important, the development environment integrating it also plays a huge role. That led me to explore tools specifically designed for an AI-centric workflow, like Cursor.
Another interesting tool I've explored is Cursor, an AI-powered code editor built on top of VS Code. If you're already comfortable with VS Code, the transition feels pretty seamless because the layout and shortcuts are largely the same. What sets Cursor apart is how deeply AI is woven into the core experience, rather than feeling like an add-on extension.
Here are some of the features I've found most noteworthy:
AI completions (tab key): This is probably Cursor's standout feature for many. Its tab completion goes way beyond standard suggestions. It genuinely feels like it anticipates your next move, suggesting entire lines or blocks of code, finding bugs, and proposing fixes, all incredibly quickly. It's a subscriber feature, and while generally brilliant, sometimes the suggestions disappear fast or aren't quite right.
Chatting with your code: Cursor offers several ways to "talk" to your code using AI models. You can make quick inline edits (Cmd/Ctrl+K), have longer conversations in a sidebar (Cmd/Ctrl+L) for bigger refactors or generating new files, or use the 'Composer' for complex changes across multiple files. It's quite versatile for manipulating and generating code through natural language prompts.
Agent mode: This is where Cursor tries to handle tasks more autonomously, like figuring out which files to create or modify for a feature request (e.g., building a new page). It's powerful when it works, but requires very clear instructions, especially in large projects, to avoid unintended changes. It can even run terminal commands (with confirmation).
Guiding the AI with Project Rules: This is crucial for teamwork and maintaining standards. Instead of the older (and now deprecated) .cursorrules file, Cursor now uses Project Rules stored in a .cursor/rules directory within your project. These version-controlled files let you provide specific instructions to the AI. Think enforcing architectural patterns, coding conventions, or specifying tech stack details. It's essentially a way to encode your team's knowledge and preferences, ensuring the AI generates code that's consistent and high-quality. You can define rules that always apply, are triggered automatically based on file patterns, or are only used when the AI deems them relevant. There are also global User Rules you can set for personal preferences like response style.
Understanding your codebase: Cursor works hard to grasp the context of your project. It uses codebase indexing (which you can disable) and allows you to easily reference specific files, documentation, or even perform web searches using the '@' symbol in chat to give the AI better context.
When Cursor shines, it really shines, significantly speeding up development, particularly with its tab completion and refactoring tools. Using Project Rules helps maintain code quality and consistency, which is great for teams.
However, it's not without its quirks. The UI can feel a bit cluttered with all the AI elements. Like any AI tool, suggestions can sometimes be off-base or even counterproductive. Agent mode needs careful handling, and there's a learning curve to mastering all its features.
Compared to simpler extensions like GitHub Copilot, Cursor offers a much more integrated and feature-rich AI environment. It typically has a free tier with limits and a Pro plan (around $20/month) for more extensive use.
For me, Cursor represents a fascinating step towards AI-native development environments. While it requires some learning and adaptation, its potential for boosting productivity, especially with features like intelligent tab completion and configurable Project Rules, makes it a tool worth watching and experimenting with.
Seeing Cursor's potential, especially for complex tasks using its Agent mode, I realized I needed a structured way to harness its power effectively. And from the Cursor workshop from John Lindquist (https://egghead.io/workshop/cursor). This led me to develop a specific workflow, particularly tailored for my Laravel projects.
For tackling substantial features or tasks in my Laravel projects with Cursor, I've learned a structured approach works best. It generally follows these steps: Scaffold -> Plan -> Execute -> Monitor.
I start by creating the basic building blocks using the standard command-line tools. Relying on the AI to initialize things from scratch can sometimes lead to outdated templates or missed configurations. So, whether it's a new project (laravel new my-app
or composer create-project laravel/laravel my-app
) or adding new dependencies to an existing one, I lay the foundation manually first. This ensures I'm using the latest official structures and dependencies.
This is the most crucial step. Before asking Cursor's agent to perform complex tasks, I need to give it a detailed plan and all the necessary context.
Gather full context: I use a tool like FileForge (ffg -y --template plan
) or RepoMix (npx repomix@latest > context.txt
) to create a comprehensive snapshot of my project. This usually generates an XML-like file containing the directory structure and the content of relevant files (respecting .gitignore
). I make sure this includes key code files, configuration files (like .env.example
, config/*.php
), and importantly, any custom Cursor Project Rules I've defined in the .cursor/rules
directory. Providing everything upfront prevents the AI from making assumptions or generating incorrect code (like a wrongly formatted config entry). For huge projects, I use exclude/include flags in these tools to keep the context manageable.
Instruct a capable AI: I copy this entire context package and paste it into a powerful AI model that has web search capabilities (my preference is Gemini 2.5 Pro via AI Studio, but others could work). Along with the context, I provide clear instructions, typically including:
General workflow: How I want it to handle Git (e.g., feature branches, commit message format), testing preferences (e.g., "Generate Pest tests for new features"), and package manager usage (composer install
vs update
).
Laravel specifics: Explicit instructions like "Always use herd php artisan ...
for local commands," "Generate code following Laravel best practices," "Ensure new routes are added to routes/api.php
," or "Use dependency injection in controllers."
The task: A clear description of what needs to be done, asking for a step-by-step plan. For example: "Generate a detailed, step-by-step guide including code snippets and exact php artisan
commands to add a new feature that allows users to upload profile pictures. This should include creating the necessary migration, model updates, controller logic, API routes, validation rules, and basic Pest tests."
Once the external AI provides a detailed, step-by-step plan with specific code and commands based on my prompt, I copy that entire plan. I then start a new Cursor agent session (Cmd+N, then Cmd+I) and paste the plan into the agent input, making sure auto-run is enabled. Cursor then attempts to execute the plan step-by-step.
I watch Cursor execute the plan. It often works smoothly, but sometimes it needs guidance. If it stalls, makes a mistake (like using a wrong command or putting code in the wrong file), or gets stuck:
Interrupt: I use Cmd+Shift+Backspace.
Correct: I provide specific feedback. I might paste the error message and say, "You need to run herd php artisan migrate
first," or "That method should be in the UserProfileController
, not the UserController
," or "You forgot to import the Storage
facade." Simple prompts like "Fix, please" can sometimes work for minor errors, but targeted instructions are usually better.
Restart: If things go significantly off track, I might use Cursor's "Reject All" (though I often prefer resetting changes with Git) and restart the agent session with a slightly revised plan based on what went wrong.
This Scaffold -> Plan -> Execute -> Monitor process might seem elaborate, but I find it's the most reliable way to leverage Cursor's agent capabilities for complex development tasks in Laravel, minimizing errors and ensuring the AI follows project standards and my specific instructions.
A key part of making this workflow reliable, especially the 'Execute' step with the Cursor agent, involves guiding the AI effectively. This isn't just about the initial plan; it's also about setting persistent guidelines within the editor itself. This is where Cursor's Project Rules become essential.
Simply having the AI access your code isn't always enough. To get truly consistent and high-quality results, especially when working in a team or on complex projects, you need to guide the AI. This is where Cursor's Project Rules come in, which I find essential.
First off, it's important to use the newer Project Rules system, which stores rules as individual files in a .cursor/rules
directory within your project. The older, single .cursorrules
file in the root directory is still supported for now, but it's deprecated, and the new system offers more flexibility.
I recommend starting with a general rule that explains the overall architecture, key business logic, and core technologies of your project. This gives the AI foundational knowledge. Here’s an example of an architecture rule I use for one of my Laravel/React/Inertia projects:
---description:globs:alwaysApply: true---# Architecture Documentation: tts.test SaaS Platform ## 1. High-Level Overview ### System Purpose and Functionalities The system is a Software as a Service (SaaS) platform designed primarily for **Spanish Text-to-Speech (TTS) synthesis**. Based on the codebase structure (`Project`, `ProjectAudioGeneration` models, TTS service integration), its core functionalities include: - **User Authentication & Management:** User registration, login, password management, profile updates, and email verification.- **Team Management:** Users belong to teams, can own teams, and have a concept of a "current team" for context switching.- **Project Management:** Users can create projects within their current team to organize TTS tasks.- **Text-to-Speech (TTS) Generation:** Users can input text (via a rich text editor - Tiptap), select a voice, and initiate an asynchronous job to generate an audio file using an external TTS service (Google Cloud TTS).- **Audio Management:** Generated audio files are stored (using Spatie MediaLibrary) and associated with their respective generation tasks and projects. Users can preview/play generated audio within the application.- **Settings:** Users can manage profile details, passwords, and interface appearance (light/dark/system themes). ### Architectural Style The system follows a **monolithic, client-server architecture**. - The backend is a traditional **Laravel (PHP)** application adhering largely to the **Model-View-Controller (MVC)** pattern, extended with **Action classes** to encapsulate specific business logic.- The frontend is a **React (TypeScript) Single Page Application (SPA)**.- **Inertia.js** acts as the bridge between the Laravel backend and the React frontend, allowing the backend controllers to directly render React page components and pass data without requiring a separate REST/GraphQL API.- **Asynchronous processing** is used for time-consuming tasks like TTS generation via Laravel Queues and Jobs. ### Core Technologies, Languages, and Libraries - **Backend:** - Language: PHP 8.4+ - Framework: Laravel 12.x - ORM: Eloquent - Queue System: Laravel Queues (Configured for Database driver by default) - Database: PostgreSQL (Migrations set up, default connection often SQLite for local dev via `.env.example`) - Key Libraries: - `inertiajs/inertia-laravel`: Bridge to Inertia frontend. - `tightenco/ziggy`: Share Laravel routes with JavaScript. - `spatie/laravel-data`: Data Transfer Objects (DTOs). - `spatie/laravel-medialibrary`: File/Media management. - `google/cloud-text-to-speech`: Google Cloud TTS client library.- **Frontend:** - Language: TypeScript - Framework/Library: React 19.x - UI Components: shadcn/ui (built on Radix UI and Tailwind CSS) - State Management: Primarily via Inertia props; component-level state managed by React hooks. - Routing: Inertia.js (client-side), Laravel (server-side definition). - Build Tool: Vite - Styling: Tailwind CSS v4 - Rich Text Editor: Tiptap- **External Services:** - Google Cloud Text-to-Speech API ## 2. Component Interactions ### Major Components - **Frontend (React/Inertia - `resources/js`)**: - `pages/`: Contains React components representing individual application pages (e.g., `dashboard.tsx`, `Project.tsx`, `settings/profile.tsx`). Rendered by Inertia based on backend responses. - `layouts/`: Structural components defining page layouts (e.g., `AppSidebarLayout`, `AuthSimpleLayout`, `SettingsLayout`). - `components/`: Reusable UI elements (`ui/` - shadcn/ui primitives, `app-logo.tsx`, `TiptapEditor.tsx`, `AudioPlayer.tsx`, etc.). - `hooks/`: Custom React hooks (e.g., `useAppearance`, `useMobile`). - `lib/`: Utility functions (e.g., `utils.ts`).- **Backend (Laravel - `app/`)**: - `Http/Controllers/`: Handle incoming HTTP requests, interact with Actions/Models, and return Inertia responses (e.g., `DashboardController`, `ProjectController`, `Auth/RegisteredUserController`, `Settings/ProfileController`). - `Http/Requests/`: Define validation rules and authorization logic for specific requests (e.g., `StoreProjectRequest`, `StoreProjectAudioGenerationRequest`). Often create DTOs. - `Http/Middleware/`: Process requests before they reach controllers (e.g., `HandleInertiaRequests` shares data, `HandleAppearance` manages theme cookies). - `Http/Resources/`: Transform Eloquent models into structured data for the frontend (e.g., `ProjectResource`, `UserResource`, `ProjectAudioGenerationResource`). - `Actions/`: Encapsulate specific business logic operations (e.g., `CreateProjectAction`, `StartAudioGenerationAction`, `GetDashboardData`). Called by Controllers. - `Models/`: Eloquent models representing database tables and relationships (`User`, `Team`, `Project`, `Voice`, `ProjectAudioGeneration`). Interact with the database. - `Jobs/`: Define asynchronous tasks processed by the queue (`GenerateSpeechJob`). - `Services/`: Wrapper classes for external services (`TextToSpeechService`). - `Providers/`: Service providers for bootstrapping services (`TextToSpeechServiceProvider`). - `Policies/`: Define authorization rules for model actions (`ProjectPolicy`, `ProjectAudioGenerationPolicy`). - `Enums/`: Define enumerated types (`AudioGenerationStatus`). - `Exceptions/`: Custom exception classes (`TextToSpeechGenerationException`).- **Database (`database/`)**: Stores application state (users, teams, projects, voices, audio generations, media files, jobs, cache, sessions). Accessed via Eloquent Models. Migrations define the schema.- **Queue System (`config/queue.php`, `app/Jobs/`)**: Manages background tasks. `GenerateSpeechJob` is pushed onto the queue and processed by a worker.- **File Storage (`config/filesystems.php`, `storage/`)**: Stores generated audio files (via Spatie MediaLibrary, configured for local or cloud disks like `teams_cloud`).- **External TTS Service (Google Cloud)**: Integrated via `TextToSpeechService` for audio synthesis. ### Interaction Patterns - **Client-Server (Inertia)**: User interacts with the React frontend. Actions trigger Inertia visits/requests to the Laravel backend. Laravel Controllers handle these, often using Actions, Models, and Requests, then return an Inertia Response with a page component and data (transformed by Resources).- **Controller-Action**: Controllers delegate complex business logic to dedicated Action classes (e.g., `ProjectController` uses `CreateProjectAction`).- **Action-Model/DB**: Actions interact with Eloquent Models to read/write data from the database.- **Request-Validation**: Form Requests automatically validate incoming data before controller/action logic executes.- **Job Dispatch**: Controllers or Actions dispatch Jobs (e.g., `StartAudioGenerationAction` dispatches `GenerateSpeechJob`) to the queue for asynchronous processing.- **Job-Service**: Jobs utilize Services to interact with external APIs (e.g., `GenerateSpeechJob` uses `TextToSpeechService`).- **Service-External API**: Services make calls to external APIs (e.g., `TextToSpeechService` calls Google Cloud TTS).- **Model-Media Library**: Models implementing `HasMedia` (e.g., `ProjectAudioGeneration`) interact with Spatie MediaLibrary to manage associated files.- **Middleware Pipeline**: Requests pass through middleware for authentication, Inertia setup, etc. ### Mermaid Component Diagram ```mermaidgraph LR subgraph "User Browser" Frontend["React SPA (Inertia.js)"] end subgraph "Laravel Backend" Middleware["Middleware Pipeline"] Controllers["Controllers"] Actions["Action Classes"] FormRequests["Form Requests"] Models["Eloquent Models"] QueueSystem["Queue"] Jobs["Jobs (e.g., GenerateSpeechJob)"] Services["Services (e.g., TextToSpeechService)"] MediaLib["Media Library (Spatie)"] Policies["Policies"] Resources["API Resources"] Middleware -- Request --> Controllers Controllers -- Uses --> Actions Controllers -- Uses --> FormRequests Controllers -- Returns Inertia Response --> Frontend Actions -- Uses --> Models Actions -- Dispatches --> QueueSystem Jobs -- Processed by --> QueueWorker Jobs -- Uses --> Services Jobs -- Uses --> Models Models -- Interacts with --> Database[(Database)] Models -- Uses --> MediaLib MediaLib -- Writes/Reads --> FileStorage[(File Storage)] Services -- Calls --> ExternalTTS["External TTS API (Google)"] Policies -- Authorizes --> Controllers FormRequests -- Validates --> Controllers Resources -- Formats Data --> Controllers end subgraph "Infrastructure" QueueWorker["Queue Worker"] Database[(Database)] FileStorage[(File Storage)] end subgraph "External Services" ExternalTTS end Frontend -- HTTP Request --> Middleware``` ## 3. Data Flow Diagrams ### Audio Generation ```mermaidgraph LR subgraph "User Interaction - React Project Page" A[User Edits Text] --> B(TiptapEditor); C[User Selects Voice] --> D(Voice Select Dropdown); E[User Clicks Generate] -- Form Submit (Text JSON, Voice ID) --> F["POST /projects/ID/generations"]; end subgraph "Backend Processing" F -- Request --> G["StoreProjectAudioGenerationRequest"]; G -- Validates Text/Voice, Checks for In-Progress, Authorizes --> G; G -- Creates DTO --> H[StartAudioGenerationData DTO]; F -- Passes DTO --> I["ProjectAudioGenerationController store"]; I -- Calls Action --> J["StartAudioGenerationAction handle"]; J -- Creates Generation Record (Status: PENDING) --> K[(Database - project_audio_generations)]; J -- Dispatches Job --> L{Queue::dispatch}; I -- Redirects Back --> M["React Project Page (w/ Flash Message)"]; end subgraph "Async Job Processing" N[Queue Worker] -- Picks Up Job --> O["GenerateSpeechJob handle"]; O -- Finds Generation & Voice --> P[(Database - project_audio_generations, voices)]; O -- Checks Voice Active --> O; O -- Gets Plain Text --> O; O -- Calls Service --> Q["TextToSpeechService synthesizeAndSave"]; Q -- Calls External API --> R[Google Cloud TTS API]; R -- Returns Audio Content --> Q; Q -- Saves Audio to Disk --> S[(File Storage - teams_cloud/local)]; O -- Adds Media Record --> T[Spatie MediaLibrary]; T -- Creates Media Entry --> U[(Database - media)]; O -- Updates Generation Status (COMPLETED/FAILED) --> K; end style K fill:#f9f,stroke:#333,stroke-width:2px style P fill:#f9f,stroke:#333,stroke-width:2px style U fill:#f9f,stroke:#333,stroke-width:2px style S fill:#ccf,stroke:#333,stroke-width:2px``` ## 4. Design Decisions and Rationale - **Laravel Framework:** Chosen likely for its rapid development capabilities, extensive ecosystem (authentication, ORM, queues, testing), and strong community support. Its MVC structure provides a standard organization.- **Inertia.js with React:** Selected to build a modern SPA-like frontend without the complexity of managing a separate API backend. Allows Laravel controllers to directly serve React components, simplifying data flow.- **TypeScript:** Used in the frontend for enhanced type safety, improved developer experience, and better code maintainability compared to plain JavaScript, especially in larger applications.- **Shadcn/UI & Tailwind CSS:** Leveraged for the frontend UI. Provides unstyled, accessible component primitives built with Radix UI, styled using Tailwind CSS utility classes. This offers high customizability and consistency while speeding up UI development. Rationale: modern, utility-first CSS, component reuse.- **Action Classes (`app/Actions`)**: Used to separate business logic from controllers. This promotes cleaner controllers, improves code organization, and makes the core logic more testable and reusable. Rationale: Single Responsibility Principle, maintainability.- **Data Transfer Objects (DTOs - `spatie/laravel-data`)**: Used in `StartAudioGenerationData` and `ProjectData` (within `StoreProjectRequest`). Ensures structured, type-safe data transfer between layers (Request -> Action). Rationale: Improves code clarity and reduces errors from passing unstructured arrays.- **Eloquent ORM:** Laravel's default ORM is used for database interaction. Rationale: Convention, ease of use, integrates well with Laravel features.- **ULIDs for Primary Keys:** Modern choice over traditional auto-incrementing integers. ULIDs are sortable, unique across tables/systems, and less predictable. Rationale: Scalability, uniqueness.- **Queues & Jobs (`app/Jobs`)**: TTS generation is offloaded to a background job (`GenerateSpeechJob`). Rationale: Prevents blocking HTTP requests for long-running tasks, improves user experience, allows for retries and scaling of workers.- **Service Layer (`app/Services`)**: A dedicated `TextToSpeechService` encapsulates interaction with the Google Cloud TTS API. Rationale: Decouples external service interaction, makes it easier to test or swap implementations.- **Service Provider (`app/Providers`)**: `TextToSpeechServiceProvider` handles the instantiation and dependency injection of the `TextToSpeechClient`. Rationale: Centralized service configuration, follows Laravel's IoC principles.- **Spatie MediaLibrary (`spatie/laravel-medialibrary`)**: Used for handling the storage and association of generated audio files (`ProjectAudioGeneration` model). Rationale: Provides a robust and conventional way to manage file uploads and associations in Laravel.- **API Resources (`app/Http/Resources`)**: Used to transform Eloquent models into consistent JSON structures for the Inertia frontend. Rationale: Controls data exposure, ensures consistent data format, decouples frontend from raw model structure.- **Policies (`app/Policies`)**: Used for authorization logic (e.g., ensuring a user can only view/modify projects or cancel generations belonging to their team). Rationale: Centralizes authorization rules, keeps controllers cleaner.- **Pest Testing Framework:** Used for writing tests. Rationale: Modern PHP testing framework with a focus on readability and developer experience. ## 5. System Constraints and Limitations - **External Dependency (Google Cloud TTS):** - Requires valid Google Cloud credentials (`GOOGLE_APPLICATION_CREDENTIALS`) and a configured Project ID (`GOOGLE_CLOUD_PROJECT`). - Subject to Google Cloud pricing, usage limits, and potential API changes. - Network connectivity to Google Cloud APIs is essential for TTS functionality.- **Asynchronous Processing:** - Requires a running queue worker (`php artisan queue:work` or similar) to process `GenerateSpeechJob`. Without it, audio generation will remain pending. - Queue configuration (`config/queue.php`) determines reliability and throughput (e.g., database driver might not be ideal for high load vs. Redis/SQS).- **File Storage:** - Requires correctly configured filesystem disks (`config/filesystems.php`), especially the `teams_cloud` disk if using cloud storage (like S3 or DO Spaces). Credentials and bucket names must be set in `.env`. - Storage costs apply if using cloud storage. - Local storage (`public` disk linked via `storage:link`) might not be suitable for production scaling or multi-server deployments.- **Inertia SSR:** - The configuration (`config/inertia.php`) enables Server-Side Rendering. This requires Node.js on the server and a running SSR process (`php artisan inertia:start-ssr`) for optimal initial page loads. If SSR is not running or fails, Inertia falls back to client-side rendering.- **Environment Configuration:** The application relies heavily on environment variables defined in `.env` (based on `.env.example`) for database connections, API keys, URLs, etc. Incorrect configuration will lead to failures.- **Scalability:** - TTS generation can be a bottleneck. Scaling requires adding more queue workers. - Database performance under load depends on the chosen database and server resources. - Filesystem performance depends on the chosen disk driver and infrastructure.- **Error Handling:** While the `GenerateSpeechJob` includes basic failure handling (updating status to FAILED), more sophisticated error reporting or user notification mechanisms might be needed for production. The `TextToSpeechGenerationException` provides a specific exception type.- **Security:** Assumes standard Laravel security practices are followed. Sensitive credentials (like Google Cloud keys) must be securely managed and not committed to version control. Media URLs generated for cloud storage are temporary and time-limited.
While the recommendation is often to keep rules under 500 characters, I find that for complex architectural overviews like this, more detail is necessary. The key is providing the right context for the AI. Because this rule has alwaysApply: true
, its content is included in every interaction with the Cursor agent or Cmd-K AI for this project. Since these rules live in .cursor/rules
, you can commit them to your repository so the whole team benefits from the same guidance.
Beyond general architecture, I create rules for specific coding standards and preferences. For instance, here’s a rule file detailing some of my conventions for Laravel development:
---description: Laravel project rulesglobs: *.phpalwaysApply: false---# Laravel project rules- Every time you have to run an php artisan command, run it with "herd" at the start. Example: `herd php artisan migrate`. ## Models- Always create models using the artisan command.- All models use ULIDs instead of big integers for ids as primary key.- All models should be soft deleted. ## Controllers- Always create controllers using the artisan command.- Create controllers following laravel conventions, having index, create, store, show, edit, update, delete method. But only add the methods you need for the specific case.- When storing or updating, always use form requests for data validation.- Don't just put all the logic in the controller, use action classes to clearly separate logic.- Whenever you have to return data to inertiajs or as json, use api resources, and only share the data you need to share.- Always create feature tests for the controller, mocking the external services.- Test all the form request validation rules by testing the full request lifecycle, and asserting you got the correct error. ## Form Requests- Always create form requests using the artisan command. ## API Resources- Always create api resources using the artisan command.- When it makes sense, try to add a `dto` method that returns a data object from the request. ## Action classes- These are simple classes that do only one specific action.- Put all the action classes under app/Actions directory.- Use a public `handle` method to execute the action.- Pass all the necessary parameters to the `handle` method.- Instead of passing raw arrays use data objects to pass the necessary data to the action class. We use Spatie laravel data package.- Create feature test for all the action classes that you create. ## Testing- We use Pest for testing. So, create tests following Pest conventions.- Everytime you do changes to any php file, you have to run tests to verify everything works as expected.- Always run mutation tests to verify that the code is fully covered.- You can run tests with `herd php artisan test --filter=...`.- You can run mutation tests with the next command `herd coverage ./vendor/bin/pest --mutate`.- Never use `uses(RefreshDatabase::class)` because it's already added on [Pest.php](mdc:tests/Pest.php) file. ## Formatting- Make sure you run "./vendor/bin/pint" command, without herd prefix, after you do any change to php files.
Notice the globs: *.php
and alwaysApply: false
here. This means Cursor will automatically consider applying this rule whenever it's interacting with a PHP file, but it won't always include it. Also, as noted in the rule, some instructions might be very specific to my personal setup (like using herd). If collaborating, it might be better to put such personal preferences in a separate rule file that isn't committed to the shared repository, or perhaps use User Rules (defined in Cursor Settings) which apply globally just for you.
Finally, you'll likely find you need to add more nuanced rules as you work with the AI and see recurring mistakes or patterns you want to enforce. For example, I noticed the AI wasn't always using Laravel's preferred factory relationships when generating tests, so I added a specific testing rule:
---description: Use this rules whenever you are working on Laravel testsglobs:alwaysApply: false--- # General Guidelines- Always use factory relation helpers to create models with relations.**Example:** **DO:** `Team::factory()->for($user, 'owner')->create();` or `Project::factory()->for($team)->create();` **DON'T:** `Team::factory()->create(['owner_id' => $user->id]);
This rule uses alwaysApply: false
and relies on its description
. The AI will decide whether to include this rule's content based on whether the description seems relevant to the current task (e.g., if it's working on files in the tests/
directory).
Using these different types of rules allows you to progressively refine how Cursor's AI interacts with your specific project and coding style.
But effective rules rely on the AI having the right information before it even starts planning or executing. That brings us to the crucial step of gathering comprehensive project context.
So, how do I actually gather all that code context mentioned in the "Plan" step of my workflow? While there are a few tools for this, my personal preference is the Repomix CLI. I find it straightforward for managing different configurations depending on what I need the context for.
Essentially, Repomix bundles up your project's code (respecting things like .gitignore
) into a single file, and importantly, it lets you embed custom instructions alongside the code.
My setup involves creating a .repomix
directory at the root of my project. Inside this, I keep my different Repomix configuration files.
For example, when I'm planning a new feature, I use a configuration file I've named plan.config.json
(located at .repomix/plan.config.json
). This JSON file tells Repomix things like where to save the output (e.g., .repomix/output/plan.xml
), what format to use, which files to ignore, and crucially, points to a separate instruction template file (e.g., .repomix/instructions/plan.md
).
{ "output": { "filePath": ".repomix/output/plan.xml", "style": "xml", "parsableStyle": true, "compress": false, "headerText": "Custom header text", "instructionFilePath": ".repomix/instructions/plan.md", "fileSummary": true, "directoryStructure": true, "removeComments": false, "removeEmptyLines": false, "topFilesLength": 5, "showLineNumbers": false, "copyToClipboard": false, "includeEmptyDirectories": false, "git": { "sortByChanges": true, "sortByChangesMaxCommits": 100 } }, "include": ["**/*"], "ignore": { "useGitignore": true, "useDefaultPatterns": true, "customPatterns": ["./repomix/**", "tmp/", "*.log", "storage/**", ".github/**", "artisan", "bootstrap/cache/**"] }, "security": { "enableSecurityCheck": true } }
This plan.md
instruction file defines the detailed prompt template for the AI that will create the step-by-step plan. It outlines the role, context, and required output sections (Data Structures, Implementation Steps, Code Snippets, Unit Tests, Verification, Explanation).
# Feature Implementation Planning ## Role You are an AI assistant specializing in code analysis and software feature planning. ## Context You will be provided with two main inputs: 1. The **complete source code** of a software project.2. A **specific task** detailing a new feature to be added or an existing feature to be modified (provided below). ## Objective Your primary objective is to analyze the provided code and the specific task, and then generate a detailed, step-by-step plan to implement the required changes. You **must** use the provided code as the primary context for all suggestions. ## Output Requirements Your generated plan **must** include the following sections, clearly delineated: 1. **Data Structure Definition:** - Analyze if the task necessitates the creation of new data structures (e.g., classes, interfaces, structs, database table schemas, configuration objects) or modifications to existing ones. - If new structures or modifications are needed, clearly define them. Include field names, data types, relationships, and any relevant constraints or default values. Use appropriate code-like notation for clarity (e.g., class definition syntax for the project's language). If no changes are needed, state this explicitly. 2. **Implementation Steps:** - Provide a numbered list of specific, actionable steps required to implement the feature or modification. - **1. Prepare Git Branch:** - Instruct the user to check their current Git branch (e.g., using `git branch` or `git status`). - Instruct the user that if they are not already on a dedicated feature branch for this task (e.g., they are on `main`, `master`, or `develop`), they **must** create and check out a new feature branch before proceeding. Provide an example command with a placeholder name (e.g., `git checkout -b feature/your-feature-name`). - Emphasize that all subsequent code changes described in the plan should be committed to this new feature branch. - **2. [Original Step 1 - e.g., Modify File X]:** (Renumbered) Describe the next logical step... - **3. [Original Step 2 - e.g., Create File Y]:** (Renumbered) Describe the next logical step... - _(Continue renumbering and detailing all subsequent implementation steps)_ 3. **Code Snippets:** - For key steps involving code additions or significant modifications, provide relevant code snippets. - These snippets should illustrate **exactly** what needs to be added or changed. Use placeholders (e.g., `// ... existing code ...` or `/* TODO: Implement complex logic here */`) where appropriate, but provide the essential structure and key lines of code. - Ensure the snippets adhere to the coding style, conventions, and language/frameworks evident in the provided source code. 4. **Unit Tests:** - Provide specific unit tests required to verify the correctness of the implemented changes. - These tests should cover: - Happy paths (expected successful execution). - Edge cases. - Potential error conditions or invalid inputs. - Write the tests using the testing framework and conventions already established in the project's source code. Include necessary imports, setup, assertions, and teardown. 5. **Verification Instruction:** - Explicitly instruct the user to run the newly created unit tests _and_ the existing test suite (after committing changes to the feature branch) to ensure the changes work as expected and have not introduced regressions. Specify the command(s) if discernible from the project structure or common practices for the language/framework. 6. **Detailed Explanation:** - Provide a comprehensive explanation of the proposed changes. - Clarify **why** this approach was chosen (e.g., leveraging existing patterns, minimizing changes, performance considerations). - Explain how the new code integrates with the existing system architecture. - Mention any potential impacts on other parts of the application or any trade-offs made in the proposed solution. ## Important Considerations - Base **all** recommendations directly on the provided source code.- Maintain consistency with the project's existing architecture, patterns, naming conventions, and coding style.- Be precise and unambiguous in your instructions and explanations.- Ensure the final generated plan is formatted using Markdown. --- ## Here is the specific task you need to plan: **[PLACEHOLDER: Insert the specific feature request or modification task here.]** ---
So, when I run repomix -c .repomix/plan.config.json
, Repomix generates the plan.xml
output file containing the code context and the pre-defined instructions. I just replace the task placeholder, and it's ready for the planning AI.
I also have another configuration, say arch.config.json
, stored in .repomix/
, pointing to a different instruction file (.repomix/instructions/architecture-docs.md
). This one prompts the AI to generate architecture documentation based on the codebase.
arch.config.json
{ "output": { "filePath": ".repomix/output/architecture-docs.xml", "style": "xml", "parsableStyle": true, "compress": false, "headerText": "Custom header text", "instructionFilePath": ".repomix/instructions/architecture-docs.md", "fileSummary": true, "directoryStructure": true, "removeComments": false, "removeEmptyLines": false, "topFilesLength": 5, "showLineNumbers": false, "copyToClipboard": false, "includeEmptyDirectories": false, "git": { "sortByChanges": true, "sortByChangesMaxCommits": 100 } }, "include": ["**/*"], "ignore": { "useGitignore": true, "useDefaultPatterns": true, "customPatterns": ["./repomix/**", "tmp/", "*.log", "storage/**", ".github/**", "artisan", "bootstrap/cache/**"] }, "security": { "enableSecurityCheck": true } }
architecture-docs.md
## AI Agent Instructions: Architecture Documentation Generation ### Role You are an AI assistant specializing in code analysis and the generation of software architecture documentation. ### Context You will be provided with the **complete source code** of a software project. ### Objective Your primary objective is to analyze the provided codebase and generate comprehensive architecture documentation covering the key aspects listed below. Your analysis and documentation **must** be derived directly from the provided source code. ### Required Documentation Sections Your generated documentation **must** include the following sections, clearly delineated: 1. **High-Level Overview:** - Provide a concise summary of the system's primary purpose and its main functionalities as inferred from the code. - Describe the overall architectural style identified (e.g., monolithic, microservices, client-server, event-driven, layered). - List the core technologies, programming languages, frameworks, and significant libraries used in the project. 2. **Component Interactions:** - Identify the major logical or physical components, modules, services, or layers within the system based on the code structure (e.g., directories, namespaces, classes). - Describe how these components interact with each other (e.g., function calls, API requests, events, shared data stores). - Where feasible, generate diagrams using Mermaid syntax (e.g., component diagrams, sequence diagrams) to visually represent these interactions. If diagrams are not feasible, provide clear textual descriptions of the communication patterns. 3. **Data Flow Diagrams:** - Describe the flow of data through the system for key operations or use cases identifiable from the code (e.g., user registration, order processing, data reporting). - Explain where data originates (e.g., user input, external systems), how it is processed or transformed by different components, where it is stored (e.g., databases, caches, files), and its eventual destination or output. - Generate diagrams using Mermaid syntax (e.g., flowcharts) to illustrate these data flows, or provide detailed textual descriptions if diagrams are not practical. 4. **Design Decisions and Rationale:** - Infer significant design decisions based on the code structure, chosen libraries/frameworks, observed design patterns (e.g., MVC, Repository, Singleton), and any explicit comments. - For each identified decision, attempt to explain the likely rationale behind it (e.g., "The use of framework X suggests a focus on rapid development," "The Repository pattern likely aims to decouple data access logic"). - Clearly state when a rationale is an inference based on common practices versus explicitly mentioned in comments. 5. **System Constraints and Limitations:** - Identify potential system constraints, external dependencies, or limitations evident from the code. - Examples include: reliance on specific external APIs or services, assumptions about the deployment environment (e.g., OS-specific calls), potential scalability bottlenecks suggested by algorithms or data handling, areas where the code structure might limit future flexibility. ### Important Considerations - Base **all** documentation strictly on the provided source code and its structure.- Use clear, concise, and objective language.- Structure the output logically with clear headings for each required section.- **Ensure the final generated documentation is formatted using Markdown.**- When generating diagrams (Mermaid), ensure they accurately reflect the interactions or flows derived from the code.- If information for a section cannot be reasonably inferred from the code, state that explicitly.- Make sure you are using the correct Mermaid syntax for the diagrams. Don't use weird characters like `>` or `<` in the diagrams.
Running Repomix with this config (repomix -c .repomix/architecture-docs.config.json
) produces output perfect for creating that general Cursor Project Rule or for general developer documentation.
It's a good idea to commit the .repomix
directory containing your configuration (.json
) and instruction (.md
) files but add the output directory (e.g., .repomix/output/
) to your .gitignore
. And don't hesitate to use AI to help refine these instruction templates!
With a complete context package generated by Repomix, the next step is to feed this into a powerful AI model capable of detailed planning. This is where the large context window models truly shine.
So, what do I do with that output file from Repomix, the one containing both the code context and my instructions template? This is where Google AI Studio comes into play in my workflow.
The main reason I add this step is to leverage the power of a model like Gemini 2.5 Pro. As confirmed by recent information, these models boast a massive context window; often up to 1 million tokens! This is huge, allowing the AI to process nearly the entire context I generated with Repomix in one go.
And the best part? Using the Google AI Studio web interface itself is generally free for experimentation and development purposes, with generous usage limits.
So, my process is simple:
Go to Google AI Studio (you can find it at https://aistudio.google.com/ ).
Start a new chat prompt.
Paste the entire content from the Repomix output file (the code context + the instructions template, with my specific task filled in) into the prompt.
Run it.
Because Gemini 2.5 Pro gets to see the extensive code context provided by Repomix, I find the resulting plan is significantly better (more accurate, more context-aware, and generally a more viable implementation strategy) than if I were to just give instructions directly within Cursor without this deep preparation. It really highlights how crucial providing the right, comprehensive context is for getting good results from these powerful models. Most of the time, the plans it generates are excellent starting points.
Once AI Studio generates the detailed, step-by-step plan (including code snippets, commands, etc., as defined in my Repomix instruction template), I simply copy that entire plan.
The next step? Taking that plan over to Cursor and letting its agent do the work.
This structured process significantly boosts productivity, but using AI so heavily also brings important considerations and led me to refine my approach further, adding crucial steps after the AI has done its part.
There's no doubt this AI-assisted workflow significantly speeds up feature development. However, after using it for a while, I came to an interesting realization. At one point, I felt a bit detached from the codebase because, well, I hadn't written large parts of it myself. Initially, I even got frustrated with things like the AI agent adding lots of comments. My first instinct was to try and make it stop.
But then I had a change of heart. I realized those comments, and the act of reviewing the AI's work, were actually crucial. Reading through the code, understanding the why behind it, and even cleaning up comments myself forces me to engage with it deeply. It's how I truly learn and internalize the code being added to my project.
So, I've refined my workflow slightly, adding a dedicated review phase after the AI agent finishes its work:
Understand the AI's 'thinking': I go back through the Cursor chat history and carefully examine the changes the AI made. This helps me grasp the solution path it took and any hurdles it encountered.
Git is king: I generally accept all the changes Cursor proposes initially. I don't get bogged down in Cursor's diff interface; I rely on Git as the ultimate source of truth for reviewing changes.
Review & refine: As I review the changes (often using Git tools), I stage the files that look correct. During this process, I'll make small necessary tweaks and remove any comments that aren't adding value.
Take notes: If I spot larger issues or things I want to refactor differently, I jot them down.
Test thoroughly: Once I've staged the main changes, I run all the tests (unit, integration, etc.) multiple times and perform manual checks to ensure everything works and looks right.
Commit & iterate: I commit the reviewed and validated changes. Then, I address the notes I took earlier. Sometimes I make these follow-up changes myself, other times I might prompt Cursor again for specific fixes.
Improve the rules: If I notice the AI consistently making the same kind of mistake, I update my Cursor Project Rules or Repomix instruction templates to prevent it in the future.
Now, I know what some might think: "If you're reviewing everything manually, what's the point of using AI?" It's a fair question. But for me, it's about balance. It's vital that we, as developers, truly know and own the code in our projects. This process helps me do that and keeps my skills sharp. And honestly, even with this review step, the overall process is still significantly faster than writing everything from scratch.
A note for junior developers: If you're earlier in your career, I highly recommend paying close attention to the plan generated by the AI (in the Google AI Studio step). Read through it carefully. You can learn a tremendous amount about different approaches, potential pitfalls, and best practices just by studying the AI's proposed solution. Don't hesitate to ask the AI questions if you don't understand parts of its plan.
A major consideration, especially when working with client projects, is the security and privacy implications of sending code to third-party AI services. Most of us have confidentiality agreements or concerns about proprietary code. It's essential to have an open conversation with your team and clients about what level of code sharing is acceptable. We're still in a bit of a grey area with these technologies, and clear guidelines and decisions are needed on a case-by-case basis.
Navigating the rapidly evolving world of AI coding assistants can feel overwhelming, but the potential benefits for productivity are undeniable. My journey has led me to favor powerful models like Gemini 2.5 Pro for their planning capabilities and integrated environments like Cursor for their AI-first features.
The structured Scaffold -> Plan -> Execute -> Monitor workflow, enhanced by meticulous context gathering with tools like Repomix and detailed planning via Google AI Studio, has proven incredibly effective for tackling complex Laravel tasks. Guiding the AI with well-defined Project Rules in Cursor further ensures consistency and adherence to standards.
However, speed isn't everything. As I've learned, integrating AI effectively requires a mindful approach. The crucial review and refinement step after the AI completes its tasks is non-negotiable for maintaining code ownership, understanding the changes, and ensuring quality. It's about leveraging AI as a powerful assistant, not a replacement for developer skill and judgment.
While challenges remain, particularly around security and the ongoing need to adapt as technology progresses, I firmly believe that embracing these tools thoughtfully allows us to build better software, faster. The key is finding the right balance between automation and active developer involvement, ensuring we remain firmly in control of the code we create.