Archives

MY APPROACH

The right solution, not the biggest solution.

You don't need to know whether your problem needs an API, an automation workflow, a dashboard or a custom application.

Tell me the problem. I'll help figure out a practical way to solve it.

How to Use AI to Understand a Legacy Laravel Codebase Before You Change It

A legacy Laravel application rarely announces its architecture.

There may be a Services directory, but half of the business logic may still live inside controllers. There may be repositories, but some models may query the database directly. A queue job might update a record, trigger an event, send an email, and quietly call an external API.

And then someone asks you to make a "small change."

This is where AI can become extremely useful.

Not because AI can magically understand the application for you, but because it can help you explore a large codebase much faster than you could manually.

The important distinction is this:

Don't ask AI to refactor an unfamiliar Laravel application first. Ask it to help you understand the application first.

That change in approach can make a significant difference when working with inherited applications, old Laravel projects, undocumented business systems, or applications that have evolved for years without a consistent architectural direction.

In this article, we'll build a practical workflow for using an AI coding assistant to investigate an existing Laravel 13 application before making significant changes.

We'll look at how to:

  • build a map of the Laravel application

  • identify the real entry points

  • trace a feature through controllers, services, models and jobs

  • discover hidden business rules

  • understand Eloquent relationships

  • identify database side effects

  • investigate events and listeners

  • trace queued jobs

  • find external API integrations

  • understand authentication and authorization

  • use tests as documentation

  • distinguish facts from AI assumptions

  • create an architecture map

  • prepare a safe refactoring plan

  • and use AI without allowing it to rewrite the application prematurely

The examples use Laravel 13 and PHP.


Before You Ask AI to Touch the Code

Imagine that you inherit an application for managing customer orders.

The code works.

The business depends on it.

Nobody has comprehensive documentation.

The original developer is no longer available.

You have been asked:

"Add automatic cancellation for unpaid orders after 30 minutes."

This sounds straightforward.

You might immediately create something like:

Order::where('status', 'pending')
    ->where('created_at', '<', now()->subMinutes(30))
    ->update([
        'status' => 'cancelled',
    ]);

Technically, that may work.

But before writing it, you need to answer some questions.

What exactly makes an order unpaid?

Does pending always mean unpaid?

Can an order be pending because payment is being verified?

What happens to reserved inventory?

Should the customer receive an email?

Does cancellation trigger a refund?

Does another system receive a webhook?

Can an order be cancelled if it contains a manually approved item?

Is there already a scheduled command doing something similar?

Could an existing queue job update the order at the same time?

The database query is easy.

Understanding the application is the difficult part.

That's where AI can help.


The Right Mental Model: AI as a Codebase Investigator

When developers first use AI against an existing project, they often ask questions such as:

Refactor this controller.

Make this service cleaner.

Convert this to repository pattern.

Apply SOLID principles.

Improve the architecture.

Those prompts immediately put the AI into implementation mode.

That is often the wrong starting point.

Instead, treat the AI like a junior engineer who can inspect thousands of files extremely quickly.

You don't ask that engineer to rewrite the billing system on their first day.

You ask them to investigate it.

A better first instruction is:

You are investigating an existing Laravel 13 application.

Do not modify any files.

Your job is to understand the existing system.

Start by identifying:

1. Application entry points
2. HTTP routes
3. Controllers
4. Models and relationships
5. Services
6. Repositories
7. Jobs and queues
8. Events and listeners
9. Console commands
10. Scheduled tasks
11. Notifications
12. External integrations
13. Authentication and authorization
14. Database structure
15. Tests

For every conclusion, provide the file path
that supports it.

If something cannot be established from the
available code, mark it as UNKNOWN.

Do not guess.
Do not propose refactoring yet.

That final instruction is important.

Do not propose refactoring yet.

You are still collecting evidence.


Step 1: Build a Map of the Laravel Application

Before examining individual classes, understand the shape of the project.

A typical Laravel application might contain:

app/
├── Console/
├── Events/
├── Exceptions/
├── Http/
│   ├── Controllers/
│   ├── Middleware/
│   └── Requests/
├── Jobs/
├── Listeners/
├── Mail/
├── Models/
├── Notifications/
├── Policies/
├── Providers/
└── Services/

