February 14, 2018 Javier Eguiluz

Reproducible builds are a set of software development practices that create "a verifiable path from human readable source code to the binary code used by computers". In other words, if you don't change the source code, the compilation result should always be exactly the same.

Explained more simply in the case of Symfony: if you build the container and warm up the cache of the same unchanged application multiple times, the result should always be the same.

Why are reproducible builds important? Because software inspections and audits are done on the source code, but applications always run compiled on devices. If the build is reproducible, you can compile the source code that you audited and verify that the compiled result is exactly the same as the one being run on some device. Multiple parties can redo this process independently and ensure they all get exactly the same result, ensuring that the binary code comes from the given source code.

In practice, reproducible builds means that the compilation process must be completely deterministic. The compiled code can't contain date/time values or randomly generated values. That was not the case for Symfony and some of its major third-party dependencies like Monolog and Doctrine.

This required some changes in the way Symfony applications are compiled (we did those changes in the 3.4 branch, so you don't have to install Symfony 4 to use them):

Variable names are no longer random ( uniqid(mt_rand(), true), false) ) in compiled Twig templates (twig#2621)

) in compiled Twig templates (twig#2621) Class names generated for lazy services proxies are no longer random ( hash('sha256', spl_object_hash($definition).$this->salt) ) in Symfony container (symfony#25978)

) in Symfony container (symfony#25978) Monolog bundle no longer generates random IDs for some of its services ( uniqid('monolog.gelf.publisher.', true) ) (monolog-bundle#248)

) (monolog-bundle#248) The Filesystem adapter of the Symfony Cache component now makes non-expirable items independent from time (before they expired in one year) (symfony#26127)

The container compiled by Symfony uses the build datetime to generate the unique hash of the container (it used a simple time() call). Now this time is configurable with the kernel.container_build_time parameter (symfony#26128)

The work to make Symfony builds reproducible is finished on the Symfony side, but you may still face some issues in other dependencies commonly used in Symfony apps. For example: