Skip to content

ADR-0002: Modernize YourBOX With A Symfony 8 Strangler Pattern

Accepted

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.

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.

  • 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_once globals) 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_once outside the legacy bundle path.