bootstrap/
config/
database/
├── factories/
├── migrations/
└── seeders/

resources/
routes/
storage/
tests/

But don't assume the directory structure tells the whole story.

An older application might contain:

app/
├── Helpers/
├── Libraries/
├── Managers/
├── Repositories/
├── Services/
├── Traits/
└── Utils/

That doesn't automatically mean the application follows a clean architecture.

The names are clues.

They are not proof.

Ask AI for evidence

Use a prompt like:

Inspect the Laravel project structure.

Create an application map.

For each major component identify:

- directory
- responsibility
- important classes
- dependencies
- entry points
- external systems
- uncertainty

Do not make assumptions based only on directory names.

Support every architectural conclusion with
specific file paths.

A useful response might look like:

HTTP entry points

routes/web.php
    ↓
OrderController
    ↓
OrderService
    ↓
Order model

Evidence:
- routes/web.php
- app/Http/Controllers/OrderController.php
- app/Services/OrderService.php
- app/Models/Order.php

That is much more useful than:

This application uses MVC architecture.

The second statement might technically be true, but it doesn't tell you how the application actually behaves.


Step 2: Find Every Way a Feature Can Enter the System

One of the easiest mistakes when analyzing a Laravel application is assuming that everything starts with an HTTP request.

It doesn't.

A business operation can begin from:

  • a web route

  • an API route

  • a webhook

  • a scheduled command

  • a queued job

  • an Artisan command

  • an import

  • an event listener

  • a notification callback

  • an external integration

For example, suppose you are investigating orders.

You might find:

Route::post('/orders', [OrderController::class, 'store']);

You could conclude that orders are created there.

But later you discover:

app/Jobs/ImportOrders.php
app/Console/Commands/SyncOrders.php
app/Http/Controllers/WebhookController.php
app/Services/MarketplaceOrderService.php

Suddenly there are four different paths that can create an order.

That changes the entire refactoring strategy.

Ask AI to search by business object

Find every location in this Laravel application
that creates, modifies, cancels, approves, deletes,
or restores an Order.

Search beyond controllers.

Include:

- routes
- controllers
- services
- repositories
- models
- jobs
- listeners
- console commands
- scheduled tasks
- event handlers
- webhooks
- imports
- tests

Return:

File
Class
Method
Operation
Reason

You are trying to answer:

Where can this business object change?

That is a much more valuable question than:

Where is the OrderController?


Step 3: Trace One Real Business Workflow

Don't try to understand the entire application at once.

Choose one important business operation.

For example:

Customer places an order.

Now trace it from beginning to end.

You might discover a flow such as:

POST /orders
      ↓
OrderController@store
      ↓
StoreOrderRequest
      ↓
OrderService::create()
      ↓
PricingService
      ↓
InventoryService
      ↓
OrderRepository
      ↓
OrderCreated event
      ↓
SendOrderConfirmation
      ↓
PaymentJob

This gives you something much more useful than a collection of class descriptions.

You have a behavioral map.

Ask AI to produce one

Trace the "customer creates an order" workflow.

Start at the HTTP entry point.

Follow the execution until all observable
side effects have completed.

For every step provide:

1. File
2. Class
3. Method
4. Input
5. Output
6. Database changes
7. Events dispatched
8. Jobs dispatched
9. External API calls
10. Exceptions
11. Transactions
12. Queue boundaries

Do not skip intermediate methods.

If execution cannot be established statically,
mark that step as uncertain.

The result might look like:

1. POST /orders
   routes/api.php

2. OrderController::store()
   app/Http/Controllers/OrderController.php

3. StoreOrderRequest
   app/Http/Requests/StoreOrderRequest.php

4. OrderService::create()
   app/Services/OrderService.php

5. Product::lockForUpdate()
   app/Models/Product.php

6. Order::create()
   app/Models/Order.php

7. OrderCreated event
   app/Events/OrderCreated.php

8. SendOrderConfirmation job
   app/Jobs/SendOrderConfirmation.php

Now you have a workflow worth investigating.


Step 4: Don't Trust the Controller

Controllers are often the first place developers look.

That's understandable.

For example:

public function store(StoreOrderRequest $request)
{
    $order = $this->orderService->create(
        $request->validated()
    );

    return response()->json($order);
}

It looks clean.

But the interesting behavior may be somewhere else.

The service might contain:

public function create(array $data): Order
{
    return DB::transaction(function () use ($data) {
        $order = Order::create($data);

        $this->inventory->reserve($order);

        OrderCreated::dispatch($order);

        return $order;
    });
}

Then the event might trigger:

public function handle(OrderCreated $event): void
{
    SendOrderConfirmation::dispatch($event->order);
}

And the job might call:

$this->mailer->sendOrderConfirmation($this->order);

The controller was only the front door.

The actual behavior was distributed across the application.

This is why AI should trace execution rather than merely summarize individual files.


Step 5: Investigate Eloquent Models Carefully

Laravel's Eloquent models can contain far more business behavior than their small size suggests.

Consider:

class Order extends Model
{
    protected $fillable = [
        'customer_id',
        'status',
        'total',
    ];

    public function customer()
    {
        return $this->belongsTo(Customer::class);
    }

    public function items()
    {
        return $this->hasMany(OrderItem::class);
    }
}

At first glance, it looks like a simple database model.

But there may also be:

  • accessors

  • mutators

  • casts

  • scopes

  • relationships

  • model events

  • observers

  • global scopes

  • attribute hooks

  • computed properties

  • authorization assumptions

  • database constraints

Ask AI:

Analyze the Order model.

Identify:

- relationships
- casts
- scopes
- accessors
- mutators
- events
- observers
- global scopes
- traits
- custom methods
- database assumptions

For each item, identify where it is used elsewhere.

Do not suggest changes.

Then investigate each relationship.

For example:

public function customer()
{
    return $this->belongsTo(Customer::class);
}

The important question isn't only:

What does this relationship do?

Ask:

Where is this relationship relied upon?

Search for:

$order->customer

and:

$order->customer()

and:

with('customer')

and:

load('customer')

This helps reveal how the model participates in the rest of the application.


Step 6: Find Business Rules Hidden Inside PHP

Legacy applications frequently contain business rules disguised as ordinary conditions.

Consider:

if ($customer->country === 'IN' && $order->total > 5000) {
    $shippingFee = 0;
}

A developer might call this a pricing rule.

But you don't yet know why 5000 exists.

Maybe:

  • it is a promotional threshold

  • it is a contractual agreement

  • it applies only to specific customers

  • it was introduced temporarily

  • it is obsolete

  • another service has a different threshold

AI can help identify suspicious rules.

Ask:

Inspect this class for business rules.

Identify:

- magic numbers
- magic strings
- conditional business logic
- status transitions
- date-based rules
- customer-specific exceptions
- country-specific behavior
- pricing rules
- permission assumptions

For every rule explain:

1. What the code does
2. Where the rule comes from, if discoverable
3. Where else the same rule appears
4. What remains uncertain

This is where AI becomes particularly useful.

It can compare similar logic across dozens of files much faster than a human can.


Step 7: Search for Duplicated Business Logic

Duplication isn't always a problem.

But duplicated business rules deserve attention.

Imagine you find:

// OrderService.php

if ($order->total >= 1000) {
    $discount = 10;
}

Then:

// InvoiceService.php

if ($invoice->subtotal >= 1000) {
    $discount = 10;
}

And:

// CheckoutController.php

if ($cart->total >= 1000) {
    $discount = 10;
}

This may be three implementations of the same rule.

Or it may be three subtly different rules.

Don't immediately merge them.

Ask AI:

Find potentially duplicated discount rules
throughout the application.

Group similar implementations.

For each group show:

- file
- class
- method
- condition
- calculation
- differences

Do not assume similar code represents
the same business rule.

The final sentence prevents an important AI failure mode.

Similarity does not prove equivalence.


Step 8: Investigate Events and Listeners

Laravel applications can hide significant behavior behind events.

Consider:

OrderCreated::dispatch($order);

That one line may trigger several listeners.

You should therefore ask:

For the OrderCreated event, find:

- where it is dispatched
- all listeners
- queued listeners
- subscribers
- notifications
- jobs dispatched by listeners
- database changes
- external API calls

