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