skip to content

In Laravel, what is the difference between the dump() and dd() helpers, and when is Log::debug() the better choice?

level: juniorimportance: must knowfreq 66%

answer

  1. one keeps running, one stops
  2. Symfony VarDumper underneath
  3. file and line of the call shown
  4. output you cannot see: jobs, JSON, cron
  5. LOG_LEVEL filters debug entries

basics

~20 s

dump() prints its arguments and lets the request continue; dd() prints them and stops execution. Log::debug() writes to the log instead of the output, so it suits queued jobs, JSON endpoints and anything whose output you cannot see.

solid answer

~40 s

`dump($a, $b)` and `dd($a, $b)` both pretty-print values through Symfony's VarDumper, which Laravel registers with its own HTML and CLI dumpers that also show the file and line of the call (mapping compiled Blade views back to the template). `dump()` returns and the request carries on, so you can dump several points in one run; `dd()` dumps and ends the script, which is handy for inspecting one value before anything else happens. Blade has `@dump(...)` and `@dd(...)`. Both write into the response or the terminal, so they are useless where you cannot see that output: queued jobs, JSON calls from a frontend, scheduled commands. There `Log::debug('Discount computed.', ['total' => $total])` writes to the log without touching the response, and `php artisan pail` shows it live. Debug entries are dropped when `LOG_LEVEL` is above `debug`.

code

php · 17 lines
php
<?php

use Illuminate\Support\Facades\Log;

public function discountFor(Cart $cart): int
{
    dump($cart->items->pluck('sku'));   // keeps running

    $total = $this->rules->apply($cart);

    Log::debug('Discount computed.', [   // visible in pail or laravel.log
        'cart_id' => $cart->id,
        'total' => $total,
    ]);

    dd($total);                          // stops here
}

go deeper

for a junior

Know that dump() continues and dd() stops, that both exist as Blade directives, and that Log::debug() writes to the log instead of the page.

for a middle

Explain where each helper's output goes in web requests, Artisan and workers, and why dumps fail for JSON endpoints and jobs.

for a senior

Choose the tool by context: logs for jobs and APIs, dumps only locally, and guard against stray dumps reaching shared environments.

for a principal

Set team conventions for debugging output, balancing quick local inspection against the risk of dumps or noisy debug logs reaching production.

## Two helpers, one difference Laravel's global `dump()` and `dd()` helpers come from **Symfony VarDumper**. Both accept any number of values and print a readable, collapsible representation of each, including nested arrays, objects and Eloquent models. | Helper | Prints | Then | |---|---|---| | `dump(...$values)` | every argument | returns; execution continues | | `dd(...$values)` | every argument | ends the script ("dump and die") | Laravel adds two things on top of plain VarDumper. `FoundationServiceProvider::registerDumper()` installs Laravel's own `HtmlDumper` (for web requests) or `CliDumper` (for Artisan, tests and workers), and those dumpers print the **source file and line** of the call, so a forgotten dump is easy to find. When the call sits in a Blade view they point at the original template rather than the compiled file in `storage/framework/views`. The dumpers also shorten internals of noisy services such as the container, the event dispatcher and database connections. In Blade templates the same helpers are available as directives: `@dump($cart)` and `@dd($cart)`. ## Where the output goes | Context | Dumper Laravel registers | Where you see it | |---|---|---| | Web request | `HtmlDumper` | at the top of the response body in the browser | | Artisan command, test run | `CliDumper` | the terminal | | Queue worker | `CliDumper` | the worker's standard output | The `VAR_DUMPER_FORMAT` server variable overrides the choice (`html`, `cli`, or a `server`/`tcp` target that sends dumps to a separate dump server instead of the output). ## Using them on the grocery discount bug Suppose a local copy of a grocery app shows a wrong discount on the cart page. A quick investigation might go: 1. `dump($cart->items)` at the top of the discount service to see what arrived. 2. `dump($rule)` inside the loop to see which promotion matched each item. 3. `dd($total)` right before the return, to stop and inspect the final number. `dump()` suits steps 1 and 2 because the page keeps rendering and all dumps appear in order at the top of the output. `dd()` suits step 3 because nothing after it matters. ## When Log::debug() is the better tool Dumps are written **into the output**: the HTML page, the terminal or the HTTP response body. That fails whenever the output is not where you are looking: - **queued jobs and scheduled commands** run in a worker or a cron shell, so their output goes to a process log, if anywhere; - **JSON endpoints** called by a frontend: the dump is prepended to the response and breaks the client's JSON parsing; - **redirects**: after a form post that redirects, a `dump()` is on the page nobody sees; - **anything shared**, such as a staging server, where a dump would show data to other users. `Log::debug()` writes to the configured log channel instead: ```php Log::debug('Discount computed.', ['cart_id' => $cart->id, 'total' => $total]); ``` - It does not change the response, so it works in every context above. - It is filtered by the channel's `level`, which reads `LOG_LEVEL` (the skeleton uses `debug`); raising the level in production silences these lines without a code change. - `php artisan pail` tails the log in the terminal, or you read `storage/logs/laravel.log`. - If Debugbar is installed, its messages tab also shows log entries for the current request. ## Rules of thumb - Use **`dd()`** for a single, immediate look at one value while developing locally. - Use **`dump()`** to follow several values through one request. - Use **`Log::debug()`** for jobs, APIs, commands and anything you might want to read later. - Delete dumps before committing; a debug-level log line can stay if it is useful and contains no secrets. ## Common misunderstandings - `dd()` does not throw an exception that the handler can catch; it ends the script outright. - `dump()` is not silenced in production by `APP_DEBUG=false`; a stray call still prints. - `Log::debug()` is not free: context arrays are serialised and written, so keep them small.

  • Your dump() output does not appear when a Vue page calls a Laravel JSON endpoint. Where did it go?
    The dump was written into the HTTP response body ahead of the JSON, so the browser's network tab shows it, but the frontend's JSON parser fails on it and the page shows nothing useful. Inspect the raw response in dev tools, or switch to `Log::debug()` and watch it with `php artisan pail`.
  • How do you debug a value in a Blade template without touching the controller?
    Use the `@dump($variable)` directive, which compiles to a `dump()` call inside the view, or `@dd($variable)` to stop rendering there. Laravel's dumper reports the original Blade file and line, not the compiled view, so the output points back to the template.

dump() is a sticky note left on each station of a production line while the line keeps moving; dd() is pulling the emergency stop to look at one part. Log::debug() is the line's logbook: it records what happened even at stations you were never standing next to.

saying these in an interview costs you the question

  • dd() throws an exception that the handler reports.
  • Setting APP_DEBUG=false disables dump() output.
  • dump() output from a queued job appears in the browser.
  • Log::debug() entries are always written regardless of LOG_LEVEL.
  • dd() only works inside controllers.