Create a complete event chain.

You might discover:

OrderCreated
   ├── UpdateCustomerStatistics
   ├── SendOrderConfirmation
   ├── NotifyWarehouse
   └── CreateInvoice
          ↓
       InvoiceCreated
          ↓
       SendInvoiceEmail

Now imagine removing OrderCreated::dispatch() during a refactor because it "looked unnecessary."

You have just removed four business behaviors.

This is one reason legacy code needs investigation before cleanup.


Step 9: Trace Queued Jobs

Jobs introduce another layer of complexity.

Consider:

SendOrderConfirmation::dispatch($order);

You need to know:

  • Is the job queued?

  • Which queue?

  • How many retries?

  • Is there backoff?

  • What happens after failure?

  • Is the job idempotent?

  • Can it run twice?

  • Does it call an external API?

  • Does it update the database?

Ask AI:

Analyze SendOrderConfirmation.

Determine:

- how it is dispatched
- queue configuration
- retry behavior
- timeout
- backoff
- failure handling
- external services
- database writes
- notifications
- possible duplicate execution

Identify every file supporting these conclusions.

A job that looks harmless:

public function handle(): void
{
    $this->mailer->send($this->order);
}

could ultimately trigger an external HTTP request, update delivery status, and create an audit record.

Never evaluate a job only by its handle() method.


Step 10: Investigate External API Calls

External integrations are particularly important during refactoring.

Search for:

Http::get(...)
Http::post(...)
Http::withToken(...)
Http::retry(...)

Laravel provides an HTTP client for communicating with external services, so these calls can often be found systematically across the project.

Ask AI:

Find every external HTTP integration.

For each integration identify:

- URL
- service class
- authentication mechanism
- HTTP methods
- timeout
- retry behavior
- request payload
- response handling
- exception handling
- database effects
- jobs involved
- tests or fakes

Group integrations by external service.

You might discover:

Payment Gateway
    app/Services/PaymentGateway.php

Shipping Provider
    app/Services/ShippingService.php

CRM
    app/Integrations/CrmClient.php

This starts turning an unknown application into a system you can reason about.


Step 11: Use Tests as Behavioral Documentation

One of the most valuable sources of legacy-system knowledge is often the test suite.

Even when there is little documentation, tests can reveal what developers considered important enough to protect.

For example:

public function test_large_orders_require_manual_review(): void
{
    $order = Order::factory()->create([
        'total' => 15000,
    ]);

    $this->assertTrue(
        $order->requiresManualReview()
    );
}

This tells you something about the business.

Search for tests related to the feature you are investigating.

Ask AI:

Find tests related to Order approval.

Summarize the behavior each test protects.

Group tests into:

- validation rules
- business rules
- authorization
- state transitions
- integrations
- failure scenarios
- edge cases

Identify important behavior that has no obvious test.

This gives you two valuable outputs:

  1. What the application is expected to do.

  2. What the application does not appear to protect with tests.

Both matter.


Step 12: Ask AI to Separate Facts from Assumptions

This is perhaps the most important technique in the entire workflow.

AI is extremely good at producing plausible explanations.

That's also its weakness.

Suppose it says:

OrderObserver updates inventory when an order is created.

That sounds convincing.

But where did it get the information?

You want the AI to distinguish between:

CONFIRMED

and:

INFERRED

and:

UNKNOWN

Use a prompt such as:

For every conclusion in your analysis, classify it as:

CONFIRMED
- directly supported by code

INFERRED
- strongly suggested but not proven

UNKNOWN
- insufficient evidence

Never present an inference as a confirmed fact.

Include file paths for confirmed conclusions.

This small instruction dramatically improves the usefulness of AI-assisted codebase analysis.


Step 13: Build a Dependency Map

Once you understand one or two workflows, ask AI to build a lightweight dependency map.

For example:

OrderController
    |
    +-- StoreOrderRequest
    |
    +-- OrderService
            |
            +-- PricingService
            |
            +-- InventoryService
            |
            +-- OrderRepository
            |
            +-- OrderCreated
                    |
                    +-- SendOrderConfirmation
                    |
                    +-- NotifyWarehouse
                    |
                    +-- CreateInvoice

