ADR-0002: Modernize YourBOX With A Symfony 8 Strangler Pattern
Status
Section titled “Status”Accepted
Context
Section titled “Context”YourBOX is a streaming application I started in 2020. The current codebase is plain PHP with
jQuery and hand-written CSS. Every entry point begins with several require_once lines that
pull a chain of helpers, the database is reached through a global $pdo, business logic
sits in the same files as HTML output, and there is no autoloader, dependency injection or
routing layer. The application also exposes SQL injection surfaces, broken remember-me logic
and absolute filesystem paths stored in the database.
A full greenfield rewrite would discard the feature accretion from 2020 onwards and stall the modernization for months. Leaving the codebase as-is blocks every other modernization goal because the lack of dependency injection and the global state make Authentik OIDC, hexagonal domain layers, Tailwind templating and observability all impractical.
A progressive migration is the explicit constraint: keep YourBOX deployable at every step, do not rewrite everything at once.
Decision
Section titled “Decision”Adopt Symfony 8 with the strangler fig pattern. The repository gains a new app/ directory
that hosts the Symfony skeleton, the front controller, the dependency injection container
and the new routing layer. The existing YourBOX/ tree stays untouched at this step and is
mounted as a fallback bundle inside the Symfony kernel. A low priority LegacyController
serves any URL that no new Symfony route claims, by including the matching legacy PHP file
inside an isolated function scope and streaming the captured output.
The migration order follows the dependency graph captured in the YourBOX service documentation: secret loading first, then authentication, then video upload, then comments and votes, then search, then the admin panel. Each rewrite removes the corresponding legacy files when the new Symfony route covers them, so the strangler bundle shrinks as the modernization advances.
The PHP runtime targets PHP 8.5 to match the existing mise.toml pin and the pipe operator
already used in functions.php. Symfony 8 (released in November 2025) is the first Long
Term Support line that officially supports PHP 8.5, which is why this ADR landed on the 8.x
branch rather than the 7.x branch the original draft cited. The HTTP layer drops Apache
mod_php and runs nginx in front of php-fpm inside the same Docker image.
Consequences
Section titled “Consequences”- The application stays deployable at every step. There is never a window where YourBOX is half-rewritten and broken.
- Each modernization plan ships and reviews independently. The blast radius of a refactor is bounded to the routes the new Symfony controller claims.
- The legacy code keeps its security debt until the rewrite reaches it. The deployment documentation makes this explicit so that Authentik, network policies and WAF rules cover the legacy surface during the transition.
- Operating two patterns side by side (Symfony services and legacy
require_onceglobals) costs cognitive overhead. The strangler bundle is documented and reviewers reject any attempt to extend the legacy tree. - Doctrine ORM, Twig, Security and Mailer bundles become the canonical building blocks for
new code. Reviewers reject any new
require_onceoutside the legacy bundle path.