ddtcorex

13 Aug 2026 · 5 min read

Debugging & QA with Govard

Stop fighting Xdebug and scattered test tools — Govard gives you one toggle, one log stream, and one test runner.

Govard is a Go-native local development orchestrator for PHP and web projects. It already handles spinning environments up and discovering frameworks; this post focuses on the part of the loop that usually eats the most time — actually debugging the thing and proving it works. The goal is simple: one place to turn on Xdebug, one stream to read your logs, and one command to run the whole test pyramid. Every command below is a real Govard subcommand; see the CLI command reference for the full surface.

Xdebug without the yak-shave

Setting up Xdebug by hand is the classic time sink: you edit php.ini, match the listen port, repair path mappings so breakpoints actually bind, and then discover the debugger never connects because the IDE key is wrong. Govard folds all of that into the php-debug service. You do not edit ini files by hand, and you do not guess the listen port.

Start the environment with the debug service enabled:

# bring the stack up with the php-debug service attached
govard env up --with php-debug

Govard writes the Xdebug listen port, the container-to-host path mappings, and the IDE key into the generated container config, so your editor picks them up without manual setup. For VS Code, scaffold the matching launch.json directly from Govard:

# generate a VS Code debug configuration globally
govard vscode setup --global

PhpStorm reads the same generated settings — point an existing Xdebug run configuration at the listen port Govard exposes and the breakpoints bind immediately. No per-project XML to maintain.

The remaining piece is deciding when a request should trigger the debugger. Govard uses the standard browser trigger cookie, so you can turn debugging on for a single request instead of every request:

# trigger Xdebug from a one-off request
curl -b "XDEBUG_SESSION=govard" https://local.project.test/cart

# or set the same cookie in your browser via a bookmarklet / extension

That is the whole flow: govard env up --with php-debug, let your editor discover the generated config, and drop the XDEBUG_SESSION cookie on the requests you want to step through. Toggle the service off when you are done and the stack behaves like a normal environment again.

Tip: keep the cookie out of automated test traffic. Trigger Xdebug only on the requests you are actively stepping through, so background calls do not stall on a debugger that is not listening.

Log access & inspection

Once the environment is running, logs are the first thing you reach for — and the last thing you want to hunt across multiple container terminals. Govard aggregates environment output behind a single command:

# stream every service's logs, following new lines
govard env logs -f

# list running services and their container names
govard env ps

When you need to go deeper than the aggregated stream, open a shell in the relevant container. The container names come straight from govard env ps:

# open a shell in the php container
docker exec -it govard-php-1 bash

# PHP and Xdebug error logs are also mirrored to the host for quick grepping
ls /tmp/govard/logs

The /tmp/govard/... host mirror is the practical payoff: you can tail a PHP error log from your own terminal without attaching to the container, and the files survive a govard env restart because they live on the host, not inside the throwaway container filesystem. docker exec stays available for the cases where you need the in-container toolchain — composer, bin/magento, or a quick php -m to confirm an extension actually loaded.

Unified test runner

Most PHP projects end up with three or four ways to run checks: PHPUnit for units, PHPStan for static analysis, and MFTF for functional browser tests — each with its own invocation, working directory, and report format. Govard collapses them into one entry point:

# run the unified suite — PHPUnit, PHPStan, and MFTF in one pass
govard test

govard test orchestrates all three behind a single command and writes JUnit reports that your CI can consume as build artifacts. That matters for two reasons. First, a developer runs the exact same command locally and in the pipeline, so "works on my machine" stops being a different process than "works in CI". Second, the JUnit output means a failed run is drillable in whatever CI surface you already use, instead of buried in a wall of console text.

For day-to-day work the loop is short: make the change, run govard test, read the JUnit report, fix the failure, repeat. Because Govard owns the environment, the test runner already knows where the source is, which database fixture to use, and how to reach the browser driver for MFTF — you are not re-supplying that wiring on every invocation. If a single layer is flaky, run the full command and let the report tell you which suite regressed rather than triangulating by hand.

Drift detection & audit

The quieter failure mode is drift: the running environment slowly diverges from the definition it was built from — a config value edited in place, an extension added ad hoc, a mounted path that no longer matches the lock file. Govard can check the running state against its recorded definition before you waste an hour debugging a difference that only exists on your machine:

# confirm the running environment matches its lock/definition
govard verify

# inspect the effective configuration and surface detected drift
govard inspect

govard verify is the gate you run before trusting a local result; govard inspect is the drill-down that shows you what actually differs. Together they turn "why is this behaving differently for me?" into a concrete diff instead of a guess. For the deeper, workflow-level audit — performance, configuration hygiene, and environment health across a project — the Govard audit workflow builds on the same inspection primitives and is worth reading once you are past the basics.

Drift detection also pairs naturally with snapshots: capture a known-good state, make your change, and verify against it. That keeps local debugging honest — you prove the environment itself did not shift under you while you were changing code.

Next in this series: Sync & deploy safely with Govard remotes — /blog/govard-remote-sync

govardxdebugtesting