Don't try to create a perfect architectural diagram.

The goal is to identify high-impact nodes.

For example:

OrderService
├── 14 callers
├── 6 dependencies
├── 3 events
└── 2 external integrations

That class deserves more caution than:

CurrencyFormatter
└── 1 caller

You can ask AI:

Identify classes that are high-impact dependencies.

Rank them using:

- number of callers
- number of dependencies
- database writes
- external integrations
- events
- queued jobs
- authentication implications
- business-critical workflows

Explain why each class is risky to change.

This helps you prioritize investigation.


Step 14: Investigate Authentication and Authorization

A legacy application may have authorization rules in several places.

You could find:

$this->authorize('update', $order);

in one controller.

But elsewhere:

if ($user->is_admin) {
    // ...
}

And somewhere else:

if ($order->user_id !== auth()->id()) {
    abort(403);
}

These may represent overlapping authorization strategies.

Ask AI:

Analyze authorization for the Order feature.

Find:

- middleware
- gates
- policies
- controller authorization
- manual permission checks
- role checks
- direct auth()->user() conditions

Group them by operation:

view
create
update
delete
approve
cancel
refund

Identify conflicting or duplicated authorization rules.

This is especially important before moving logic between controllers, services, policies, or jobs.


Step 15: Investigate Database Behavior

The database is part of the application's behavior.

Don't limit your investigation to migrations.

Look for:

DB::transaction(...)
DB::statement(...)
Model::query()
->update(...)
->delete(...)
->increment(...)

and direct SQL.

Also inspect:

  • foreign keys

  • unique indexes

  • nullable columns

  • database defaults

  • triggers

  • soft deletes

  • casts

  • enum-like values

  • migration history

Ask AI:

Analyze the database behavior of the Order workflow.

Identify:

- tables touched
- inserts
- updates
- deletes
- transactions
- locking
- foreign keys
- unique constraints
- soft deletes
- database defaults

Show the order in which database changes occur.

A refactoring that looks safe at the PHP level can still change transaction boundaries or database behavior.


Step 16: Look for Hidden Side Effects

A method named:

cancel()

might do more than cancel an order.

For example:

public function cancel(Order $order): void
{
    $order->update([
        'status' => 'cancelled',
    ]);

    $this->inventory->release($order);

    $this->payment->refund($order);

    event(new OrderCancelled($order));
}

So when you see:

$order->cancel();

you should ask:

What else happens?

A useful AI prompt is:

For the Order::cancel operation, identify every
observable side effect.

Follow:

- model methods
- observers
- events
- listeners
- jobs
- notifications
- database writes
- external API calls

Create a side-effect tree.

Do not stop at the first method.

This is particularly useful before replacing a method with direct model updates.


Step 17: Create an "Unknowns" List

Most developers create documentation containing what they know.

When dealing with legacy software, also document what you don't know.

For example:

Known
-----

Order cancellation updates status.
Inventory is released.
OrderCancelled is dispatched.


Unknown
-------

- Whether refunds are always required.
- Whether cancelled orders can be restored.
- Whether external shipping systems receive cancellation.
- Why cancellation is disabled for certain countries.
- Whether duplicate cancellation is safe.

This list becomes extremely valuable.

Instead of pretending the system is understood, you have a controlled list of questions.

Ask AI:

Based on everything investigated so far,
create an "Unknowns and Questions" document.

Include anything that cannot be proven from
the codebase.

Prioritize questions by risk:

HIGH
MEDIUM
LOW

Now your investigation has direction.


Step 18: Use AI to Create a Refactoring Boundary

Only after the system is reasonably understood should you ask:

What should we change?

Suppose you discover that this class:

OrderService.php

contains:

  • order creation

  • pricing

  • inventory reservation

  • payment

  • notifications

  • auditing

You could ask AI:

Based on the investigation, propose a refactoring plan.

Constraints:

- preserve existing behavior
- no database changes initially
- no public API changes
- no change to queue behavior
- no change to authorization behavior

For each proposed change explain:

1. Current behavior
2. Problem
3. Proposed boundary
4. Files affected
5. Risks
6. Tests required
7. Rollback strategy

Do not generate implementation code yet.

Notice what changed.

You are no longer asking:

"Can you make this code cleaner?"

You are asking:

"Given what we know about this system, where can we safely establish a boundary?"

That's a much better engineering question.


A Practical Investigation Prompt You Can Reuse

Here is a reusable prompt for a Laravel project.

You are helping me understand an existing Laravel 13
application before I make changes.

Your role is codebase investigator, not refactoring agent.

IMPORTANT RULES

1. Do not modify files.
2. Do not generate implementation code unless I explicitly ask.
3. Do not assume directory names represent architecture.
4. Do not invent business rules.
5. Every confirmed conclusion must reference file paths.
6. Clearly distinguish:
   - CONFIRMED
   - INFERRED
   - UNKNOWN
7. Follow execution across:
   - controllers
   - services
   - repositories
   - models
   - events
   - listeners
   - jobs
   - commands
   - notifications
   - policies
   - external integrations
8. Pay particular attention to database writes and side effects.

FIRST TASK

Create a map of the application containing:

- HTTP entry points
- API routes
- console commands
- scheduled tasks
- queued jobs
- events
- listeners
- models
- services
- repositories
- external integrations
- authentication
- authorization
- tests
- database structure

For every item provide:

File
Class
Method
Purpose
Dependencies
Confidence

Do not propose improvements yet.

After the map is complete, wait for my next instruction.

This prompt establishes a useful boundary between investigation and implementation.


A Better Workflow for Real Projects

When I inherit a Laravel application, I would use the following sequence.

Phase 1 — Orientation

Understand:

Repository
    ↓
Laravel structure
    ↓
Routes
    ↓
Commands
    ↓
Jobs
    ↓
Events
    ↓
Integrations

Don't refactor anything.


Phase 2 — Choose One Business Capability

Pick something meaningful:

Create Order
Process Payment
Generate Invoice
Register Customer
Ship Package
Approve Refund

Then trace it end-to-end.


Phase 3 — Identify State Changes

Document:

What enters the system?
        ↓
What changes?
        ↓
Who changes it?
        ↓
What triggers next?
        ↓
What leaves the system?

This is where most hidden behavior appears.


Phase 4 — Find Business Rules

Look for:

conditions
thresholds
status transitions
permissions
exceptions
country rules
date rules
pricing rules

Then determine whether each rule is:

confirmed
inferred
unknown

Phase 5 — Find Side Effects

Trace:

database
email
notifications
events
jobs
external APIs
files
logs
audit records

A method is rarely as isolated as it looks.


Phase 6 — Inspect Tests

Determine:

What behavior is protected?
What behavior is undocumented?
What behavior has no test?

Phase 7 — Build the Dependency Map

Identify:

high-impact classes
high-risk workflows
external dependencies
shared business rules

Phase 8 — Define the Refactoring Boundary

Only now decide:

What should change?
What should remain untouched?
What can be tested?
What can be migrated incrementally?

What I Would NOT Ask AI to Do First

There are several prompts I would avoid at the beginning of a legacy project.

"Refactor this entire application."

Too broad.

"Convert this to clean architecture."

You don't yet know whether the current behavior can safely be moved.

"Apply SOLID principles everywhere."

Architecture is not a formatting exercise.

"Rewrite all controllers."

Controllers may contain business behavior you haven't discovered yet.

"Replace all repositories with Eloquent."

Repositories might be unnecessary — or they might hide important abstraction boundaries.

"Remove all duplicate code."

Some duplication may be intentional.

"Make this code production-ready."

Production-ready according to what requirements?

"Optimize everything."

You need evidence that something actually needs optimization.


AI Is Most Useful When It Helps You Ask Better Questions

The biggest advantage of AI in legacy development isn't that it can write PHP faster.

You already have tools for writing PHP.

The bigger advantage is that AI can help you navigate a large amount of unfamiliar code.

Instead of spending an hour manually searching:

Where is this event dispatched?

Who calls this service?

Why is this column updated?

Where else is this status used?

Which jobs depend on this model?

you can ask AI to perform the first pass.

But the AI should produce evidence, not just explanations.

That's the difference between:

I think this service handles payment.

and:

CONFIRMED

PaymentService::charge()
is called from:

- OrderService::complete()
- RetryPayment.php

Evidence:
app/Services/PaymentService.php
app/Services/OrderService.php
app/Jobs/RetryPayment.php

The second result is something you can verify.


A Simple Rule for AI-Assisted Legacy Development

I use a simple mental model:

          UNDERSTAND
               ↓
            VERIFY
               ↓
            DOCUMENT
               ↓
             TEST
               ↓
            CHANGE
               ↓
            VERIFY

Not:

       AI
        ↓
    "Refactor"
        ↓
     Deploy

AI makes the second workflow dangerously easy.

The first workflow is slower initially, but much safer.


Turning Codebase Investigation Into Documentation

There is another useful side effect of this approach.

Your investigation can become documentation.

For example, create:

docs/
├── architecture.md
├── order-workflow.md
├── payment-flow.md
├── authorization.md
├── external-integrations.md
├── database-behavior.md
├── background-jobs.md
└── unknowns.md

You don't need to write these documents manually from scratch.

Use AI to generate the first version from your verified findings.

Then review them yourself.

For example:

Create docs/order-workflow.md.

Use only confirmed findings from our investigation.

Document:

- entry points
- validation
- service calls
- database changes
- transactions
- events
- jobs
- external integrations
- failure scenarios

Do not add assumptions.

Mark unresolved behavior under
"Open Questions".

Now the next developer doesn't have to rediscover the same system.


The Real Goal Is Not Understanding Every File

A common mistake is thinking:

"I need to understand the whole application before I can change anything."

Usually, you don't.

For a large Laravel application, complete understanding may be unrealistic.

Instead, aim for sufficient understanding of the change surface.

If you're modifying payment processing, you need a strong understanding of:

PaymentController
PaymentService
Payment model
OrderService
payment jobs
payment events
payment provider
related database tables
tests

You don't necessarily need to understand:

PDF reporting
admin dashboard widgets
customer newsletter
image processing

unless they interact with the payment workflow.

This makes the investigation manageable.


Final Checklist Before Changing a Legacy Laravel Feature

Before making a significant change, I would want to answer these questions:

Entry points

  • How does this feature start?

  • Are there multiple entry points?

Business logic

  • Where are the actual business rules?

  • Are any rules duplicated?

Database

  • Which tables are modified?

  • Are transactions involved?

  • Are there hidden model events?

Events

  • Which events are dispatched?

  • Who listens to them?

Jobs

  • Which jobs are triggered?

  • Are they retryable?

  • Can they execute more than once?

Integrations

  • Which external services are called?

  • What happens if they fail?

Authorization

  • Who is allowed to perform the operation?

  • Where is that authorization enforced?

Tests

  • Which existing tests protect this behavior?

  • Which important behavior isn't tested?

Unknowns

  • What don't we know yet?

  • Which unknowns could affect the proposed change?

If you cannot answer several of these questions, you may not be ready to refactor.


Conclusion

Legacy Laravel applications are rarely difficult because the PHP syntax is complicated.

They are difficult because the application's behavior is distributed.

A controller may call a service.

The service may update a model.

The model may trigger an event.

The event may dispatch a job.

The job may call an external API.

The API response may update another table.

And somewhere in the middle, there may be a business rule that nobody remembers writing.

AI is particularly good at helping you follow these connections.

But the safest use of AI is not:

"Rewrite this code."

It is:

"Help me discover what this code actually does."

Start with evidence.

Trace real workflows.

Follow side effects.

Search for duplicated rules.

Inspect tests.

Separate facts from assumptions.

Document the unknowns.

Then — and only then — decide what should change.

The goal isn't to make AI responsible for understanding your legacy application.

The goal is to make you faster at understanding it.

And once you understand the system, AI becomes much more useful as an implementation partner.

That's when prompts such as:

Refactor this service.

start becoming safe.

Because now you know what must not break.

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment

RDA
Rashid Anwar
Independent Developer

I’m Rashid, an independent developer focused on practical software solutions for businesses.

HAVE A PROJECT IN MIND?

Let's talk about the problem you're trying to solve.

Share what you need. We can discuss the requirements and a practical next step.

Start a Conversation